In part 1 we used the TEW-654TR’s TFTP service to retrieve the administrative credentials to our target system.
But what if we didn’t have access to the TFTP service? Many embedded devices don’t have a TFTP service, or there may be a firewall between us and the target that blocks traffic to UDP port 69. In this case, we’ll have to take a closer look at the web application running on the target.
Using an intercept proxy like Burpsuite (above), we can see that the HTML login page submits our provided credentials to the my_cgi.cgi script. Let’s find that file in our firmware:
eve@eve:/opt/firmware-mod-kit/trunk/fmk/rootfs$ find . -name my_cgi.cgi ./usr/bin/my_cgi.cgi eve@eve:/opt/firmware-mod-kit/trunk/fmk/rootfs$ file ./usr/bin/my_cgi.cgi ./usr/bin/my_cgi.cgi: ELF 32-bit LSB executable, MIPS, MIPS-II version 1 (SYSV), dynamically linked (uses shared libs), stripped
From the Burpsuite capture, we know that the user inputs from the login page are called ‘user_name’ and ‘user_pwd’; let’s start by looking in the CGI script for any references to these strings:
eve@eve:/opt/firmware-mod-kit/trunk/fmk/rootfs$ strings ./usr/bin/my_cgi.cgi | grep -e user_name -e user_pwd print_user_pwd select level from user where user_name='%s' and user_pwd='%s' select level from user where user_name='%s' user_name user_pwd
%s %sselect user_pwd from user where rowid = 1 select conn_type, ip_addr, subnet_mask, gateway, server_ip, user_name, user_pwd from wan_l2tp where rowid = 1 select conn_type, ip_addr, subnet_mask, gateway, server_ip, user_name, user_pwd from wan_pptp where rowid = 1 select conn_type, user_name, user_pwd, service_name, ip_addr from wan_pppoe where rowid = 1
There are several strings here that appear to be SQL queries. The second string looks particularly interesting: it is selecting the ‘level’ column from the ‘user’ table based on the values of ‘user_name’ and ‘user_pwd’.
Using IDA, we find this SQL string referenced in the do_login function:
The user-supplied user name and password are presumably passed to the sprintf function call along with the SQL query. The result is stored in the sql variable, which is subsequently passed to the exec_sql function. There doesn’t appear to be any filtering of the values passed to sprintf, so unless these values are filtered prior to do_login or in the exec_sql function itself, the login appears to be vulnerable to SQL injection.
If our assumptions are true and the user name and password aren’t sanitized before being used in the SQL query, then we should be able to log in using a specially crafted password, such as:
' or '1'='1
This would cause the resulting SQL query to read:
select level from user where user_name='admin' and user_pwd='' or '1'='1'
This should cause the resulting exec_sql function to return the desired level for the admin user. Let’s try it:
Hey! Another 0-day!
It should be noted that we got a bit lucky on this one. The SQL query that we crafted will actually return ALL entries in the level column, but it just so happens that the admin’s entry is the first one so it is used. There are at least two users – the admin account and the unprivileged user account – so if the user account’s entry was first, then we’d end up logging in with unprivileged rights.
Since we are assuming this is a blind attack, we wouldn’t necessarily know that the admin account would be the first returned from the database. To make matters more difficult, the admin and user account names can be modified by the administrator. So a better password to use to ensure we always get admin rights would be something like:
' or level = (select level from user order by level desc limit 1)/*
The above sub-select ensures that we always select the highest level from the user table, no matter what.
This is just the tip of the iceberg; coming up next, we’ll be getting down and dirty with MIPS emulation and the IDA debugger in order to locate and exploit more severe bugs in the TEW-654TR. Check back for part 3 soon!