Goals
The objective of this Wifi captive portal is to mimic the behaviour of a legitimate access point protected by a portal login page for demonstrational purposes. That includes the following:
- Broadcast a rogue access point
- Mimic captive portal behaviour:
- User gets to see a login page when trying to connect;
- After logging in, the user can continue to access the network and surf freely.
Tools
The following tools and hardware were used to set up this proof of concept.
- airmon-ng
- airbase-ng
- dnsmasq
- iptables
- apache2 web server
- USB-connected Wifi antenna
How it’s done
The portal backend
First, we set up the web server to be able to serve a login page when a user is trying to access any non-existing page.
- Configure the
.htaccess
file in the root of the server to contain the followingRewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f #INSERT# RewriteRule .? http://myportal/login.php [L,QSA]
The second line tells the server to only redirect when the requested file does not exist. The third line will be dynamically replaced to whitelist IP addresses to make sure no redirect will be triggered after entering credentials a first time. Make the .htaccess file writeable:
chmod 755 /var/www/.htaccess
. - We need a file called
login.php
that can be reached onhttp://myportal/login.php
. Note thatmyportal
will resolve to localhost (see below). It should contain something like the following code:<?php if(!empty($_POST)) { $htaccess = file_get_contents("file:///var/www/.htaccess"); $escIP = str_replace(".","\.",$_SERVER['REMOTE_ADDR']); $htnew = str_replace("#INSERT#", "#INSERT#\nRewriteCond %{REMOTE_ADDR} !^$escIP$", $htaccess); file_put_contents("file:///var/www/.htaccess", $htnew); } <form action="" method="post"> Username: <input type="text" /><br /> <input type="submit" value="Log in!" /> </form>
Pay close attention to what happens with the .htaccess file. The rule
RewriteCond %{REMOTE_ADDR} !^10\.0\.0\.40$
gets added when the user with (local) IP address 10.0.0.40 submits the form on the page. That means the redirect rule will no longer apply to his requests from then on.
The Wifi network
Now we set up the Wifi network and make sure every user connecting for the first time is redirected to the captive portal login page we just set up.
- Connect the Wifi antenna to the system and make sure the card is recognized. The name will likely be
wlan0
orwlan1
. In this guide, we will assume the antenna is known aswlan1
and the system is connected to the internet through theeth0
interface. - Make sure your system allows ipv4 forwarding by uncommenting the line
#net.ipv4.ip_forward
in/etc/sysctl.conf
. - Use the following commands to configure your ip forwarding. This will remove all existing rules and reroute all traffic on the
at0
interface (which will be set up in the next step) to the existing internet connection on interfaceeth0
iptables -F iptables -t nat -F iptables -t nat -A POSTROUTING --out-interface eth0 -j MASQUERADE iptables -A FORWARD --in-interface at0 -j ACCEPT
- Now we can start broadcasting our network:
airmon-ng start wlan0 airbase-ng -e "YOURNETWORKNAME" -c 11 -v mon0
- Assign an IP-address to the new
at0
interface and make sure it’s up and runningifconfig at0 10.0.0.1 netmask 255.255.255.0 up
- Now all that’s left to do, is capture all dns queries and answer them with a crafted IP address where needed. For that purpose, we use the tool dnsmasq with the following configuration (saved in configuration file
/etc/dnsmasq.conf
):no-resolv interface=at0 dhcp-range=10.0.0.3,10.0.0.230,12h # use Google's DNS servers to route dns requests to if we don't handle them server=8.8.8.8 server=8.8.8.4 # allows the use of "myportal" instead of "localhost" or "10.0.0.1". Could also be configured in /etc/hosts address="/myportal/10.0.0.1" # make sure some standard DNS-requests are redirected to our localhost. address="/apple.com/10.0.0.1" address="/appleiphonecell.com/10.0.0.1" address="/itools.info/10.0.0.1" address="/ibook.info/10.0.0.1" address="/airport.us/10.0.0.1" address="/thinkdifferent.us/10.0.0.1" address="/edgekey.net/10.0.0.1" address="/akamaiedge.net/10.0.0.1" address="/akamaitechnologies/10.0.0.1" address="/clients3.google.com/10.0.0.1"
And spin up dnsmasq with the command
dnsmasq -C /etc/dnsmasq.conf
.
The list of addresses are domains that are commonly used by iOS and Android to test if a Wifi network is working as expected. More precisely, the OS will quietly try to contact one of the listed domains with a (seemingly) randomized URL, and wait for a reply. In the case of iOS, if the repy is “success”, the system assumes the Wifi network is open, and it will allow the user to continue. When the user is redirected (HTTP status code 302) instead, the system assumes a captive portal is in place, and will prompt the the user to log in, showing the page the earlier request was redirected to.Because of our configuration in
.htaccess
on the local webserver, that is exactly what a request will do. For example, iOS will try to contact a randomized location on “https://www.ibook.info/random/page”, the DNS query will resolve to “10.0.0.1” thanks to the dnsmasq configuration above, Apache will look up http://10.0.0.1/random/page, notice it doesn’t exist and initiate a redirect (302) to http://myportal/login.php
In action
The image below is an anonymized screenshot of a working WiFi captive portal on iPhone.