Bluetooth → Wi-Fi Code Execution & Wi-Fi Debugging

 If you ever looked into one of Broadcom's combo chip datasheets, you might have noticed a feature called "WLAN RAM Sharing". In the following, I will explain how to use it to get controlled code execution on Wi-Fi via Bluetooth, which can be helpful in a couple of scenarios:

  • Hack around in Wi-Fi firmware during runtime without disabling SELinux on Android (Nexmon requires kernel patching with disabled SELinux).
  • Indirect Wi-Fi firmware hacking support on platforms that are not supported by Nexmon (iOS, macOS, etc.).
  • Reaching states in the Wi-Fi driver one could not reach over-the-air (💥💥💥).

WLAN RAM Sharing?!

Francesco and me looked into so-called coexistence features. If you want more details on this, watch our DEF CON or Black Hat talk from last year :)

In short, despite running on different ARM cores, Broadcom and Cypress BT/Wi-Fi have coexistence features, which include a unidirectional RAM sharing feature. This feature is shown in some leaked datasheets in the schematic overview, e.g. on the BCM4339 (aka BCM4335C0) that is in the Google Nexus 5. The feature is not described in detail, but at least it is shown in the overview. While coexistence is a great feature in terms of performance on a shared medium, this is definitely the place to implement bugs that break chip separation 👀
On most Broadcom chips, the Bluetooth memory region from 0x680000 onward is mapped to the Wi-Fi memory region 0x180000 onward. The Wi-Fi RAM starts on 0x170000 on most chips, so we miss a small region in the beginning of the Wi-Fi RAM. I have not found/verified the region's end yet, but it's definitely almost the full Wi-Fi RAM that is mapped. While reading from that region often results in zeros or shows weird repeating blocks, writing to that region is always successful.

InternalBlue vs. Nexmon Platform Support

Nexmon was built to support Wi-Fi monitoring mode on the Nexus 5, thus the name Nexmon. Meanwhile, it supports many other features including writing to a raw IQ sample buffer turning your Nexus 5 into a (very limited) 2.4 GHz SDR. It can also read raw channel state information, enabling various measurements on realistic platforms that are not an SDR. Nexmon never had a focus on security research, despite being used by many external security researchers. For its application, it is totally legit to never ever update your system once Nexmon is working or downgrade the Wi-Fi firmware to a build that is older than a year, since it is all about utilizing low-level functionality of a chip. Features are released for such firmware builds and are rather stable. Thus, it can be used by wireless researchers who are into signals but have no clue about firmware hacking.

InternalBlue is more like a toolsuite to enable Bluetooth firmware hacking. The patches are a bit more unstable and usually ugly raw Assembly instead of slightly more readable C patches. However, it tries to support the most recent platforms and builds no matter how ugly the hack for this gets. This means that you can run InternalBlue on the most recent iOS and macOS builds, Android 11, and, obviously, Linux. (Still no Windows support, sorry...) All it needs is a rooted smartphone. Since macOS Bug Sir it also requires disabled SIP for writing to the Bluetooth chip's RAM.

I didn't look to much into Nexmon internals and how it interacts with the drivers. Yet, one of the requirements for Android is to disable SELinux. On recent Android builds, even if you root your phone with Magisk, SELinux is still enabled, even after running setenforce 0 etc. You can of course build a custom kernel but that's a lot of work. In short, Nexmon won't run on a Magisk-rooted Samsung Stock ROM out of the box. Also, no iOS support etc.

Wi-Fi Debugging without Nexmon

So, what is left if there is no Nexmon support?

Apple devices are straightforward to debug :) On iOS with a Wi-Fi debug profile, a new Wi-Fi folder will appear in the crash log directory whenever the chip crashes. The same is the default behavior on macOS without any profile installed. These crash logs are quite verbose and they even contain a full RAM snapshot of the Wi-Fi chip. Note that these snapshots are mixed data+code and IDA will do a very bad job in analyzing those. If you want to take a look into one of these RAM snapshots, they're called SoC_RAM.bin and you can find one in the Thumbs Up GitHub issues. Be a bit careful when sharing these snapshots, depending on the RAM state they'll contain your Wi-Fi access point names and passwords.

Samsung Android sometimes has Wi-Fi debugging enabled—sometimes. I haven't figured out all the details yet. For me, it was working on a Samsung Galaxy S8 on the G950FXXS4CRLB build (Android 8), but for whatever reason it didn't work any more on the S8 build with the security patch level of October and December 2020 (Android 9). It also worked on a Samsung Galaxy S10e on some Android 10 build. If it works, there is a directory called /log/vendor/wifi or similar and it will contain files like this:


...again containing various information on the RAM state and crash reason.

Even if this debug feature is not enabled, the Wi-Fi chip still has console output that is logged to the kernel. Some of this output is prefixed with CONSOLE, at least on the Samsung Galaxy S8 chip. Older chips, like the one in the Galaxy S6, do not seem to prefix it in the same way and/or are less verbose, so you should definitely double-check the output for your specific chip. If the chip crashes, you can always observe it as follows:

# cat /dev/kmsg | grep CONSOLE

4,264114,2745545998,-;CONSOLE: 000296.572 
4,264115,2745546014,-;CONSOLE: FWID 01-2c4297ff
4,264116,2745546030,-;CONSOLE: flags 30040007
4,264117,2745546045,-;CONSOLE: 000296.572 
4,264118,2745546063,-;CONSOLE: TRAP 3(2bfe68): pc cafebabc, lr 1841d5, sp 2bfec0, cpsr 20000193, spsr 20000013
4,264119,2745546080,-;CONSOLE: 000296.572   ifsr d, ifar cafebabc
4,264120,2745546098,-;CONSOLE: 000296.572   srpwr: 0x100f0700 clk:0xb0040 pmu:0x13e 0x3e7ffff7 0x0
4,264121,2745546116,-;CONSOLE: 000296.572   r0 242de4, r1 4, r2 0, r3 1, r4 242de4, r5 0, r6 242de4
4,264122,2745546134,-;CONSOLE: 000296.572   r7 0, r8 0, r9 0, r10 2bdff0, r11 0, r12 1fd3f4
4,264123,2745546150,-;CONSOLE: 000296.572 
4,264124,2745546167,-;CONSOLE:    sp+0 00000000 001c7149 11a0b14d 0000018d
4,264125,2745546184,-;CONSOLE: 000296.572   sp+10 119f2825 00242de4 00000000 00242dc4
4,264127,2745546216,-;CONSOLE: 000296.572 sp+4 001c7149
4,264128,2745546232,-;CONSOLE: 000296.572 sp+c 0000018d
4,264129,2745546248,-;CONSOLE: 000296.572 sp+34 001841d5
4,264130,2745546265,-;CONSOLE: 000296.572 sp+54 001843ef
4,264131,2745546282,-;CONSOLE: 000296.572 sp+6c 0018438d
4,264132,2745546298,-;CONSOLE: 000296.572 sp+7c 001a4ea3
4,264133,2745546314,-;CONSOLE: 000296.572 sp+9c 001a5001
4,264134,2745546331,-;CONSOLE: 000296.572 sp+ac 001c37c5
4,264135,2745546347,-;CONSOLE: 000296.572 sp+b0 001a503f
4,264136,2745546363,-;CONSOLE: 000296.572 sp+c4 001a503b
4,264137,2745546379,-;CONSOLE: 000296.572 sp+d4 001a0629
4,264138,2745546396,-;CONSOLE: 000296.572 sp+e4 001a1c93
4,264139,2745546412,-;CONSOLE: 000296.572 sp+ec 001a19e7
4,264140,2745546429,-;CONSOLE: 000296.572 sp+10c 0003edf1
4,264141,2745546445,-;CONSOLE: 000296.573 Memory usage:
4,264142,2745546462,-;CONSOLE: 000296.573 \x09Text: 569258(556K), Data: 5876(6K), Bss: 4748(5K)
4,264143,2745546481,-;CONSOLE: 000296.573 \x09Arena total: 797432(779K), Free: 64204(63K), In use: 728160(712K), HWM: 745172(728K)
4,264144,2745546500,-;CONSOLE: 000296.573 \x09In use + overhead: 733228(717K), Max memory in use: 1325054(1294K)
4,264145,2745546518,-;CONSOLE: 000296.573 \x09Malloc failure count: 0
4,264146,2745546535,-;CONSOLE: 000296.573 \x09Min heap value is 52260
4,264147,2745546551,-;CONSOLE: 000296.573 ?
4,264148,2745546567,-;CONSOLE: 000296.573 Pool Usage:
4,264149,2745546584,-;CONSOLE: 000296.573 \x09In use pool 24(16): 46080(45K), w/oh: 47520(47K)
4,264150,2745546602,-;CONSOLE: 000296.573 \x09In use Frag pool 400(400): 98400(97K), w/oh: 135200(133K)
4,264151,2745546620,-;CONSOLE: 000296.573 \x09In use Resv pool 80(80): 19680(20K), w/oh: 27040(27K)
4,264152,2745546638,-;CONSOLE: 000296.573 \x09In use RX Frag pool 290(258): 54520(54K), w/oh: 81200(80K)
4,264153,2745546656,-;CONSOLE: 000296.573 \x09In use - pools : 509480(498K), w/oh: 442268(432K)

As you can see, this is already a crash due to somewhat controlled code execution 🎉

Confirming Controlled Code Execution

The Wi-Fi RAM contains a lot of code it executes regularly. This is due to its patching mechanism. On startup, it loads a firmware image that either defines station (client) or access point behavior. Most of the code runs in RAM and is executed in RAM. However, without knowing too much about the currently running firmware, we still need to get somewhat controlled code execution to confirm that the WLAN RAM Sharing feature actually leads to code execution.

For this, create an InternalBlue script based on one of the examples for the chip you're looking at and do the following in a loop:

adr = random.randrange(0x680800, 0x6a1f00)
code = asm("b 0xcafebabe", vma=adr-0x500000)  # rebase to 0x180000
internalblue.writeMem(address=adr, data=code)

The sleep is optional, but without it you will gain code execution so fast that you won't even be able to see at which address that was. Sometimes you need to unlock the phone because Wi-Fi is in a sleep state otherwise. This is the same code execution we demoed in the talk :)

Unpatchable Hardware Issue

The RAM sharing is anchored so deep in the hardware that it still works more than a year after reporting it. There are some security notes by Cypress on this, but I haven't seen any by Broadcom. Since it is unpatchable, it also didn't appear in any iOS or Android release notes. So, just in case you haven't heard about it, this might be the reason ;)


  1. Awesome information about the bluetooth wifi coding execution


Post a Comment

Popular posts from this blog

Always-on Processor magic: How Find My works while iPhone is powered off

Hunting Ghosts in Bluetooth Firmware: BrakTooth Meets Frankenstein