Bypassing hotspot limits on iOS

How to hotspot without a plan

My parents live in a fairly rural area of Texas. There’s no wired internet. Technically there’s a wired phone line, so we had an incredibly flaky DSL setup when I was in high school, but nowadays we use hotspots.

So if there’s no hotspot, there’s no internet.

And when I flew in this winter to visit, my paid hotspot plan didn’t activate on time. So there I was, sitting in quarantine with no internet. But annoyingly, I already had unlimited phone data.

I wondered: why can’t I use my phone’s LTE data to hotspot? As it turns out, it’s not that you can’t—it’s just that iOS doesn’t want you to.

Here’s how I managed to get it working on a regular, non-jailbroken iPhone.

Logs from sshd -d -d

And the speed's not bad!
Note: Cloudflare Warp is the VPN app I use on iOS

How does it work?

When you use a hotspot on iOS, your device treats hotspot packets differently than the data apps use. They get routed differently, and they get metered. And if your hotspot plan isn’t activated, your device won’t even let you hotspot at all.

Connecting through my built-in iOS hotspot doesn't route traffic through my iOS VPN

In order to prevent that, this custom hotspot routes packets through an app (in this case, iSH). This way, your laptop’s traffic gets treated as if it’s just an app making network connections.

Here’s a quick technical overview of the process:

sequenceDiagram participant $P as iPhone 📱 participant $L as 💻 Laptop autonumber % Note over $L: Creates an ad-hoc WiFi network $L->>$L: Creates an ad-hoc WiFi network $P->>$L: Connects to Laptop's wifi network $L->>$P: iPhone assigned an IP address % Note over $P: Sets up SSH server $P->>$P: Sets up SSH server $L->>$P: Laptop SSHes into iPhone $P->>$L: SOCKS5 proxy established $L->>$L: Configures applications to use proxy

After setting this up once, subsequent runs take under a minute

At this point, the custom hotspot is configured. Data from this hotspot is treated just like normal app data!

Setting it up

Here’s a loose idea of how I got this working. I estimate it’d take 15-30m.

  1. Download iSH, an interactive shell, to proxy connections through an iOS app
    • If you previously had an old version of iSH installed, you will need to manually enable its built in apk for package management. New downloads have it enabled by default.
  2. Create a new adhoc wifi network from your laptop, connect to it from your phone, and get your phone’s IP
    • you can quickly verify that the connection works using something simple like python3 -m http.server
  3. Send your SSH public key from laptop → phone
    This was a bit harder without wifi. Here's the quick-and-dirty way I did it On my laptop, I ran cat publickey | base64 | pbcopy, then pasted the result into http://$PHONE_IP/$RESULT. This hit the server on my phone (run using python3 -m http.server), which showed it in the URL path of the server logs on my phone.
    Then on my phone, taking that result from phone logs, I copy → pasted it into a shell command, echo "[paste here]" | base64 -d > publickey.
  4. Install sshd on iSH
    • Add your public key from the previous step to the /.ssh/authorized_keys file
    • Configure the ssh server settings. The changes I made were:
    Port 2222 # Custom port, 22 won't work
    ListenAddress 0.0.0.0 # Allow the laptop to connect via wifi
    PermitRootLogin prohibit-password # Allow root login
    PubkeyAuthentication yes # Allow public key auth
    AuthorizedKeysFile /.ssh/authorized_keys # Allow access from your public key
    PasswordAuthentication no # Disable passwords
    PermitEmptyPasswords no
    Compression no # CPU is a concern more than bandwidth, when we're doing this emulated + local network
    AllowTCPForwarding yes # Allow port forwarding
    GatewayPorts yes # Allow port forwarding
    UseDNS no # Not useful
    PermitTunnel yes # Allow our reverse tunnel
    
  5. Test an SSH connection to the app
    • You should be able to get your phone’s IP from the settings page
    • SSH in with ssh [email protected]$PHONE_IP -p 2222

Now, you should be able to run ssh -N -D 1080 [email protected]$PHONE_IP -p 2222 to create a SOCKS5 proxy server on port 1080 on your laptop! You can open your browser (Firefox, Chrome), configure this as a proxy, and use this to route traffic!

If you only need to use your browser, you’re good! But you might also want to use other tools, and many of these tools don’t respect system proxy settings. For that, we can use proxychains.

Using Proxychains

Proxychains and proxychains-ng are tools that allow you to use a proxy with software that isn’t configured for it.

Since I’m on macOS, and proxychains-ng has better support, I used it here. Here’s the setup:

  1. Install with brew install proxychains-ng
  2. Edit the file /usr/local/etc/proxychains.conf to use socks5 127.0.0.1 1080 as the proxy
  3. Run the program you want to use with proxychains4 program_name [arguments]

Limitations

Though this is a great backup internet solution, it’s not as good as a normal carrier-enabled hotspot. Here’s a little more depth on what its drawbacks are and why they occur.

Desktop limitations

On an unrooted iPhone, you can’t modify iptables or truly run as root, so setting up a VPN server on the phone and connecting to it on your laptop doesn’t work. You’ll have to route the traffic through SSH or another protocol whose server can be run in iSH.

I spent a couple hours, but for whatever reason, I couldn’t get sshuttle working. Proxychains works fine, but it means you need to manually launch every program you’d like proxied, and UDP traffic isn’t supported, just TCP.

Note that you may be able to work around these limitations on Linux, where you have more control over the network stack than on macOS.

App limitations

There are a couple of limitations to this method from the app side, as well. Though they’re both bearable as-is.

1. Backgrounding

When your phone locks, or you switch apps, iSH will get backgrounded, and your connection to the server will be lost.

To allow iSH to run into the background, you can run cat/dev/location > dev/null &, which polls your location, though it’s still a bit flaky. You could also disable auto-lock screen, though remember to re-enable it after you’re done.

2. Heat

After an hour or two of constantly running sshd on iSH, your phone gets warm and toasty. This is because iSH emulates all of its commands—but for good reason, App Store approval!


However, with effort, a custom app could solve the above limitations by:

  1. Allowing more control over backgrounding behavior, including abusing background music for unlimited backgrounding.
  2. Running arm64 sshd without requiring x86 emulation, reducing CPU usage.
  3. And doing both of the above without requiring App-Store approval, since you can load custom apps locally.

Though the current state is good enough for me, so this is left as an exercise to the reader.

Enjoy!