At the beginning of October I wrote a blog article called White Hacking: WeMos and SquirelCrawl!. I used the WeMos (ESP32) and the firmware provided by Hacker Arsenal to do a captive portal. As you known, a captive portal is a web page which is displayed to newly connected users before they are granted broader access to network resources [wiki]. This can be used in combination with evil portals to obtain login credentials. The firmware provided by Hacker Arsenal has multiple limitations, e.g. the web page has to be a single file, without external files, meaning that all images should be integrated in the html file using base64. Moreover, the maximal allowed file size is 150 kb. I wrote a Python script (SquirelCrawl) that copies and compresses an entire web page into a file.
Portal optimization is required! This is only a sample and a very beta code. The portal is far away from stable :S, but it works.
I assume no responsibility for the usage of this code and post. I repeat again, the book "The Hacker Playbook 2: Practical Guide to Penetration Testing - Peter Kim" says
Just remember, ONLY test systems on which you have written permission. Just Google the term “hacker jailed” and you will see plenty of different examples where young teens have been sentenced to years in prison for what they thought was a “fun time.” There are many free platforms where legal hacking is allowed and will help you further educate yourself.
How does a Captive Portal work?
There is more than one way to implement a captive portal. I used the "redirect by DNS". Every time a client requests a website, a DNS query is started. I implemented a DNS server that answers all the DNS lookups returning the IP address of the ESP32 and thus, as a result, the captive portal page.
How to get the "connect to a network" message?
Every time that you connect to a hotspot, you get usually the "connect to a network" message. This is triggered in different ways depending on the OS of your device. I used Wireshark to see the package communication (I connected an external WiFi interface to my computer and I configured the interface as an access point) and I saw the following:
Windows searches for the following web addresses and expects the answers with the described texts.
Address Answer Code Text * www.msftconnecttest.com/ncsi.txt HTTP/1.1 200 OK Microsoft NCSI * www.msftconnecttest.com/connecttest.txt HTTP/1.1 200 OK Microsoft Connect Test * www.msftconnecttest.com/redirect HTTP/1.1 302 Redirect
All other traffic should be redirected to the captive portal IP.
Android searches for the following web addresses, if the answer code is 204 (no content), there is no captive portal between the device and Internet. That means, if you want to have a "connect to a network" message, you can answer these requests with a code 200 (ok). The
gen_204needs a 302 (redirect) to your captive portal web page.
Address Answer Code connectivitycheck.gstatic.com/generate_204 (>6.0) HTTP/1.1 204 No Content clients3.google.com/generate_204 (4.4) HTTP/1.1 204 No Content connectivitycheck.gstatic.com/gen_204 (>6.0) HTTP/1.1 204 No Content
iOS works as Windows, it searches for a web address and expects the answer
Address Answer Code Text captive.apple.com/hotspot-detect.html HTTP/1.1 200 OK Success
Using picoweb, a web micro-framework that works on the ESP32, it is possible to respond to the described requests and simulate that the ESP32 is connected to the Internet and acts as a hotspot. The devices that connect to the ESP32 think that it is a normal Hotspot and the "connect to a network" message appears. The "evil" portal is then opened, when the user clicks on the message.
Hardware & Software Requirements
Installing packages: upip, picoweb & notes-pico
I wrote the captive portal code inspired and using code of the notes-pico package. This package is amazing! You need to test it on the ESP32. If you want to see it live, install MicroPython on the board and then, type the following lines on the PyMark console
import upip upip.install('notes-pico')
upipinstalls also the following dependencies
picoweb utemplate micropython-logging micropython-pkg_resources micropython-btreedb
upiprequires that you board has access to the Internet! That means, you need to connect the board to a WiFi node. That can be done using the following code
import network ssid_ = <#your_ssid#> wp2_pass = <#your_wpa2_pass#> sta_if =  def do_connect(): global sta_if sta_if = network.WLAN(network.STA_IF) if not sta_if.isconnected(): print('connecting to network...') sta_if.active(True) sta_if.connect(ssid_, wp2_pass) while not sta_if.isconnected(): pass print('network config:', sta_if.ifconfig()) # connecting to WiFi do_connect()
After the installation is finished, you can run the
notes-picoapplication using the following lines
import notes_pico.__main__ notes_pico.__main__.main(host=sta_if.ifconfig(), port=80)
Then, you can access the note application using your browser and the IP and port that are reported on the console, e.g.
Running on http://192.168.178.79:80/
If you want to read more about this application, click here.
DIY: Captive Portal on ESP32
Get the required hardware.
Install MicroPython on the ESP32. You need a nightly version greater than esp32-20171031 (31 Oct. 2017)! Versions older than this one have a problem with the DNS address of the access point (see here). A tutorial to install MicroPython on the ESP32 can be found here.
Using the PyMark console, connect the WeMos to the Internet (code above or
setup_sta_upip.py), and install the captive-portal dependencies (picoweb, micropython-logging, utemplate, micropython-pkg_resources, micropython-btreedb). I included the file
setup_sta_upip.py. You need to change the
<your_ssid>, and the
<wpa2_password>to the corresponding of your network and execute the file on the PyMark console (click on the run button).
Check the configuration variables in
config.pyand modified them if you want to.
The configuration variables are located in this file. The logged data will be save on a file on the WeMos. To do that, you can choose between 3 possibilities (btree, filedb or sqlite). I tested only btree ;). This can be selected using the variable
DB_BACKEND. The variable
DB_PASSWORDis the admin password. I included an admin section to read the database. The user is admin, the password is defined using
DB_PASSWORD. The DNS service only responds to the requests that contain at least of the words from the array
DNS_ANSWERS. This reduces the CPU workload. The words listed per default are the required in order to get the "connect to a network" message (you should not remove any of them). You can add others to this array. The problems with sockets that I described in the Wemos-Alexa blog article has also consequences here. The number of opened sockets is limited. You can expect some fatal error from the picoweb service.
Modify the captive portal webpage included in the
login.htmlpages. These two files are only for you to see the logged data (administrator section).
Load the captive_portal folder using the FTP server included (
ftp.py), or using the
Configure the name of the access point in the file
boot.pyand load this file to the root folder.
Reset the Wemos.
Connect to the access point network and wait for the "connect to a network" message. Click on it and wait for the portal.
http://<ip wemos>/admin.htmlenter the
adminas user and the password from
config.pyand you will see the logged data.