Lukas Klein

Python and Django developer. Docker enthusiast. This is my blog.

Mc Donalds WiFi, surrender!

Ever since I’m here in New Zealand I’ve been using the free WiFi at every Mc Donalds. Great! Free WiFi! =)

But wait, what’s the catch? Well, it’s limited to 50MB data per day. Uhm. Sounds like a challenge, doesn’t it? ;)

So, how does this work? They’re probably identifying you by your Mac address. To test this I used all my data allocation, changed my Mac address via:

sudo ifconfig en0 ether <yournewrandomaddress>

and ta-da, another 50MB! But wouldn’t it be great to automate this process? It is! (Note: I’m on a Mac, but for your Linux computer it should work similar, if you proofed it please leave a comment below :))

What the script needs to do is:

  1. Set a new Mac address
  2. Disconnect from the WiFi
  3. Reconnect
  4. Sounds like a challenge? Well, not really.
rootpass = getpass.getpass()
interface = 'en0'
def setMAC(mac):
	os.system("echo '%s' | sudo -S -s ifconfig %s ether %s"%(rootpass, interface, mac))

def disconnect():
	os.system("networksetup -setairportpower %s off"%(interface))

def connect():
	os.system("networksetup -setairportpower %s on"%(interface))

To generate a random Mac address I use the following function:

def randomMAC():
	mac = [ 0x00, 0x16, 0x3e,
		random.randint(0x00, 0x7f),
		random.randint(0x00, 0xff),
		random.randint(0x00, 0xff) ]
	return ':'.join(map(lambda x: "%02x" % x, mac))

Alright, so now we got everything we need to reconnect to the WiFi. But wait, what about this fancy login-page, where you have to confirm the ToS before you can use the internet? In fact, that was more like a challenge. Let’s fire up Wireshark and try to connect to What happens: You’ll be redirected to a perlscript on a local server::;=

What’s next? Believe it or not, but what follows is another redirect.::;=;≈_name=&ssid;=≈=&mac;=00%3A16%3A3e%3A7e%3Ab1%3A7f

This is finally the loginpage you get to see in your browser. It contains a form with some fancy hidden inputs like

<input type="hidden" name="bs_password" value="bigmac" />

McDonalds? I love you for that! :D

In fact there are these fields:

  • bs_name
  • bs_password
  • username1
  • password1
  • which_form
  • destination
  • agree (a checkbox)

Now we could just hardcode all the parameters into our script or we could do it the fun way:

f = urllib2.urlopen(loginurl)
html =
for line in html.split("\n"):
	if line.find('bs_name') > -1:
		bs_name = line[line.find('ue="')+4:line.find('">')]
	elif line.find('bs_password') > -1:
		bs_password = line[line.find('ue="')+4:line.find('">')]
	elif line.find('username1') > -1:
		username1 = line[line.find('ue="')+4:line.find('">')]
	elif line.find('password1') > -1:
		password1 = line[line.find('ue="')+4:line.find('">')]
	elif line.find('which_form') > -1:
		which_form = line[line.find('ue="')+4:line.find('">')]
	elif line.find('destination') > -1:
		destination  = urllib.quote(line[line.find('ue="')+4:line.find('">')])
	elif line.find('method="POST"') > -1:
		action = line[line.find('on="')+4:line.find('"" acc')]
data = "bs_name=%s&bs;_password=%s&username1;=%s&password1;=%s&which;_form=%s&destination;=%s&agree;=1&submitButton;=&submitButton.x;=1&submitButton.y;=2"%(bs_name, bs_password, username1, password1, which_form, urllib.quote_plus(testurl))
req = urllib2.Request(url=action, data=data)
o = urlparse(loginurl)
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
req.add_header('Origin', "%s://%s"%(o.scheme, o.netloc))
req.add_header('Referer', loginurl)
req.add_header('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1')
f = urllib2.urlopen(req)

Now we can put it all together into one simple script that checks periodically whether the connection is still alive and if not, automagically reconnects us:

It’s not perfect, but at least it works! :)