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:

eve@eve:/opt/firmware-mod-kit/trunk/fmk/rootfs$ cp $(which qemu-mipsel) .
eve@eve:/opt/firmware-mod-kit/trunk/fmk/rootfs$ sudo chroot . ./qemu-mipsel ./bin/ls 
bin          lib          mnt          root         usr
dev          linuxrc      proc         sbin         var
etc          lost+found   qemu-mipsel  tmp          www

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.

All this is easily automated with the following bash script:


LEN=$(echo -n "$INPUT" | wc -c)

if [ "$LEN" == "0" ] || [ "$INPUT" == "-h" ] || [ "$UID" != "0" ]
	echo -e "\nUsage: sudo $0 \n"
	exit 1

cp $(which qemu-mipsel) ./qemu

echo "$INPUT" | chroot . ./qemu -E REQUEST_METHOD="POST" -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REMOTE_ADDR="" -g $PORT /usr/bin/my_cgi.cgi 2>/dev/null

rm -f ./qemu

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:

eve@eve:/opt/firmware-mod-kit/trunk/fmk/rootfs$ sudo ./run_cgi.sh "request=login&user_name=admin&user_pwd='%20or%20'1'%3D'1"

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!

Tagged , , . Bookmark the permalink.

17 Responses to Exploiting Embedded Systems – Part 3

  1. Francisco says:

    Great serie of articles! Keep up the good work!

  2. ethy says:

    > 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!

    Wow, that sounds awesome! Looking forward to read part 4.

  3. Aaron H. says:

    Great work! I dabble and ive been wanting a fuzz a web server on MIPS device for a while. The fuzzing part isnt the difficult … it’s catching the results. This will be useful. Nice blog!


  4. Pingback: SCADA Security Friday News and Notes #69

  5. Pintas says:

    Hi again Craig

    My journey had problems, but i am ok 🙂

    Now i want to install qemu to help ida to emulate that file i uploaded in the another post. I dont know if it is possible, i know the cpu is mipsel but is a zny0s system not linux.

    I tried different ways, with diferent operation systems, diferent sources but it not worked 🙁

    I installed dependencies but probably not all ?!

    The errors i geting from the source are not well explained, i searched on google for them and google not helped…

    Now i want to know what distribution version you used , what source of qemu and what dependencies (this part i can search…)

    Sorry for ask that but i tried several hours searching, try anothers… and the result is nothing.

    I have another firmware here. This one is linux, but in binwalk scan only works with -a option and dont display the size of the parts of firmware (in scan).

    Could you give a look? If you want…

    Sorry my english

    Have a nice Sunday

  6. Craig says:

    What errors are you getting when building qemu?

    Do you have a link for the linux firmware?

  7. Pintas says:

    I get diferent errors in diferent moments. I give up now because i cant reproduce my better compiling.

    Now i will look for a OS with the package qemu-user-static.

    The another file i uploaded i think is the kernel, because it have all the strings of web interface of the router. How can i read it in asm code if i know is a mipsel cpu?

    The Linux firmware:


  8. Pintas says:

    Can you give me a tip, how to read the mipsel code? I dont know what program i can use. I have no ideas! Thank you!

  9. Craig says:


    I would recommend using IDA Pro, if you can get it. If not, you can try using objdump to disassemble the code (good info here: https://huaweihg612hacking.wordpress.com/2011/07/14/using-mips-linux-objdump-for-disassembly-of-raw-binary/).

  10. Pingback: Emulating NVRAM in Qemu | /dev/ttyS0

  11. Jeff says:

    Great series, Craig! Thanks for sharing!

    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.

    Thanks again!

  12. Robel says:

    Alternative to statically building qemu is to use PRoot. PRoot is a user-space implementation of chroot, mount –bind, and binfmt_misc.
    Example: proot -r rootfs/ -Q qemu-mips

  13. Rem says:

    Hi, first of all awesome work with this series.

    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

    This is the output of an standard ls of the directory:

    bin cdrom dev etc home init lib linuxrc mnt opt proc qemu-mipsel root sbin sys tango tmp udev usr var

    Does somebody know what’s wrong? I cant figure out what’s the problem.

    • Craig says:

      My guess is that qemu-mipsel is not statically linked. You must have qemu-mipsel built statically in order to run it like this.

      • Rem says:

        Yes that was the problem. I switched to a 32bits OS and installed qemu from the repository:

        sudo apt-get install qemu binfmt-support qemu-user-static

        And instead of copy qemu-mipsel I copied qemu-mipsel-static, now it’s working fine. Thanks.

  14. ednolo says:

    Hi all,

    I was wondering to myself whether it is so bad to use “qemu-mips-static” from ubuntu’s repositories and use “qemu-mips” compiled dynamically in our system. I mean, we have both options in our system.

    Trying to statically compile qemu in Ubuntu12.04.4 was quite impossible, it was always crashing all the time for libgcrypt-libssh2. I was able to solve it by using this combination of flags:

    $ ./configure –static –disable-libssh2 && make clean && make -j2 && sudo make install

    I hope you can give an advice about which approach is better for embedded systems’ analysis.


  15. Silvio L says:

    Hi Craig, excellent posts.

    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 ?

    Thanks again for a great site.

Leave a Reply

Your email address will not be published. Required fields are marked *