In the last blog post, "Opps I pwned your router Part One", I talked about some of poor security that went into the basic embedded router operating systems. In this post, I will flush out in more detail how one can go about reverse engineering these devices, what tools worked for me, and some of the results that I was able to get to. Hint: Having root on your hardware is good for me, bad for you.
This is Customer Premises Equipment (CPE) device that provides end users with Internet access. Thus only the head end systems should have access, more specifically the end user should not.So first lets talk about some of vectors I used to attack this embedded device:
- JTAG Header
- Web Server & CGI binary
I want to identify some of the issues with these embedded routers, and hopefully will prevent future mistakes for vendors and manufactures.
The device had SNMP enabled, with the default read-only string set to "public", and it was accessible from the user network segment. As this is Customer Premises Equipment (CPE) device, only the head end systems should have access. In this case the end user should not have any sort of device or managment access.
I was able to walk the MIB, and see all the devices interfaces/IP Addresses, memory usage, mount points, operating system version, etc. Most importantly I was able to ascertain the CPU architecture. All of these things were very helpful in narrowing down what tools I could use, and how I could use them. If I didn’t determine these things first, I would be taking shots in the dark. Knowing that the system was MIPS allowed me to create a targeted payload later on.
Besides the Cisco devices that are EOL, or lazy network admins, who uses telnet anymore? Yes, you own the network, but that doesn’t make it secure. Use SSH for all management communication.
When I first arrived on-site, I could telnet to the device. My initial thought was that brute forcing the password was going to take eons, otherwise I would have started here. However after the fact, a simple dictionary attack may have worked using something like the “rockyou” wordlist or the “all.txt” word list from John The Ripper. However it would have had to go through 31k passwords before guessing it. A brute force attack is still possible, with enough time (which I didn’t really have).
However, one major fail was that when the CPE was disconnected from the head end, by telneting to the device, it dropped me right into the device as root. As soon as the head end was plugged back in, this was disabled, however I could update iptables and .ssh/authorized_keys and SSH right back into the router, oops.
JTAG – FTW!
This router had a JTAG header. What is JTAG you ask? It stands for Joint Test Action Group, a standard for debugging CPU’s, and memory. What an awesome tool this must be for hardware and software developers. With it you can, read the raw memory registers, halt the CPU, dump the memory of the system, dump the flash memory, etc. Oh, almost forgot, and you can write back to the flash memory so you update and change the file system and registers.
As for changing the registers in flash, this has been something that hardware manufactures have done for many years. This was how you would do a password recovery of a Cisco router or switch. From the console you set a register to read it’s configuration file from a different location on the flash ROM, and poof- it boots as if it’s a new device.
With this device, there were a few settings in the registers that also allowed basically putting this device into password recovery mode, however I’m not going to spill the beans on this one.
Next I dumped the Image via the JTAG header, and got a binary file with (surprise) a file system in it. Awesome! Now I can recover the files on the system with out having to break into it via the Serial port.
I wish that this was the way that it actually worked when I was on-site, however it didn't. I didn't arrive on-site with a JTAG reader, not something in my kit that I had with me. So I had the $Client put JTAG pins on and dump the firmware via their JTAG equipment. It was super slow, like 26 hours to dump a 12M image. Oh and the image was corrupted when I came in the next day to start analyzing the data.
Curses! What now? I've only got one more day on-site...
Finally I talked them in to delivering the .BIN image that they write to the device at the factory. Thank goodness, as my time on-site was limited, and should not be wasted. Now that I have the binary image, I started with a few tools:
The "file" command identified this as "data", yes I know it's data, but how do I break out the stuff inside the file. While I started doing research on unrolling the binary file, I found a project named "firmware-mod-kit" that will unroll boot images and allow you to modify a BIN image replace it's kernel, etc and repackage it up for upload to the router. Sweet, this must be it! I now I can go home.
Not so fast, this tool really only works with OpenWRT distributions. Most certainly not my MIPS router or custom distributions. So basically, I had to do everything by hand to recover the file system and the image.
First step, identify where stuff lives in the binary image using binwalk:
$ binwalk router-1.2.3.bin DECIMAL HEX DESCRIPTION ------------------------------------------------------------------------------------------------------- 20 0x14 gzip compressed data, from Unix, DD-WRT date: Wed Dec 31 18:00:00 1969
Ok, so we know that something gziped starts at offset decimal 20 of the image. Lets extract that shall we.
$ dd if=router-1.2.3.bin of=router-p1.gzip skip=20 bs=1 12522993+0 records in 12522993+0 records out 12522993 bytes transferred in 53.385772 secs (234575 bytes/sec)Ok, now what is this again?
$ file router-p1.gzip router-p1.gzip: gzip compressed data, from UnixOk, good it's a GZIP compressed archive. Lets ungzip it.
$ zcat router-p1.gzip > router-p1.img gzip: router-p1.gzip: decompression OK, trailing garbage ignoredNow let's see if there is anything behind that.
$ binwalk router-p1.img DECIMAL HEX DESCRIPTION ------------------------------------------------------------------------------------------------------- 0 0x0 ELF 64-bit MSB executable, MIPS, MIPS64 rel2 version 1 (SYSV) 30715 0x77FB LZMA compressed data, properties: 0xAA, dictionary size: 759693312 bytes, uncompressed size: 167788804 bytes 52267 0xCC2B LZMA compressed data, properties: 0x9F, dictionary size: 757596160 bytes, uncompressed size: 203637768 bytes (snipped)Awesome! We have a binary, that appears to be MIPS from blocks 0 - 30714. So lets try to extract the squashfs LZMA file system now.
$ dd if=router-p1.img of=router-p2.lzma skip=30715 bs=1 15898717+0 records in 15898717+0 records out 15898717 bytes transferred in 67.454183 secs (235697 bytes/sec)At this point we should be able to mount the file system:
$ mount -o loop -t squashfs router-p2.lzma /mnt/tmp
Now we can "cd" into /mnt/tmp, we should see the file system root, and from this we can browse and snoop around the file system!
Get your passwords, get your passwords here!
From this we can see that the file system is just like any other Linux system, and contains a "passwd" file. The /etc/passwd file (in this case, as they don't have the shadow files enabled) I can easily read out the static password hash and toss that over to John The Ripper.
Cool, now with this info I can telnet or SSH to the router, and get a root shell! Sweet! But what if they change the password, or remove my access? How can I get back in?
Web Server: BUT WAIT! There is more!
Now that I have access to the CGI-BIN files, I can extract the C++ web application that this router is running for administration and configuration. Now to disassemble the binary, and identify if there are any buffer overflows that I can take advantage of. For that I used IDA Pro to disassemble the binary into something that I can at least start to make sense of.
From that I got a few entry points (of which I had a funny feeling about already) that I confirmed where I could start smashing. Before I ran IDA Pro, I was playing with all the fields of the router's web application using BurpSuite, and found one page that silently died when I sent more then 24 characters on an input field. Remember kids, use STRLCPY, not STRCPY unless you like people going out of bounds. Below shows an area that Identified as having an issue as it used STRCPY in the input from the user.
Once I figured that out, I grabbed the "Linux/MIPS - reboot() - 32 bytes." from exploit-db.com and compiled it. I then encoded the shell code into a payload and attached it to the "linksys_apply_cgi.rb" module in the Metasploit Framework. Once I ran the module, the router seemed to lock up, and give me no output what so ever.
With more time, I should be able to pin point an entry for this system. However this was what I needed to bring back to the $Client so that they could prove to the developers that it needed to be fixed. And it did get fixed, however now there is a cross site scripting error, as it copies all of the data sent in the buffer and re-displays that to the user. It is a win, but also a fail. It's been fixed, but who knows for how long as secure coding practices are not part of the overall development process yet.
- Train your developers on secure coding practices.
- Have the developers do what your source code review tool said to do.
- Disallow access from the users.
- Something I didn't touch on, but a routed network has much better controls then a bridged network.
- Assume that the system is compromised from the get go.
- Do code and security reviews often (that's all we do at SpiderLabs).
- Don't ever trust shared root passwords to be secure very long.
- BusyBox needs to implement strong security controls that are in other modern Linux distributions to restrict user processes.
JTAG Hardware Tools:
Flyswatter 2 Works well, has decent documentation, and has bus adapters.
BUS Pirate Works OK, but has weak documentation for JTAG. Works great with Arduino ISP stuff.
JTAG Software Tools:
OpenOCD The software you will need to talk to the Flyswatter or Bus Pirate. Works well, and will also integrate with Gnu Debugger.