This weekend, I spent some time continuing my investigation of a Huawei E5573 modem. In this, I was able to identify and briefly PoC a process by which an attacker can gain privileged code execution on this device, without resorting to the “boot pin” trick used by public jailbreaks of this device.
This method works with an E3372 device, and should work across most, if not all, Huawei 4G modems (if not more Huawei devices). To be clear, this is not “elite hacking” – however, I couldn’t find any documentation on the subject while I was doing this work. Furthermore, this method can be used as a “first principles” primitive: this can be done with no prior knowledge of the device.
If you wish to follow along, some files (binary dump, extractor script, minicom script) are available to help you here, if I know you, ask me for the idb:
Introduction – Welcome to the E5573
The Huawei E5573 device is a 4G modem, resold by Optus in Australia, and available elsewhere across the world. This device comes in a small, white plastic case, and when opened, looks like this:
The identifiable debug pins are as follows:
- The pins in the red box up the top are UART. The second and third pins on the top row are receive (data in) and transmit (data out) respectively.
- The three pins on the left, outlined in yellow, are another set of debug pins: the bottomost pin is the “boot pin” – if this pin is grounded when the device is powered on, it will boot into a “usb dload” mode, which can be used to overwrite the device’s bootloader (and thus, firmware). This is the generally used method for jailbreaking and unbricking the device.
- The pins on the right are presumably JTAG – this configuration appears common across Huawei’s devices (as alluded to here), though I couldn’t see anything on any of the pins on a logic analyzer.
My first step was to observe the device booting over UART:
This device then boots Linux, and runs a custom password prompt. A little bit of brute forcing reveals that the password must be 8 characters in length – but no other information is provided.
Inspecting the boot process in further detail, we come across a curious line:
[0000028ms]Heap:0x57d3bbe0 -- 0x57d3c180, 1440Here, I [0000029ms]Please distribute uart with command L/V/M... [0000029ms] heap:0x57d3bbe0 -- 0x57d3c180, 1440
We get a clue from 4pda.ru, from the user forth32:
Plugging this into Google Translate, we learn that one can send “m”, “v” or “l” through the UART connection to boot into different modes: the “M3 console”, VxWorks, or the Linux Kernel respectively. There is at least one more mode available (“K”), uncovered through reverse engineering of publicly available firmware.
Selecting the “L” option allows a regular boot, while selecting V gives a non-interactive VxWorks console (on the E3372, this is interactive – I have no idea why this is switched off here, and don’t know enough about the device to re-enable it).
Selecting “m” gives a much more interesting prompt:
This can be done via a script, passed to minicom via the -S option, to make life a bit easier, so you don’t have to hold down the “M” button on boot. the download link to this script is above.
Typing help gives us the following:
[0000029ms]Welcomehelp [0000726C]exc: help(00000000, 00000000, 00000000, 00000000, 00000000, 00000000)  command list:24  help  d  m  system_error  pm_set_debug  pm_print_debug  pm_wakeup_acore  pm_wakeup_ccore [0000727A] pm_appa9_wsrc_set [0000727C] pm_mdma9_wsrc_set [0000727D] pm_mcu_wsrc_set [0000727E] pm_appa9_wsrc_unset  pm_mdma9_wsrc_unset  pm_mcu_wsrc_unset  cpufreq_print_debug  dump_wdt_hook  ios_list_echo [0000787A] bsp_wdt_print_debug [0000787B] bsp_wdt_stop [0000787C] bsp_dump_bus_error_status [0000787E] set_pm_test_timer [0000787F] m3_send_a_ipc  m3_send_c_ipc  set_pm_dfs_profile exc: help = 0
A little bit of experimentation reveals that “d” allows us to write memory, and “w” allows us to dump memory. Starting from this, I ran a memory dump from 0 to 0xFFFFFFFF, logging the results to file. This generated output in the following form:
It should be noted that you cannot obtain a “full” memory dump – the device seems to crash after a few moments of printing memory, and needs to be recovered via a power cycle. Furthermore, this does not seem to follow a sane memory mapping: that is, dumping at 0x0 and 0x10000000 produce the same result. I wrote a Python script to help extract this from a minicom capture file – this script is available above.
Into the Unknown
Running strings across the file reveals that we are either looking at some arbitrary process memory, or more likely, the Balong MCU Console itself:
Unfortunately, the memory dump is not in a sane executable format. Loading this into IDA does not reveal any kind of header. We know from the boot log that the processor is ARM, so we can simply load this into IDA, and begin forcing conversion to ARM code, and see what we get:
The initial instructions don’t look like valid ARM, but if we continue through the code, we can see meaningful ARM functions (wrapped with sane push/pop instructions), as well as data starting to take shape:
Curiously, we see several references to something in the 0x10000000 memory region. On a hunch, I decided to rebase the executable at 0x1000000, and try again. This time, the code comes apart easily, and IDA Pro’s cross-referencing capability shines through: these mysterious DWORDs are mostly function pointers to elsewhere in the code.
Going back to the start of the memory dump, we can also recognize a similar pattern – the code at 0 isn’t code, but is infact an array of dwords.
Half an hour of reversing later, the smell of success is on the air – IDA manages to successfully cross-reference a function to a string reference from within the memory dump, indicating that this is indeed the MCU Console itself:
This appears to be a triggerable number-of-arguments check – and is enough for us to begin proving that we can execute arbitrary code without the boot pin, via the MCU Console alone.
Modifying Code Flow
To prove we can execute arbitrary code, it is enough that we prove we can control code flow. The above check is a fantastic place to start: we can trigger this code flow, and the result is logical – if there are more than 5 arguments, a message will be displayed about there being too many arguments and the command will not execute. If we can lower this limit, we can control the code executing.
With a little experimentation, we can work out that the “m” command allows us to edit memory, but only one dword at a time, as follows (you must supply a full DWORD, or it will assume the missing bytes are zero and overwrite your target address):
m 0x1000F000 0xAABBCCDD
We can use this to modify the check at 0x1000075C, as follows, keeping endianness in mind:
m 0x1000075C 0x0F02F1BB
This should mean that we can supply no more than two arguments to a command, where previously, we could supply a maximum of 5. Firstly, let’s test that “d” with 5 arguments behaves as expected (the test cases below use minicom scripts for automation, so it’s missing some output – but close enough).
Now, we add the “m” command to the script, and try now:
We can edit the error message as well:
Final Thoughts + Further Work
At this point, we have:
- Identified an unknown binary, and recovered it into a somewhat sane format for further analysis.
- Proven we are able to both edit resources in the binary, as well as arbitrarily execute code
- Demonstrated privileged code execution without the “boot pin” trick / with a fresh bootloader (as opposed to using one from routerunlock.com to jailbreak your device).
This should be enough to load arbitrary privileged code, albeit extremely slowly via UART.
While public jailbreaks exist for this device (and are much faster), this work acts as a fundamental “lego block” for reverse engineering of both the E5573 device, and other devices where a jailbreak may not already exist.
From here, I will aim to continue my exploration of this device, using it as a practice platform to learn hardware security.