Environmently friendly reverse engineering
When I moved to my new apartment I discovered a lot of old, mostly networking-related, equipment that I was meaning to either sell or throw out, but I never got to it. Recently I also bought an Arduino and decided to learn something about a lower level of reverse engineering (e.g. the CTF binaries from RHme 2). This two things combined mean it's time for some router reverse engineering!
A word of warning: before I started playing with low level RE I didn't know anything about electronics. If you have some experience with hardware hacking, this post may be a bit too basic.
As you can see there's a huge chip in the middle of the board and some additional smaller ones to the left and next to the Ethernet port. They all have markings, so obviously my first instinct was to find their datasheets. The chips are:
HELP (?) : Print this help message
D <Address> <Len>
EW <Address> <Value1> <Value2>...
EH <Address> <Value1> <Value2>...
EB <Address> <Value1> <Value2>...
EC <Address> <Value1> <Length>...
CMP: CMP <dst><src><length>
IPCONFIG:<TargetAddress>
J: Jump to <TargetAddress>
FLW: FLW <dst><src><length>
FLR: FLR <dst><src><length>
LOADADDR: <Load Address>
AUTOBURN: 0/1
File Start: 80300000,length=00000000
**TFTP GET File test.bin,Size 00000000 Byte
-
*TFTP Client Download Success! File Size = 00000000 Bytes
.Success!
So the downloaded file will start from the 80300000 offset in the memory. Now the only thing left is to load flash into that offset:
FLR 80300000 0 200000
Another tftp get and we get the firmware dump. The only thing to remember is to switch the tftp mode to binary. Otherwise you will lose a couple of hours trying to debug the Edimax firmware file format and wanting to throw your laptop out of the window...
Now we just use binwalk with very useful -Mre options to extract everything from the firmware image. Binwalk output on the dump file shows this:
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
5424 0x1530 CRC32 polynomial table, big endian
6816 0x1AA0 gzip compressed data, maximum compression, from Unix, last modified: 2005-06-06 03:22:10
65536 0x10000 CSYS header, big endian, size: 65536
93204 0x16C14 gzip compressed data, maximum compression, from Unix, last modified: 2006-04-24 04:18:31
738779 0xB45DB Zlib compressed data, best compression
1005933 0xF596D Zlib compressed data, best compression
1030794 0xFBA8A Zlib compressed data, best compression
1058027 0x1024EB Zlib compressed data, best compression
A simple grep through everything extracted with "User Name" as argument will find only one file on that file system:
squashfs-root/bin/setup
So this is probably the file where we can find the serial console (or rather Busybox) credentials. When you enter a wrong login/password you get this message:
Hence the first thing to do will be to find that set of strings in the binary and cross reference it with the code. The code at 0x400838 is the one responsible for creating this very nice message. Following the cross references it's easy to see that 0x400CF8 is the subroutine responsible for validating user name and password.
Now, I'm not well versed in the MIPS instruction set, but fortunately it's a rather simple one. The most important parts of the subroutine are:
0x00400d40 addiu a3, a3, 0x7a00 ; "super"
0x00400d44 lb v0, (a3)
...
A word of warning: before I started playing with low level RE I didn't know anything about electronics. If you have some experience with hardware hacking, this post may be a bit too basic.
Typing random numbers into a search bar
My victim is the Edimax EW-7206APg wireless router. The very first thing to do is to remove the screws and look around the PCB (yes, I know that the lighting is horrible).As you can see there's a huge chip in the middle of the board and some additional smaller ones to the left and next to the Ethernet port. They all have markings, so obviously my first instinct was to find their datasheets. The chips are:
- RTL8186 - system-on-a-chip (SoC) with integrated 32-bit MIPS core (aka "cpu")
- PSC A2V64S40CTP - 64 Mb SDRAM chip (aka "memory")
- MX29LV160CBTI70G - 2MB flash memory (aka "flash")
- RTL8201CP - single-chip, single-port 10/100M fast Ethernet phyceiver (coincidentally, if you wonder what "phyceiver" is Wikipedia article prominently features a picture of this exact chip)
- TS6121CX - 10/100 Base-T Ethernet isolation transformer
Multimetering UART
Nothing too unusual, but we now know where are the cpu, flash and memory chips. Next, there are four pins that look suspiciously like UART - a serial interface used to debug PCBs. Fortunately the pins are soldered on so I can just connect cables to them. There are four pins, so the first order of business is to decide which pin does what. The possibilities are:
- TX - used to send data
- RX - used to receive data
- GND - ground
- Vcc - power
Now, in order to find those all you need is a multimeter or a cheap logic analyser.
Let's start with a multimeter and use it as a (very) poor-man oscilloscope. First of all, GND is fairly easy to find. If the multimeter supports continuity measurements, you have to find ground near the DC connector - this is were one of the probes will be attached. Second probe goes to every pin and when the multimeter shows continuity you have your ground. Once you have GND, connect your ground probe to it and a second probe to one of the remaining pins. Set your multimeter to voltage measurement. All other pins have the following, different, characteristics:
- Vcc - will be a constant voltage, around 3.3V in the case of this router.
- TX - will be pulled up (= Vcc) with occasional changes in the voltage. Even my cheap multimeter was fast enough to show those changes.
- RX - will be "floating", which means that the current will be close to 0V, but not exactly 0V. Either way, it's the only pin with a pretty low voltage all the time.
If you want to use logic analyser, you can get a very cheap one (~10 quid) and connect it to the open source sigrok software suite. Once you set up all four channels you'll get something like this:
Now it's fairly obvious what is going on:
- D0 is Vcc (always 1)
- D1 is RX (mostly 0)
- D2 is GND (always 0)
- D3 is TX (some data is being transmitted)
Serial console straight out of hacker movies
We have all that information so it's time to connect router to the UART-USB cable (or just go for an overkill and use Arduino for it). The last thing left is the baud rate. I wanted to use logic analyser to figure it out, but after a failed attempt I just went with a "Numberwang"-style "guess the baud rate" game until I saw something in the console that looked like text. If you don't want to play, the answer is 38400.
Anyhow, by using the wonderful picocom tool I was able to read the serial port and, after a boot sequence, I was welcomed with this rather nice looking screen (colour scheme was chosen by me to enhance the hacking factor).
Bad news is that the usual username/password combinations didn't work. I didn't want to download a firmware and disassemble it, because that would be cheating, instead I wanted to see if I can get the correct credentials from the router itself.
Before that rather lovely screen there was a booting sequence, which could be interrupted by pressing the escape key. When I did I was welcomed with a following prompt:
---Escape booting by user
<RealTek>
Hmmm... some kind of shell. Typing "help" revealed a list of commands, but without any explanation:
HELP (?) : Print this help message
D <Address> <Len>
EW <Address> <Value1> <Value2>...
EH <Address> <Value1> <Value2>...
EB <Address> <Value1> <Value2>...
EC <Address> <Value1> <Length>...
CMP: CMP <dst><src><length>
IPCONFIG:<TargetAddress>
J: Jump to <TargetAddress>
FLW: FLW <dst><src><length>
FLR: FLR <dst><src><length>
LOADADDR: <Load Address>
AUTOBURN: 0/1
After a bit of Internet searching I found a command line reference for the RealTek bootloader. Two particularly interesting commands are:
- D <Address> <Len> - print the "Len" bytes starting at "Address" as a hexdump
- FLR <dst><src><length> - read "length" bytes from flash memory starting from "src" and copy them to RAM starting at "dst"
Now, these two commands can be used to read the whole flash memory. From the flash memory datasheet we know that it holds 2MB, which is further confirmed by the log message during the boot process:
Found 1 x 2M Byte MXIC MX29LV160AB at 0xbe000000We also know where to write the flash memory:
Jump to image start=0x80300000...
TFTP and a firmware dump
This is all fine, but parsing a hexdump is not my favourite thing to do. There's another hidden feature of the RealTek bootloader: tftp get. Bootloader uses a tftp protocol to get a new firmware image. You can just send it using the PUT command. However, it also supports the GET command so that you can download a firmware image. How convenient! When the tftp IP is set up using the IPCONFIG command GET results in the following lines in the serial console:File Start: 80300000,length=00000000
**TFTP GET File test.bin,Size 00000000 Byte
-
*TFTP Client Download Success! File Size = 00000000 Bytes
.Success!
So the downloaded file will start from the 80300000 offset in the memory. Now the only thing left is to load flash into that offset:
FLR 80300000 0 200000
Another tftp get and we get the firmware dump. The only thing to remember is to switch the tftp mode to binary. Otherwise you will lose a couple of hours trying to debug the Edimax firmware file format and wanting to throw your laptop out of the window...
Now we just use binwalk with very useful -Mre options to extract everything from the firmware image. Binwalk output on the dump file shows this:
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
5424 0x1530 CRC32 polynomial table, big endian
6816 0x1AA0 gzip compressed data, maximum compression, from Unix, last modified: 2005-06-06 03:22:10
65536 0x10000 CSYS header, big endian, size: 65536
93204 0x16C14 gzip compressed data, maximum compression, from Unix, last modified: 2006-04-24 04:18:31
738779 0xB45DB Zlib compressed data, best compression
1005933 0xF596D Zlib compressed data, best compression
1030794 0xFBA8A Zlib compressed data, best compression
1058027 0x1024EB Zlib compressed data, best compression
If you compare it to the firmware updates, you can see that there's a section right before 0x10000 that doesn't appear in updates. Just a quick look at strings in that part confirms that it's the RealTek bootloader. Then you have the squashfs file system at 0x16C14.
A simple grep through everything extracted with "User Name" as argument will find only one file on that file system:
squashfs-root/bin/setup
So this is probably the file where we can find the serial console (or rather Busybox) credentials. When you enter a wrong login/password you get this message:
Hence the first thing to do will be to find that set of strings in the binary and cross reference it with the code. The code at 0x400838 is the one responsible for creating this very nice message. Following the cross references it's easy to see that 0x400CF8 is the subroutine responsible for validating user name and password.
Now, I'm not well versed in the MIPS instruction set, but fortunately it's a rather simple one. The most important parts of the subroutine are:
0x00400d40 addiu a3, a3, 0x7a00 ; "super"
0x00400d44 lb v0, (a3)
...
0x00400d50 sb v0, 0x20(sp)
...
0x00400db4 addiu a1, sp, 0x20
0x00400db8 addiu a2, zero, 5
0x00400dbc lw t9, -0x7ed4(gp)
0x00400dc0 jalr t9
First v0 holds the super string (that's what's at the 0x7a00 lower bytes address). Then a subroutine is called with parameters a0 (= the value entered as "User Name"), a1 (= "super") and a2 (= 5). That subroutine just does a simple comparison between a2 characters of the first two arguments. This way we have the proper "User Name".
The password part is the really fun part. If we follow all other calls to that string compare function we will find three more: two that compare the password part to "go" and one that compares it to "linux". This is done presumably to "obfuscate" the password so that it doesn't show up when you run the strings command on the binary. Additionally there's this check before all the string compare calls (v1 contains a string that user entered as "Password"):
0x00400de8 addiu v0, zero, 0x40 ; '@'
0x00400dec addiu a1, sp, 0x18
0x00400df0 beq v1, v0, 0x400e2c
Your homework is to patch the password given all this information ;)
Finally, after entering proper credentials we get a Busybox shell:
# sh
BusyBox v1.00-pre8 (2005.11.23-10:23+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
P.S. If you want to try the software reverse engineering part yourself, you can download a firmware from the Edimax website. Then just follow along with the blog post starting at the binwalk command.
...
0x00400db4 addiu a1, sp, 0x20
0x00400db8 addiu a2, zero, 5
0x00400dbc lw t9, -0x7ed4(gp)
0x00400dc0 jalr t9
First v0 holds the super string (that's what's at the 0x7a00 lower bytes address). Then a subroutine is called with parameters a0 (= the value entered as "User Name"), a1 (= "super") and a2 (= 5). That subroutine just does a simple comparison between a2 characters of the first two arguments. This way we have the proper "User Name".
The password part is the really fun part. If we follow all other calls to that string compare function we will find three more: two that compare the password part to "go" and one that compares it to "linux". This is done presumably to "obfuscate" the password so that it doesn't show up when you run the strings command on the binary. Additionally there's this check before all the string compare calls (v1 contains a string that user entered as "Password"):
0x00400de8 addiu v0, zero, 0x40 ; '@'
0x00400dec addiu a1, sp, 0x18
0x00400df0 beq v1, v0, 0x400e2c
Your homework is to patch the password given all this information ;)
Finally, after entering proper credentials we get a Busybox shell:
# sh
BusyBox v1.00-pre8 (2005.11.23-10:23+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
P.S. If you want to try the software reverse engineering part yourself, you can download a firmware from the Edimax website. Then just follow along with the blog post starting at the binwalk command.
Comments
Post a Comment