Picture this, you’re a teenager in the middle of the ocean on a cruise ship. All is good, except you’re lacking your lifeblood: internet. You could pay $170 for seven days of throttled internet on a single device, and perhaps split it via a travel router or hotspot, but that still seems less than ideal.
I’ve been travelling Europe with family and am currently on Princess Cruises’ Sun Princess cruise ship. For the past two days, the ship has mostly been in port, so I was able to use a cellular connection. This quickly became not feasible as we got further from land, where there was no coverage. At around the same time, I wanted to download the Princess Cruises app on my phone, and realized that it would give me a one-time 15-minute internet connection. I activated it, and quickly realized that it didn’t limit you to just the Play Store or App Store: all websites could be accessed. I soon realized that this “one-time” download was MAC-address dependent and thus, could be bypassed by switching MAC addresses. However, doing so logs you out of the MedallionNet portal, which is required to get the 15-minutes of free internet.
This means that in order to get just 15 minutes of internet, the following is required:
Change MAC address
Login to MedallionNet with date-of-birth and room number, binding the MAC address to your identity
Send a request with your booking ID activating 15 minutes of free internet, intended to be used for downloading the Princess app
Not completely sure, but it did initially seem that to activate unrestricted access to the internet, you would need to send a simple HTTP to play.google.com, although I don’t think this is needed.
This process, on the surface, seems extremely arduous. But if this could be automated, it would suddenly become a much more viable proposition. After looking at the fetch requests the MedallionNet portal was sending to login and activate the free sessions, I realized that it shouldn’t be too hard to automate.
Conveniently, my family also brought a travel router running OpenWRT as we were planning on purchasing the throttled single-device plan (which is ~$170 for the entire cruise) and using the router as a hotspot so we could connect multiple devices to the internet. This router (a GL.iNet) allows you to change the MAC address via the admin portal, needing only a single POST request. This meant that if I could string together the API requests to change the MAC address (after getting a token from the router login endpoint), login to MedallionNet, and request the free internet session, I would have free internet.
I first tried copying the requests as cURL commands via DevTools into Notepad and using a local LLM to vibe-code a simple bash file. Realizing this wasn’t going to work, I began work on a Python script instead. I converted the cURL commands to requests via Copilot (yes, I used LLMs to an extent when building this) and started chaining together the requests.
The only issues I faced that took some time to overcome were figuring out how to repeat the requests when needed and being resistant to unexpected HTTP errors. For the former, I initially tried repeating it on an interval (first through a while True loop and later via shell scripting in the container) but later realized it was much easier by checking if internet access expired by sending a request to example.com and checking if it fails. For the latter, I used while True loops to allow the requests to be retried and executed break when the requests succeeded. The only other issue, that still exists, is that occasionally, the connection will drop out while the session is refreshed, although this seems to happen less than every 15 minutes and only lasts for a minute or two.
The container running in Docker Desktop, refreshing the session when needed
After comparing speeds with another guy I met on the cruise (who also happens to be a high-school programmer), who had the highest-level MedallionNet plan, there seems to be no additional throttling (7+ Mbps). Even better, after connecting my power bank’s integrated USB-C cable to the router, I’m able to move around the ship for hours. So far, I’ve been using the router for nearly ~7 hours and my 10,000 mAh power bank is still at 42% battery. In fact, I’m writing this very post while connected to the router.
The script
Here’s the code that I’ve been using and a sample .env, although I have a repository
with the Dockerfile and compose file as well. Also, I’m aware the code isn’t pretty, I mostly just wrote it as a proof-of-concept that turned out to work amazingly well. I also doubt I’ll update the code after my cruise ends, since it won’t really be possible to test it. If you try to use this later, and it’s broken, you can go to DevTools, go to the network tab, go to “Fetch/XHR”, and then reload, which should allow you to reverse-engineer the API if you want to try to fix the script.