Exploiting Embedded Systems – Part 3

In part 2 of this series we found a SQL injection vulnerability using static analysis. However, it is often advantageous to debug a target application, a capability that we’ll need when working with more complex exploits later on.

In this segment we won’t be discovering any new vulnerabilities, but instead we will focus on configuring and using our debugging environment. For this we will be using Qemu and the IDA Pro debugger. If you don’t have IDA you can use insight/ddd/gdb instead, but in my experience IDA is far superior when it comes to embedded debugging.

Our target binaries from the TEW-654TR are little endian MIPS, so we need an emulator in order to run them on our host system. Qemu is the emulator of choice here, as it supports many different architectures and allows you to run an entire system or just a single executable. We will be doing the latter.

But before you go grab the latest version of Qemu from your distro’s repositories, keep in mind that our MIPS binaries are dynamically linked to MIPS libraries. It is a bad idea to mix your target’s libraries in with those on your host system, so we will need to run Qemu in a chrooted environment; this requires that Qemu be statically linked, as your host libraries will not be accessible from inside chroot. If your repos don’t have static builds of Qemu, you’ll have to build from source (recommended, and not hard):

eve@eve:~/qemu$ ./configure --static && make && sudo make install

With Qemu installed, cd to where you extracted the TEW-654TR’s root file system; copy qemu-mipsel there and run a simple command, such as ls, to make sure everything is working properly:

Hey! It works! Now let’s do something useful with it. Let’s get the my_cgi.cgi CGI script that we examined in our previous segment up and running inside of Qemu.

CGI scripts usually take input via environment variables and/or standard input; in order to run the CGI script stand-alone, we’ll need to determine how to pass it HTTP parameters and which parameters it expects. Looking in the CGI script’s main function, we see that it first retrieves the REQUEST_METHOD environment:

If REQUEST_METHOD is set to “GET”, then the CGI script immediately exits. Otherwise, it retrieves three more environment variables: CONTENT_LENGTH, CONTENT_TYPE, and REMOTE_ADDR:

Finally, before processing POST data, the function get_input_entries is called, which references stdin as well as the ‘=’ and ‘&’ POST data delimiter characters:

From this we can deduce that when calling the CGI script we need to set the REQUEST_METHOD, CONTENT_LENGTH, CONTENT_TYPE and REMOTE_ADDR environment variables, and pass in our POST data via standard input.

We’ll need to chroot qemu inside the target’s root file system (as we did previously for the ls command) and pipe our POST data to it through stdin. We can set environment variables for the target binary using the -E option in Qemu; additionally, the value of CONTENT_LENGTH will vary based on the length of the POST data we supply.

This script takes the POST data string as its only argument, sets the appropriate environment variables, runs the my_cgi.cgi binary inside of a chrooted instance of Qemu, and tells Qemu to start a GDB server listening on port 1234.

Usage is pretty straight forward, but be sure to properly URL encode your POST data. To test the SQL injection bug we found in part 2 of this tutorial, run:

Qemu will pause execution until we attach a debugger, so now let’s get IDA ready to do some debugging.

First, we need to configure IDA to connect to our remote GDB server. Go to Debugger -> Process options and fill in the IP address of the machine where you ran Qemu, and set the port number to 1234:

Next, let’s set a breakpoint. We want to make sure that our SQL injection in the password input field is going to result in the expected SQL query, so let’s set a breakpoint on the exec_sql call in the do_login function (F2, or right-click and select ‘Add breakpoint’):

OK, we’re ready to go! In IDA select Debugger -> Attach to process. You will get a pop up box asking which process you want to attach to; select ‘attach to the process started on target’:

You may get a couple other pop-ups warning you about the dangers of running unknown code on your system, which you can ignore. You should now be looking at the paused my_cgi.cgi process in the IDA debugger:

Click the run button (green arrow, top left corner) to allow the process to execute. It should quickly stop on the breakpoint we set:

From the registers window we can see that register $a1 holds a pointer to the sql string; go to that address in the hex view:

Excellent! We can see that our SQL injection has worked and that the SQL query is formatted as expected. Of course, we knew it would be.

Don’t miss the next installment of this series where we’ll be leveraging our newfound debugging capabilities to gain root access on the router!

I’ve been following along with zero problems, until debugging. I hit a slight snag while attaching to the process from IDA Pro. It appears that my registers never populate. But if I run the application and break on the break point, then view the address which should be in $a1, the sql string is there as it should be!

So it seems like everything is solid and working, IDA just doesn’t seem to be displaying any information in the registers window. So I never actually see where my program counter is, or the contents of the other registers. Have you every had this happen? Know the reason or a fix?

I’m using static compiled qemu 1.0.1 on a linux system, and debugging from IDA Pro 6.0 in VMWare Windows VM.

I’m getting problems when I try to run qemu with the firmware I’m analyzing. After copying qemu-mipsel to the folder where the squashfs filesystem is decompressed I try to run the chroot command and get this:

chroot . ./qemu-mipsel ./bin/uname

chroot: failed to run command `./qemu-mipsel’: No such file or directory

I am wondering about something : a bootloader would read the firmware image from flash and uncompress it to RAM. When debugging this bootloader, can we ( should we ? ) create sections of RAM to load the bootloader code and load, like, the flash image file to ROM ?

I am making some exercises, and it seems the second-stage bootloader code runs ok either from RAM or ROM provided I use the correct starting addresses, but it seems to me that during the decompression it would need to read some values from flash ( like the size of the firmware image ) . Should I somehow add the flash image file to the IDA project at the rignt addresses so that it can translate the flash offsets ?