Practice Hacking on Your Home Router
Although it's true that I tend to focus mostly on Linux in systems administration (after all, that is my day job), I've always had a secondary interest in security, whether it's hardening systems, performing forensics on a hacked system, getting root on a pico projector or even trying my hand at finding and exploiting vulnerabilities. Even though it's fun to set up your own Web services and attempt to exploit them, there's something more satisfying about finding vulnerabilities in someone else's code. The downside, of course, is that most Webmasters don't appreciate it when you break into their sites. However fun hacking is, at least for me, it isn't worth the risk of jail time, so I need to have my fun in more legal ways. This is where my wireless router comes in.
Wireless routers have a long history of being hackable. If you take any group of Linux geeks, you are bound to find a number of them who have, or have had, a member of the classic Linksys WRT series. If you look on-line, there are all sorts of custom firmware you can install to extend its functionality. Although it's true that on some versions of the router you have to jump through some crazy hoops to install custom firmware, it's still not the same kind of challenge as discovering and exploiting a vulnerability on a server. Although I have a stack of WRT54G routers, this article isn't about them; instead, it's about the D-Link DIR-685.
The D-Link DIR-685
I first became aware of the D-Link DIR-685 during a Woot-Off on woot.com. If you are familiar with Woot-Offs, you understand that when a new product shows up on the site, you have a limited time to decide whether you want to buy it before it disappears and a new product shows up. The moment I read the specs, I knew this router looked promising. First, it was an 802.11n router, and I was in the market to upgrade from my 802.11g network. Second, it had five different gigabit ports in the back along with two USB ports. Finally, as icing on the cake, it not only had this interesting-looking color LCD on the front that could show statistics, photos or other data, but you also could slot a 2.5" SATA drive up to 1Tb and turn the thing into a small NAS. Based on the fact that it required an ext3 filesystem on the 2.5" drive, I reasonably could assume it even already ran Linux. I didn't have much time to see if anyone already had hacked into the router or created custom firmware, so I made up my mind and clicked the order button.
While I was waiting for the router to ship to my house, I did some extra research. Although unfortunately it looked like there wasn't any custom firmware I could find (this originally was quite an expensive router, so I imagine it didn't have a large install base), I did find a site from someone who documented how to open up the router and wire up and connect a serial port to it, so you could access the local serial console. I decided that in the worst case, if I couldn't find a simpler method, I always could just go that route.
When I got the router, I did the initial setup on my network via the Web interface and then looked one last time for any custom firmware or other method apart from a serial console to get root on the router. I wasn't able to find anything, but before I went to the trouble of taking it apart, I decided to poke around on the Web interface and see if I saw anything obvious. The first dead end came when I enabled the FTP service via the Web interface, yet was not able to find any known vulnerabilities with that FTP server that I could exploit. Unlike when I got root on my pico projector, when I ran an nmap against the machine, I wasn't lucky enough to have telnet waiting for me:
PORT STATE SERVICE 21/tcp open ftp 80/tcp open http 139/tcp open netbios-ssn 445/tcp open microsoft-ds
One Ping Only
As I continued searching though, I got my first clue: the ping test. The
Web interface provides a complete set of diagnostic tools that, among other
things, allows you to ping remote machines to test for connectivity on
http://<router ip>/tools_vct.php (Figure 1). I figured there was a good
chance that the PHP script just forwarded the hostname or IP address you
typed in to a system call that ran ping, so I started by adding a semicolon
routine that sanitized the input, but what I noticed was that after I
submitted a valid input, the variable also showed up in the URL:
Figure 1. The Ping Test
It was at this point that I searched on-line for a nice complete table of all of the URL escape codes. You may have noticed that whenever you type a space in a URL, for instance, browsers these days tend to convert it into %20. That is just one of many different escape codes for symbols that are valid to have in a URL in their escaped form. Table 1 shows some of the more useful ones for what I was trying to achieve.
So, for instance, to perform a simple test of command injection, you might attempt to add a sleep command. If the page seems to pause for that amount of time before it reloads, that's a good sign your command injection worked. So, to attempt a sleep command with that page, my encoded URL to set pingIP to "127.0.0.1; sleep 30" looked like http://<router ip>/tools_vct.php?uptime=175036&pingIP=127.0.0.1%3B%20sleep%2030.
"If it's PHP, there will be a hole."
Figure 2. System Tools Page
Okay, so now I had a viable way to execute shell commands on the system. The next question was how I was going to take advantage of this to log in remotely. My first approach was to try to execute netcat, have it listen on a high port, and use the -e argument so that it would execute a shell once I connected to that port—a poor man's telnetd. After all, many consumer devices that run Linux use BusyBox for their shell, and BusyBox often includes a version of netcat that supports this option. Unfortunately, no combination of netcat arguments I tried seemed to do anything. I was starting to think that I didn't get a shell after all—that is, until I enclosed reboot in backticks, and it rebooted the router.
After the machine came back up, I decided it was possible netcat just wasn't installed, so it was then that I tried the fateful URL: http://<router ip>/sys_config_valid.xgi?exeshell=%60telnetd%20%26%60.
In case you don't want to look it up, that converts into
as input. Sure enough, after I ran that command, my nmap output looked a
PORT STATE SERVICE 21/tcp open ftp 23/tcp open telnet 80/tcp open http 139/tcp open netbios-ssn 445/tcp open microsoft-ds
Then, I fired up telnet from that same machine:
$ telnet <router ip> Trying <router ip>... Connected to <router ip>. Escape character is '^]'. BusyBox v1.00 (2009.07.27-14:12+0000) Built-in shell (msh) Enter 'help' for a list of built-in commands. #
I not only got a shell, but also a root shell! When I ran a
ps command, I
noticed my telnetd process on the command line:
sh -c `telnetd &` > /dev/console
It turns out any command you pass to exeshell just gets passed to
sh -c, so
I didn't need any fancy escaped backticks or ampersands,
would work just fine.