Exploiting Embedded Systems – Part 4 – /dev/ttyS0

So far in this series we’ve found that we can log in to our target TEW-654TR router by either retrieving the plain text administrator credentials via TFTP, or through SQL injection in the login page. But the administrative web interface is just too limited – we want a root shell!

We’ll be doing a lot of disassembly and debugging, so having IDA Pro Advanced is recommended. But for those of you who don’t have access to IDA, I’ve included lots of screenshots and an HTML disassembly listing courtesy of IDAnchor.

In the last segment we set up our debugging environment using Qemu and the IDA debugger. In this final segment we’ll be debugging the my_cgi.cgi executable in order to gain a better understanding of how this CGI script works, and ultimately to pop a root shell.

Let’s start by analyzing how my_cgi.cgi handles POST data. We know from our previous segments that the POST data sent during authentication is:

request=login&user_name=admin&user_pwd=password

However, there doesn’t appear to be any references to the POST parameter names, such as ‘request’:

eve@eve:~/tew654/rootfs/usr/bin$ strings my_cgi.cgi | grep -w request
eve@eve:~/tew654/rootfs/usr/bin$

In most high level languages, GET and POST parameters are referenced by name, such as $_POST[‘request’] in PHP. But since our my_cgi.cgi doesn’t contain the string ‘request’, we can assume that the POST parameters are being parsed in another fashion.

We can see that around address 4094F8 there are string comparisons against several different strings, including ‘login’; this looks like the code that processes the POST request parameter:


Let’s set a breakpoint at 4094F8 and start debugging my_cgi.cgi to get a baseline for the code flow of the program:

eve@eve:~/tew654/rootfs$ sudo ./run_cgi.sh "request=login&user_name=admin&user_pwd=password"

When the breakpoint is hit, we can see that each of the string comparisons are being done against the string pointer stored in $s0. The $s0 register contains a pointer to the string ‘login’, which was passed in via the ‘request’ POST parameter:


Now let’s see what happens when we change the POST parameter names. We’ll change ‘request’ to ‘foo’:

eve@eve:~/tew654/rootfs$ sudo ./run_cgi.sh "foo=login&user_name=admin&user_pwd=password"

And at the breakpoint we see…that nothing has changed:


So it looks like my_cgi.cgi doesn’t process the POST parameters by name, but rather by the order in which they appear in the POST data. It expects the request value to be first, the user name to be second and the user password to be third, and as long as we follow this order we can name the parameters anything we like.

Continuing on in IDA, we see that since our request string matches the ‘login’ string, the branch to 40964C is taken:


Here the request string is compared to some additional – and more interesting – strings:


The ‘admin_webtelnet’ string is particularly interesting, but our request string is ‘login’, so we will never reach that section of code. Let’s restart our emulator, this time passing the string ‘admin_webtelnet’ as our request string:

eve@eve:~/tew654/rootfs$ sudo ./run_cgi.sh "request=admin_webtelnet&user_name=admin&user_pwd=password"

Now, we want to end up at location 40964C where all the interesting string comparisons take place. But remember, our request string is first compared at our breakpoint address of 4094F8. And, since our request string does not match ‘login’, ‘logout’, or any of the other strings in that first set of comparisons, rather than ending up at our desired location of 40964C, we end up falling through to this section of code instead:


We see that there is a call to the update_login_time function, and if that function returns a non-zero value, we branch to our desired location at 40964C. We know that the TEW-654 uses a sqlite database, so we can presume that this function updates a timestamp showing the last period of activity for an authenticated user. But, if no user has authenticated, then there will be nothing to update and the SQL update will fail.

What is going on here is that the first string comparisons that we saw at our breakpoint address are the requests that you are allowed to make without authentication. If your request matches one of these, then the code moves on to the real request parsing routine at location 40964C. But, if your request is not one of these allowed values, then the code verifies that you have authenticated before moving on to the request parsing routine.

Now, since we are running my_cgi.cgi as a stand-alone application inside Qemu, we obviously have not authenticated to the device. However, we already have a couple authentication bypass exploits, so we are going to cheat and make my_cgi.ci think that we have authenticated.

In IDA’s registers window, right-click the $v0 register and increment it from zero:


To one:


The code now branches up to location 40964C. And, when our request string is successfully matched against ‘admin_webtelnet’, the address of the function send_telnet_cmd is loaded into $t9 and a branch down to 409ACC is taken:


At location 409ACC, the value loaded into $t9 (send_telnet_cmd) is called:


Stepping into this function call, we see that send_telnet_cmd creates a command string using sprintf, piping the output to the /tmp/tmp_send_result file:


Looking at the values passed to the sprintf function, we see that ‘admin’ – the value of our user_name POST parameter – is being used as the command to execute:


Knowing this, let’s re-run my_cgi.cgi with an actual shell command for the second POST value:

eve@eve:~/tew654/rootfs$ sudo ./run_cgi.sh "request=admin_webtelnet&cmd=echo test"

And check the result in the debugger:


Looks good! Let’s try it out on the live device (remember to first log in using the SQL injection or TFTP exploits):


We can verify that the echo command was successfully executed by requesting the /tmp/tmp_send_result file via TFTP:

eve@eve:~$ tftp 1.1.1.102
tftp> get /tmp/tmp_send_result
Received 2 bytes in 0.0 seconds
tftp> quit
eve@eve:~$ cat tmp_send_result
1
eve@eve:~$

Now that we’ve verified that it works, we can try some more interesting commands. Let’s start up a telnet service:


And drop the firewall:


And get a shell:

eve@eve:~$ telnet 1.1.1.102
Trying 1.1.1.102...
Connected to 1.1.1.102.
Escape character is '^]'.


BusyBox v1.01 (2011.05.30-12:58+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

/ # iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain MINIUPNPD (0 references)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain WSC_UPNP (0 references)
target     prot opt source               destination

We’re now free to upload and execute whatever we want, and that concludes this tutorial series. But there are plenty more vulnerabilities in the TEW-654 that we just don’t have time to cover, so I encourage you to take a look for yourself and see what you can find!

Bookmark the permalink.

Comments are closed.