Bypassing Captive Portals

06th Oct. 2022 - 10m read

The local network at my college requires logging into a captive portal to gain access to the internet. The network provider runs the portal on a local server, just a webpage which greets its new clients with a login form. Once logged in, the new client is added to a pool of existing users subject to a metered network quota.

Unless implemented correctly, captive portals may create security holes that can potentially allow clients to bypass it without too much hassle, and the one at my college is just a hot mess, waiting to be exploited. Several things are wrong with it, but let’s focus on a couple that matter:

  1. Firewall doesn’t block traffic through some outbound ports when logged out.
  2. Uses plain text HTTP password authentication. Super secure.

Hole in the Wall

It all happened on a whim last week. I ran a ping test on my laptop connected to the LAN while I was logged out of the network. So the ping command should have complained about it right? It should have thrown an error? Nope.

$ ping -c10 google.com
PING google.com (142.250.193.142) 56(84) bytes of data.

--- google.com ping statistics ---
10 packets transmitted, 0 received, 100% packet loss, time 9122ms

Instead you get some output but all the ICMP echo packets are lost. What’s interesting here is the first line of stdout, clearly the IP address is being resolved. So DNS queries are going through even when the user is logged out.

If all outbound ports were blocked, this wouldn’t have happened. I ran a couple of dig requests just to confirm this, and sure enough I was able to make DNS queries. This hole by itself is sufficient to setup an IPv4 tunnel through a DNS server using Iodine, which gives you free access to the internet without ever having to login through the portal. Huh.

Testing UDP ports

Another potential way to bypass the captive portal is to set up a Wireguard peer listening on a port that isn’t blocked. Since wireguard uses UDP, I wanted to examine other open UDP ports, you know, for educational purposes.

So I setup a small VPS and configured the firewall to allow inbound UDP traffic through all ports and ran tcpdump on it. Simultaneously I ran an nmap scan targeting the VPS from my machine for UDP ports in the range 1 to 1000.

$ sudo nmap -sU VPS_IP -p 1-1000

After about 5 minutes, the scan resulted in the following output on the VPS

$ sudo tcpdump -i eth0 host PUBLIC_IP and udp
14:38:05.872278 IP PUBLIC_IP.45824 > VPS_IP.domain: 30583+ TXT CHAOS? version.bind.
14:40:10.252252 IP PUBLIC_IP.51285 > VPS_IP.ntp: NTPv4, Client, length 48

As expected the first line in the stdout reports the DNS port 53/udp, we already know tunnelling internet traffic through this port is possible with Iodine.

The second line reports NTP port 123/udp which for used for outbound NTP (network time protocol) traffic. Perhaps timesync is an important operation for the network devices, but it leaves yet another hole in the network for a client to abuse. Bear in mind that we are talking about the first 1000 UDP ports only here.

Dealing With Other Annoyances

You’re a student of the college and you have valid credentials, so why bother with all this? one might ask. Curiousity is an answer good enough, I believe. That aside, there are other reasons such as the portal logging you out pseudo-randomly, at least once a day, which can be mildly annoying.

But that’s an easy fix, since the password auth is happening over http, one could just mimic the browser login using a cURL request. I could come up with a working solution by simply inspecting the form submission request on the browser

curl -d "mode=191" -d "username=USER" -d "password=PASS" 192.168.1.5:8090/login.xml

Setup a cron job that runs every couple of hours and you could rest assured that you won’t be logged out in the middle of your FPS game.

Cheers!