I recently saw a tweet that struck my curiosity. The background on this is that the TP-Link WA701Nv2
Series WiFi Access Point / Router uses Das U-Boot as a bootloader and also has an “escape sequence” that breaks into the U-Boot shell during the boot cycle. According to the tweet, that “escape sequence” is the string tpl
, entered in right after the Autobooting in ...
prompt. See the tweet for a photo. Read the whole thread here
As part of the twitter thread, the original researcher and author @SecurityJon published a link to the firmware for the router he was looking at - [https://static.tp-link.com/resources/software/TL-WA701ND_V2130528.zip](https://static.tp-link.com/resources/software/TL-WA701ND_V2_130528.zip) in his tweet here. _Warning: I had to navigate through TP-Link’s support pages to actually download the firmware, the direct link showed me a screen that said:
Please go to TP-Link Offical Website to get products, software and services for your region.
The firmware is the third version from the top at this webpage: https://www.tp-link.com/us/support/download/tl-wa701nd/#Firmware
Once I downloaded the firmware image, I unziped TL-WA701ND_V2_130528.zip
which contains wa701nv2_en_3_16_6_up_boot(130528).bin
.
Running binwalk on the extracted .bin
file resulted in this output:
$ binwalk wa701nv2_en_3_16_6_up_boot\(130528\).bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 TP-Link firmware header, firmware version: 0.-17687.3, image version: "", product ID: 0x0, product version: 117506050, kernel load address: 0x0, kernel entry point: 0x80002000, kernel offset: 4063744, kernel length: 512, rootfs offset: 891601, rootfs length: 1048576, bootloader offset: 2883584, bootloader length: 0
14160 0x3750 U-Boot version string, "U-Boot 1.1.4 (May 28 2013 - 11:35:30)"
14208 0x3780 CRC32 polynomial table, big endian
15504 0x3C90 uImage header, header size: 64 bytes, header CRC: 0xFCDBAA10, created: 2013-05-28 03:35:31, image size: 32793 bytes, Data Address: 0x80010000, Entry Point: 0x80010000, data CRC: 0xB9564E5B, OS: Linux, CPU: MIPS, image type: Firmware Image, compression type: lzma, image name: "u-boot image"
15568 0x3CD0 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 93704 bytes
131584 0x20200 TP-Link firmware header, firmware version: 0.0.3, image version: "", product ID: 0x0, product version: 117506050, kernel load address: 0x0, kernel entry point: 0x80002000, kernel offset: 3932160, kernel length: 512, rootfs offset: 891601, rootfs length: 1048576, bootloader offset: 2883584, bootloader length: 0
132096 0x20400 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 2580580 bytes
1180160 0x120200 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 2649889 bytes, 585 inodes, blocksize: 131072 bytes, created: 2013-05-28 03:45:22
Really the only thing I was interested in is this line:
15504 0x3C90 uImage header, header size: 64 bytes, header CRC: 0xFCDBAA10, created: 2013-05-28 03:35:31, image size: 32793 bytes, Data Address: 0x80010000, Entry Point: 0x80010000, data CRC: 0xB9564E5B, OS: Linux, CPU: MIPS, image type: Firmware Image, compression type: lzma, image name: "u-boot image"
Usually in these SoHo routers the bootloader is contained within the firmware image, and this instance was no different. I ran the following dd
command to carve out the bootloader:
$ dd if=wa701nv2_en_3_16_6_up_boot\(130528\).bin of=u-boot.bin bs=1 skip=15504 count=$((131584 - 15504))
If you are wondering where I got 131584
from, its the next location in the binwalk output of anything meaningful. Even if we carve out extra data, it won’t matter.
So now I run binwalk on u-boot.bin
and we see basically the same result:
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 uImage header, header size: 64 bytes, header CRC: 0xFCDBAA10, created: 2013-05-28 03:35:31, image size: 32793 bytes, Data Address: 0x80010000, Entry Point: 0x80010000, data CRC: 0xB9564E5B, OS: Linux, CPU: MIPS, image type: Firmware Image, compression type: lzma, image name: "u-boot image"
64 0x40 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 93704 bytes
This tells me that 64 bytes after the legacy U-Boot header (normally called a uImage) is a lzma-compressed blob. So lets carve out that compressed blob:
$ dd if=u-boot.bin of=u-boot.body.lzma bs=64 skip=1
Next we use 7z
to extract the lzma-compressed data:
$ 7z x u-boot.body.lzma
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=C.UTF-8,Utf16=on,HugeFiles=on,64 bits,8 CPUs Intel(R) Xeon(R) CPU E5-2660 v2 @ 2.20GHz (306E4),ASM,AES-NI)
Scanning the drive for archives:
1 file, 116016 bytes (114 KiB)
Extracting archive: u-boot.body.lzma
--
Path = u-boot.body.lzma
Type = lzma
ERROR: There are some data after the end of the payload data : u-boot.body
Sub items Errors: 1
Archives with Errors: 1
Sub items Errors: 1
The error ERROR: There are some data after the end of the payload data : u-boot.body
simply means we carved out too much data when using dd
to create u-boot.bin
. It does not prevent us from proceeding though. At this point we should have a file named u-boot.body
.
This is the point where we need to fire up Ghidra.
Create a new Ghidra Project and import u-boot.body
. This will not be recognized as any specific type of binary, so the binary format option will be set to Raw Binary
. This is because legacy U-Boot images allowed arbitrary executable data after the header.
Next set the language to MIPS Big Endian Default:
After that you will need to set the load address. We know this value from the binwalk output, which parses the U-Boot header for the information. The correct value here is 0x80010000
- search the binwalk output above for this value if you are confused as to where it came from.
The next thing I did was search for strings. I had to set the length down to 3 in order to catch the string tpl
:
After that I filtered for tpl
:
Double clicking on the string search result takes me to the location in the disassembler that the string search found the string at. This is address 0x800256bc
. We can apply the data type string
to the tpl
characters by select the four bytes at this address, right clicking on the green selection, and navigating to Data->string
. Afterwards, your selection should look like this:
Next we right click on s_tpl_800256bc
and select References->Show References to s_tpl_800256bc
at the bottom of the context-menu. You should see a screen like this:
There should only be one reference, so I double clicked on it and was greeted with the following disassembly:
And with that we have found the location in Ghidra where the “escape sequence” code exists!