Exploiting Embedded Systems – Part 3 – /dev/ttyS0

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:

#!/bin/bash

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

if [ "$LEN" == "0" ] || [ "$INPUT" == "-h" ] || [ "$UID" != "0" ]
then
	echo -e "Usage: sudo $0 "
	exit 1
fi

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="1.1.1.100" -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!

Bookmark the permalink.

Comments are closed.