Captive portals operate through multiple layers of network interception and redirection. When a device connects to such a network, the following sequence occurs:
// Example of DNS interception (Python pseudocode)
def captive_portal_intercept(request):
if not request.client.has_authenticated:
redirect_to_portal(request.ip)
else:
allow_regular_traffic(request)
The system typically involves these technical elements:
- DNS hijacking (resolving all domains to portal IP)
- HTTP/HTTPS interception
- MAC address tracking
- Session cookies or tokens
Here's how modern OSes detect captive portals by attempting to access a known endpoint:
// JavaScript example for portal detection
fetch('http://connectivitycheck.gstatic.com/generate_204')
.then(response => {
if (response.status !== 204) {
// Portal detected
window.location = response.url;
}
});
For developers creating apps that need to handle captive networks:
// Python example using requests with portal handling
import requests
def internet_connectivity_check():
try:
response = requests.get('http://example.com', timeout=5)
if len(response.content) < 1000: # Likely portal page
return False
return True
except:
return False
For automated testing scenarios where you need to authenticate through the portal:
// Automating form submission (Python + Selenium example)
from selenium import webdriver
def portal_authenticate(username, password):
driver = webdriver.Chrome()
driver.get('http://google.com') # Will redirect to portal
driver.find_element_by_name('username').send_keys(username)
driver.find_element_by_name('password').send_keys(password)
driver.find_element_by_tag_name('form').submit()
Important security aspects developers should know:
- HTTPS breaks the traditional captive portal flow (hence HTTP interception)
- Certificate pinning can prevent portal redirection
- Always validate portal pages before submitting credentials
Basic captive portal implementation using Linux and iptables:
# Basic iptables rules for captive portal
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 80 -j DNAT \
--to-destination 192.168.1.1:80
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 443 -j DNAT \
--to-destination 192.168.1.1:443
When you connect to a public WiFi network, the gateway (usually a router or firewall) intercepts all HTTP traffic through these key steps:
- Initial DNS Query: Your device resolves domains through the captive portal's DNS server
- HTTP Redirect: All HTTP requests get 302-redirected to the portal's authentication page
- Traffic Whitelist: Only specific domains (like payment processors) are allowed through
// Example of how a router might implement this in iptables:
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 80 \
-j DNAT --to-destination 10.0.0.1:80
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 443 \
-j DNAT --to-destination 10.0.0.1:443
Modern operating systems actively check for captive portals by attempting to access specific test URLs:
- Apple uses
http://captive.apple.com/hotspot-detect.html
- Microsoft uses
http://www.msftconnecttest.com/connecttest.txt
- Android pings
http://connectivitycheck.gstatic.com/generate_204
# Python example to detect captive portals
import requests
def check_captive_portal():
try:
r = requests.get("http://connectivitycheck.gstatic.com/generate_204", timeout=3)
return r.status_code != 204
except:
return True
Different networks implement captive portals in distinct ways:
Method | Implementation | Bypass Difficulty |
---|---|---|
DNS Redirection | All DNS queries resolve to portal IP | Easy (use DoH/DoT) |
HTTP Transparent Proxy | TCP 80 traffic gets intercepted | Medium (use HTTPS) |
Full SSL Inspection | MitM with custom CA cert | Hard (requires cert trust) |
For automation scenarios, you can script the authentication process:
// Node.js example for hotel WiFi authentication
const puppeteer = require('puppeteer');
async function autoAuth(credentials) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
try {
await page.goto('http://neverssl.com', {waitUntil: 'networkidle0'});
const redirectUrl = page.url();
if(redirectUrl.includes('hotelwifi.com/auth')) {
await page.type('#username', credentials.user);
await page.type('#password', credentials.pass);
await page.click('#accept_terms');
await page.click('#submit_button');
}
} finally {
await browser.close();
}
}
Captive portals create unique security challenges:
- HTTPS interception requires installing custom CA certificates
- MAC address tracking persists even after network disconnection
- Cookie-based sessions can be hijacked if not properly secured
# Bash command to monitor portal interactions
tcpdump -i wlan0 -s 0 -A 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' |
grep -E 'Host:|GET /|POST /|HTTP/'