How a Broken Bike Sync Led Me to Reverse Engineering My Wahoo's Hidden Debug Mode

How a Broken Bike Sync Led Me to Reverse Engineering My Wahoo's Hidden Debug Mode

- 5 mins

My bike rides stopped syncing to my phone. That was it. That was the whole reason I ended up reverse engineering Bluetooth packets, decompiling an Android APK, and getting greeted with “WELCOME TO HELL DEVELOPER” on my cycling computer.

The Problem

I ride a Wahoo ELEMNT Bolt v3. It’s a solid GPS cycling computer – maps, sensors, the works. But at some point my rides just stopped syncing to the companion app on my phone. Frustrating, but not the end of the world. I figured maybe there was a debug mode or some hidden diagnostic that could help me figure out what was going wrong.

So I did what any reasonable person would do: I pulled the APK off the device and started poking around.

Pulling the APK

The Bolt v3 runs a custom Android build. You can connect it over USB and it shows up as an MTP device. The main app is com.wahoofitness.bolt – I grabbed the APK and threw it at jadx to decompile it.

What I found was… a lot more interesting than a sync bug fix.

The App Profile System

Buried in the decompiled source, I found a class called CruxAppProfileType. The device has an internal profile system that gates features:

Value Name Debug Menu
0 STD No
1 BETA Yes
2 ALPHA Yes
3 DEV Yes
4 FACTORY No

Retail devices ship as STD (0). If you could bump that to DEV (3), a whole debug menu unlocks under Settings > Device > Advanced. That sounded like exactly what I needed – a way to poke at the internals and maybe figure out why sync was broken.

The question was: how do you change it?

Down the Rabbit Hole

The profile is stored in SharedPreferences on the device’s internal storage – not accessible over MTP. ADB would let you change it, but ADB access itself is gated behind the ALPHA+ profile. Classic chicken-and-egg.

But then I noticed something in the BLE code. The app has a characteristic called BOLT_CFG that lets the companion app read and write device configuration over Bluetooth. And the protocol has zero application-layer authentication. No HMAC, no nonce, no challenge-response. Security relies entirely on BLE pairing.

Reverse Engineering the BLE Protocol

This is where I teamed up with Claude (Opus 4.6) to work through the decompiled Java and figure out the exact packet format. Here’s what we pieced together:

The BOLT_CFG characteristic uses a simple binary protocol. To write a config value, you send a SEND_PREFS packet:

Offset  Size   Field
------  -----  -----
0       1      Packet type (0x01 = SEND_PREFS)
1       1      Config code
2       N      Value

APP_PROFILE is config code 66 (0x42), encoded as a single byte. DEV is value 3.

So the entire packet to unlock developer mode is three bytes:

0x01  0x42  0x03
 |     |     +-- DEV profile
 |     +-- Config code 66 (APP_PROFILE)
 +-- SEND_PREFS

That’s it. Three bytes over Bluetooth to unlock a hidden developer mode on a retail cycling computer.

Writing the Script

I wrote a Python script using bleak (a cross-platform BLE library) to automate the whole thing. The script scans for Wahoo devices, connects, and sends the packet.

There were a few gotchas we had to work through:

  1. Notification subscription is required first. The device silently drops writes unless you’ve subscribed to notifications on the characteristic beforehand. Took a bit to figure that one out – writes would just disappear into the void.

  2. Write-without-response only. The characteristic doesn’t support write-with-response, so you have to use response=False.

  3. Bonding is required. You need to be paired with the device before it’ll accept writes – so this isn’t something a random passerby can do without the owner’s involvement.

WELCOME TO HELL DEVELOPER

After running the script and rebooting the Bolt, I navigated to Settings > Device > Advanced, and there it was – a brand new Debug Menu option that wasn’t there before. And on screen, a popup:

“WELCOME TO HELL DEVELOPER”  

Wahoo Bolt WELCOME TO HELL DEVELOPER popup  

Wahoo’s devs have a sense of humor.

The debug menu gives you access to:

Beyond the debug menu, DEV mode also enables:

The Bigger Picture

What started as a broken sync issue turned into a pretty thorough look at the Bolt’s internals. The config protocol over BLE has no application-layer authentication – once you’re bonded, you can write any config value the app can. There’s no HMAC, no nonce, no challenge-response on top of the BLE layer.

I ended up documenting the full BLE protocol, the file transfer system, and some other findings in separate writeups.

As for my original sync problem? After all of that – decompiling an APK, reverse engineering a binary protocol, writing a BLE exploit script – it turned out the issue was on my phone the whole time. Not the cycling computer. The phone.

Noah Clements (genoff)

Noah Clements (genoff)

hacker of things

comments powered by Disqus