Saturday, January 03, 2015

Remote Debugging with QEMU and IDA Pro

It's often the case, when analyzing an embedded device's firmware, that static analysis isn't enough. You need to actually execute a binary you're analyzing in order to see how it behaves. In the world of embedded Linux devices, it's often fairly easy to put a debugger on the target hardware for debugging. However it's a lot more convenient if you can run the binary right on your own system and not have to drag hardware around to do your analysis. Enter emulation with QEMU.

An upcoming series of posts will focus on reverse engineering the UPnP daemon for one of Netgear's more popular wireless routers. This post will describe how to run that daemon in system emulation so that it can analyzed in a debugger.

Prerequisites

First, I'd recommend reading the description I posted of my workspace and tools that I use. Here's a link.

You'll need an emulated MIPS Linux environment. For that, I'll refer readers to my previous post on setting up QEMU.

You'll also need a MIPS Linux cross compiler. I won't go into the details of setting this up because cross compilers are kind of a mess. Sometimes you need an older toolchain, and other times you need a newer toolchain. A good starting point is to build both big endian and little endian MIPS Linux toolchains using the uClibc buildroot project. In addition to that, whenever I find other cross compiling toolchains, I save them. A good source of older toolchains is the GPL release tarballs that vendors like D-Link and Netgear make available.

Once you have a cross compiling toolchain for your target architecture, you'll need to build GDB for that target. At the very least, you'll need gdbserver statically compiled for the target. If you want to remotely debug using GDB, you'll need gdb compiled to run on your local architecture (e.g., x86-64) and to debug your target architecture (e.g., mips or mipsel). Again, I won't go into building these tools, but if you have your toolchains set up, it shouldn't be too bad.

I use IDA Pro, so that's how I'll describe remote debugging. However,  if you want to use gdb check out my MIPS gdbinit file: https://github.com/zcutlip/gdbinit-mips

Emulating a Simple Binary

Assuming you've gotten the tools described above set up and working properly, you should now be able to SSH into your emulated MIPS system. As described in my Debian MIPS QEMU post, I like to bridge QEMU's interface to VMWare's NAT interface so I can SSH in from my Mac, without first shelling into my Ubuntu VM. This also allows me to mount my Mac's workspace right in the QEMU system via NFS. That way whether I'm working in the host environment, in Ubuntu, or in QEMU, I'm working with the same workspace.


zach@malastare:~ (130) $ ssh root@192.168.127.141
root@192.168.127.141's password:
Linux debian-mipsel 2.6.32-5-4kc-malta #1 Wed Jan 12 06:13:27 UTC 2011 mips

root@debian-mipsel:~# mount
/dev/sda1 on / type ext3 (rw,errors=remount-ro)
malastare:/Users/share/code on /root/code type nfs (rw,addr=192.168.127.1)
root@debian-mipsel:~# cd code
root@debian-mipsel:~/code#

Once shelled into your emulated system, cd into the extracted file system from your device's firmware. You should be able to chroot into the firmware's root file system. You need to use chroot since the target binary is linked against the firmware's libraries and likely won't work with Debian's shared libraries.


root@debian-mipsel:~# cd code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs/
root@debian-mipsel:~/code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs# file ./bin/ls
./bin/ls: symbolic link to `busybox'
root@debian-mipsel:~/code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs# file ./bin/busybox
./bin/busybox: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked (uses shared libs), stripped
root@debian-mipsel:~/code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs# chroot . /bin/ls -l /bin/busybox
-rwxr-xr-x    1 10001    80         276413 Sep 20  2012 /bin/busybox
root@debian-mipsel:~/code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs#

In the above example, I have changed into the root directory of the extracted file system. Then using the file command I show that busybox is a little endian MIPS executable. Then I chrooted into the extracted root directory and ran bin/ls, which is a symlink to busybox.

If you attempt to simply start a chrooted shell with "chroot .", it won't work. Your user's default shell is bash, and most embedded devices don't have bash.


root@debian-mipsel:~/code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs# chroot .
chroot: failed to run command `/bin/bash': No such file or directory
root@debian-mipsel:~/code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs#

Instead you can chroot and execute bin/sh:

root@debian-mipsel:~/code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs# chroot . /bin/sh


BusyBox v1.7.2 (2012-09-20 10:26:08 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

#
#
# exit
root@debian-mipsel:~/code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs#

Hardware Workarounds

Even with the necessary tools and emulation environment set up and working properly, you can still run into roadblocks. Although QEMU does a pretty good job of emulating the core chipset, including the CPU, there is often hardware the binary you're trying to run is expecting that QEMU can't provide. If you try to emulate something simple like /bin/ls, that will usually work fine. But something more complicated such as the UPnP daemon will almost certainly have particular hardware dependencies that QEMU isn't going to satisfy. This is especially true for programs whose job it is to manage the embedded system's hardware, such as turning wireless adapters on or off.

The most common problem you will run into when running system services such as the web server or UPnP daemon is the lack of NVRAM. Non-volatile RAM is usually a partition of the device's flash storage that contains configuration parameters. When a daemon starts up, it will usually attempt to query NVRAM for its run-time configuration. Sometimes a daemon will query NVRAM for tens or even hundreds of parameters.

To work around the lack of NVRAM in emulation, I wrote a library called nvram-faker. The nvram-faker library should be preloaded using LD_PRELOAD when you run your binary. It will intercept calls to nvram_get(), normally provided by libnvram.so. Rather than attempting to query NVRAM, nvram-faker will query an INI-style configuration file that you provide.

The included README provides a more complete description. Here's a link to the project:

Even with NVRAM solved, the program may make assumptions about what hardware is present. If that hardware isn't present, the program may not run or, if it does run, it may behave differently than it would on the target hardware. In this case, you may need to patch the binary. The specifics of binary patching vary from one situation to another. It really depends on what hardware is expected, and what the behavior is when it is absent. You may need to patch out a conditional branch that is taken if hardware is missing. You may need to patch out an ioctl() to a special device if you're trying to substitute a regular file for reading and writing. I won't cover patching in detail here, but I did discuss it briefly in my BT HomeHub paper and the corresponding talk I gave at 44CON. Here is a link to those resources:
http://shadow-file.blogspot.com/2013/09/44con-resources.html


Attaching the Debugger

Once you've got your binary running in QEMU, it's time to attach a debugger. For this, you'll need gdbserver. Again, this tool should be statically compiled for your target architecture because you'll be running it in a chroot. You'll need to copy it into the root directory of the extracted filesystem.


# ./gdbserver
Usage: gdbserver [OPTIONS] COMM PROG [ARGS ...]
 gdbserver [OPTIONS] --attach COMM PID
 gdbserver [OPTIONS] --multi COMM

COMM may either be a tty device (for serial debugging), or
HOST:PORT to listen for a TCP connection.

Options:
  --debug               Enable general debugging output.
  --remote-debug        Enable remote protocol debugging output.
  --version             Display version information and exit.
  --wrapper WRAPPER --  Run WRAPPER to start new programs.
  --once                Exit after the first connection has closed.
#

You can either attach gdbserver to a running process, or use it to execute your binary directly. If you need to debug initialization routines that only happen once, you'll want to do the latter.

On the other hand, you may want to wait until the daemon forks. As far as I know there's no way to have IDA follow forked processes. You need to attach to them separately. If you do it this way, you can attach to the already running process from outside the chroot.

The following shell script will execute upnpd in a chroot. If DEBUG is set to 1, it will attach to upnpd and pause for a remote debugging session on port 1234.



#!/bin/sh
ROOTFS="/root/code/wifi-reversing/netgear/r6200/extracted-1.0.0.28/rootfs"
chroot $ROOTFS /bin/sh -c "LD_PRELOAD=/libnvram-faker.so /usr/sbin/upnpd"

#Give upnpd a bit to initialize and fork into the background.
sleep 3;

if [ "x1" = "x$DEBUG" ];
then

 $ROOTFS/gdbserver --attach 0.0.0.0:1234 $(pgrep upnpd)
fi

You can create a breakpoint right before the call to recvfrom() and then verify the debugger breaks when you send upnpd an M-SEARCH packet.

break before recvfrom()


Then, in IDA, go to Process Options under the Debugger menu. Set "hostname" to the IP address of your QEMU system, and set the port to the port you have gdbserver listening on. I use 1234.

Debug application setup: gdb


Accept the settings, then attach to the remote debugging session with IDA's ctrl+8 hotkey. Hit ctrl+8 again to resume execution. You should be able to send an M-SEARCH packet[1] and see the debugger hit the breakpoint.

debugger hits breakpoint in upnp_main()

There is obviously a lot more to explore, and there are lots of situations that can come up that aren't addressed here, but hopefully this gets you started.

[1] I recommend Craig Heffner's miranda tool for UPnP analysis:
https://code.google.com/p/miranda-upnp/

Tuesday, September 23, 2014

Exploit Tunneling and Callback

A few years ago, when I worked for my previous employer, I put together a proof-of-concept that was to be part of a client demo. I thought it was kind of cool, so I recorded a screencast of it in action. I've had the video sitting on my laptop ever since, not really sure what to do with it. I finally decided to post it.

In the video, what you see is a custom exploit script that exploits a buffer overflow in the web interfaces of several D-Link webcams. The neat part is that it tunnels the exploit and callback through each successive webcam. It works basically like this:

  1. Exploit webcam 1.
  2. Webcam 1 phones home, then downloads and executes a two-stage payload.
  3. The payload proxies all packets destined to a certain port between the exploit host and the next webcam in the chain. It does this in both directions.
  4. Tunneling through webcam 1, exploit webcam 2.
  5. Like before, webcam 2 phones home, tunneled through webcam 1, and downloads and executes a two-stage payload.
  6. Again, the payload proxies packets, this time between webcam 1 and the thrid webcam.
  7. Exploit webcam 3, tunneling through webcams 1 and 2.
  8. Webcam 3 executes a traditional payload, resulting in a connect-back shell. The shell connects back through webcams 2 and 1 to the exploit host.
  9. Root prompt on webcam 3.
I put this together pre-Bowcaster, so it's a little raw. But it was all groundwork for that framework, so Bowcaster has much of the same capability shown in the video.

As an aside, one of the nice things about exploiting these inexpensive consumer devices, besides the fact they are soft targets, usually unmonitored, and ubiquitous, is they generally don't have a writeable file system. This means you can reboot them, leaving behind no trace that you were ever there.

Anyway, here's the video. It has cool music.

Friday, May 16, 2014

Infiltrate 2014

Here are some additional resources I may have mentioned in my Infiltrate 2014 presentation.

White Paper: SQL Injection to MIPS Overflows - Part Deux
Slides: SQL Injection to MIPS Overflows - Part Deux

Original white paper from Black Hat USA 2012:
SQL Injections to MIPS Overflows: Rooting SOHO Routers

Proof of Concept Exploit code:
Here's my Github repository for proof-of-concept exploit code.  In it, you'll find the exploit code for the Netgear WNDR 3700v3 that I demoed at Infiltrate, among a few others. The white paper is in there as well.
https://github.com/zcutlip/exploit-poc

Bowcaster:
I talked about my Python API/Framework for developing buffer overflows. In particular it includes payloads for MIPS Linux.
https://github.com/zcutlip/bowcaster

Monday, December 30, 2013

Emulating and Debugging Workspace

A grad student emailed me in response to my Netgear auth bypass post.  He's working on a research project and wanted to know if I knew of any resources or techniques to use emulation for executing and debugging the net-cgi binary in the Netgear firmware.  It turns out I've got all the resources to do just that.  I replied with a description of my workspace and some links to resources I use, and, in many cases, have developed.  I thought this might make an interesting blog post, but I don't really have time to write it up all blog-post-like.  Instead I'll just paste in my email.  Maybe it'll be useful to other people as well.

Hello,

I think the best approach is to describe how I set up my tool chain and environment.  Hopefully that will be helpful for you.

To start with, I do my work in an Ubuntu VM.  Specifically 12.04.  I don't think the exact release matters, but I know 12.04 works with my tools.

I keep a set of cross compilers in my path for various architectures. In my opinion, building with a cross compiler is faster and easier than building with gcc inside QEMU.  I recommend building a set of cross-compiling toolchains using Buildroot.  Buildroot uses a Linux Kernel-style menuconfig build system.  I don't have anything written up on building cross compilers, but I could probably send you my buildroot configuration if you need it, and if I can find it.

You can download the firmware for the router from Netgear's support website.
Here's a link to the firmware:
http://support.netgear.com/product/wndr3700v4
In order to unpack the firmware, I recommend my colleague, Craig Heffner's tool, Binwalk:
https://code.google.com/p/binwalk/
Binwalk will analyze a binary file and describe the subcomponents it finds within, such as filesystems, compressed kernel, etc. Additionally, it can unpack the subcomponents it finds, assuming it knows how.
Install binwalk in your Ubuntu environment using the "debian_quick_install.sh" installation script, which will apt-get install a number of dependencies.
Rather than describe binwalk's usage, I'll refer you to the wiki:
https://code.google.com/p/binwalk/wiki/Usage?tm=6
Also, in your Ubuntu environment you'll need a Debian MIPS QEMU system that you can use to emulate the firmware's binaries.

I found lots of information about running Debian in QEMU, but most of it was incomplete, and a lot of it was inconsistent, so I've written a blog post describing how I set up my QEMU systems:
http://shadow-file.blogspot.com/2013/05/running-debian-mips-linux-in-qemu.html
This is just personal, but I like to export my workspace to the QEMU machines via NFS.  In fact, I export my workspace from my Mac via NFS, and my Ubuntu VMs and Debian QEMU VMs all mount the same directory. That way I'm not having to copy firmware, scripts and debuggers around.

Once logged into your QEMU VM, you can chroot into the router's firmware and run some of its binaries:

firmware_rootfs # chroot . /bin/sh
#

The simple ones, such as busybox, will run with no problem.  The web server, upnp server, etc. are more complicated because they make a lot of assumptions about the router's specific hardware being present.

One of the problems you run into has to do with queries to NVRAM for runtime configuration.  Obviously, your Debian MIPS Linux has no NVRAM, so these queries will fail.  For that, I have a project called "nvram-faker":
https://github.com/zcutlip/nvram-faker
You build the library for your target and preload it using the LD_PRELOAD environment variable.  It intercepts calls to nvram_get and provides answers based on the contents an nvram.ini file that you provide. It prints all the nvram queries to stdout, and colorizes the ones that it couldn't find in the .ini file.  Obviously it takes some guesswork to provide sane configuration parameters.

Sometimes you can skip running the web server and just run the cgi binaries from a shell script.  Most cgi binaries take their input from the web server as a combination of standard input and environment variables.  They send their response to the web server over standard output.

I hope this helps.  Let me know if I can help any other way.

Zach 

Saturday, December 07, 2013

BayThreat 2013 Presentation - Additional Resources

For my presentation at BayThreat, entitled "BT Wireless Routers: Adventures in Reversing and Exploiting", rather than have one or two or three slides packed with hard to read URLs, I included a single slide with a link to this post.  Here you'll find links to additional resources that I may have referenced in my talk.

White paper: Reverse Engineering and Exploiting the BT HomeHub 3.0b (pdf)
Slides: BT Wireless Routers: Adventures in Reversing and Exploiting

BT HomeHub 3.0b specifications
Here's a walkthrough I wrote on getting Debian MIPS Linux up and running in QEMU system emulation.  I use QEMU & Debian Linux to run and analyze binaries that I find in firmware.
QEMU/Debian MIPS Linux walkthrough

Often binaries found in firmware won't play nicely in emulation because they make a lot of assumptions about the underlying hardware which QEMU can't satisfy.  The most common case of this is an application querying NVRAM for configuration parameters.  Here's a library I wrote to intercept those queries and provide answers from an INI-style configuration file.
NVRAM "faker" library for use in emulation

Bowcaster is an exploit development API that I wrote to ease development of buffer overflow exploits.  It grew out of all the tools and techniques Craig Heffner and I developed for exploiting embedded devices.  It primarily targets MIPS Linux, since there support for that architecture was almost non-existent.  I plan to add support for other architectures as I have time.
Bowcaster

Here's my Github repository for proof-of-concept exploit code.  In it, you'll find the exploit code for the BT HomeHub 3.0b that I demoed at BayThreat, among a few others.
Proof-of-Concept exploit code

In the presentation I mentioned how exploiting buffer overflows on MIPS Linux is a bit different that other, more familiar architectures.  I wasn't able to go into details; that could make an entire presentation in itself.  However, I mentioned my Black Hat USA 2012 presentation, where I did describe some of the mechanics of exploiting MIPS Linux buffer overflows.  Here's the video of that presentation, entitled "From SQL Injection to MIPS Overflows: Rooting SOHO Routers".


SQL Injection to MIPS Overflows - Zachary Cutlip - Black Hat USA 2012 from Zach on Vimeo.

I hope these resources are useful.  If you came to this article because you saw my BayThreat talk and demo, I hope you enjoyed it!  Be sure to get in touch and share your thoughts!  Twitter or my email are best.

Twitter: @zcutlip
Email: uid000 at gmail

Cheers!
Zach

Thursday, October 24, 2013

Netgear Root Compromise via Command Injection

At the end of my post on the Netgear wndr3700v4's authentication bugs, I said to expect followup posts. Once the web interface is unlocked, any further bugs that normally require authentication become fair game. Well good news, everyone!!

Previously, I talked about the net-cgi executable in the wndr3700's firmware. ;net-cgi is a multi-call binary, a little like busybox. As such it has a lot of functionality baked in. One of its more interesting functions is called cmd_ping6(). Here's what it looks like:



This is a function that will ping whatever hostname or IPv6 address is passed in as the char *host argument. What you see here is such an unbelievably common pattern, that the first thing you should do with a router's firmware is check to see if there's a ping diagnostic page, and verify how ping gets executed.

What is happening here, as it so often does, is the host string gets copied into a shell command on the stack using sprintf(). This is probably the most straightforward buffer overflow vulnerability you will ever see. Sadly, you shouldn't exploit it. It is a tempting one to exploit because it is so clean and simple and because popping root with a MIPS ROP payload is sexy. But that would be silly, because right after it there is a call to system(). The system() function passes whatever string it is given to an invocation of /bin/sh. This is a command injection vulnerability in its purest form and is trivially exploitable. If the address string that gets passed in is something like "; evil_command; #", the ping6 command will be terminated prematurely, and evil_command will be executed right after it.

But what if the target isn't configured to use IPv6? Who cares? The ping6 command doesn't actually need to succeed. As long as the injected command string gets passed to system(), that's all that matters.

So how does this function get invoked?

Working backwards, cmd_ping6() gets called by cgi_commit().



The cgi_commit() function gets called by sub_4052d0(), which is the output function for the apply.cgi mime handler (I explained previously how the mime handler table works).



How does cgi_commit() know to call cmd_ping6()? That happens when when apply.cgi is requested as a post, and the post data contains "submit_flag=ping6".

sub_43a60() cgi handler gets called when submit_flag is "ping6".

There is a page that sends a post request to apply.cgi with the ping6 submit flag. That page is "ping6_traceroute6_hidden_info.htm".



This page would normally be protected behind password authentication. With the authentication bypass I wrote about previously, this page becomes accessible to anyone. As you can see, this is a form that allows you to ping an IP address or hostname. Rather than submit an IP address, you can send a shell command, such as `reboot`.

Send a shell command instead of an IP address.


Form is submitted with "ping6_text=`reboot`"

This is an easy test because the effect is immediate and easily observed, and you have very little shell syntax to troubleshoot.

This page is not exposed to the user via any reference in the web interface, and it even has "hidden" in the name. Hidden as it may be, it can't hide from Python. I've written up some proof-of-concept exploit code that will access this page and start up a telnet server, allowing you to log in unauthenticated as root.

"Hidden" pages can't hide from Python! Authentication is disabled, then command injection exploited gain root.

The exploit code does the following:
  1. Fingerprint the device to ensure it's vulnerable.
  2. Disable authentication.
  3. Inject a command to open a hole in iptables, and start a telnet server listening on the internet on port 2323[1].
  4. Re-enable authentication, restoring the device to its original state.
You can download the proof-of-concept exploit code[2] from my GitHub repo. You'll need Bowcaster installed.

[1] Netgear routers usually already have a telnet server listening on the LAN on port 23 that accepts a hardcoded backdoor password.
[2] Don't attempt to test this against devices you don't own. That's illegal in most jurisdictions.

Tuesday, October 22, 2013

Complete, Persistent Compromise of Netgear Wireless Routers

UPDATE: Turns out, Jacob Holocomb (@rootHak42 on Twitter) of Independent Security Evaluators found this bug back in April on a different device, the WNDR4700. Thanks for letting me know, Jacob. Nice find. Here's a link to that report.

UPDATE 2: Because there are almost certainly fools who would go hack somebody's router and say I told them to do it, I added a warning to not do this. DON'T DO IT.

UPDATE 3: I have to confess I tested this on an older firmware, 1.0.1.32, and neglected to test on the latest, 1.0.1.42. I did some cursory static analysis on .42, and satisfied myself that the vulnerabilities discussed still existed. Since Netgear has patched this on other devices, I became concerned that I should have tested more thoroughly, so I did that this morning. I can now say, with confidence, that these vulnerabilities apply equally to the latest wndr3700v4 firmware, 1.0.1.42.

UPDATE 4: I want to give Craig Young of Tripwire VERT credit for finding all these bugs and more. I found the ones below in June, and I believe Craig found them before me. Craig also is responsible for the Netgear ReadyNAS finding which has gotten a lot of coverage lately.

One of my favorite embedded vendors' products to find bugs in is Netgear. Naturally, I was excited to take a look at the firmware for version 4 of Netgear's venerable WNDR3700 wireless router (I talked about version 3 at Black Hat in 2012). Of course, I updated my DLNA SQL injection + buffer overflow exploit code for the new version, but I found something else that's even better.

Don't have time for a bunch of IDA Pro nonsense?  Don't worry; just skip to the TL;DR.

Still here?  Excellent. Let's find out how deep the rabbit hole goes.

An All-purpose CGI Request Handler


On the WNDR3700v4, as with many embedded web servers, a single binary executable, /usr/sbin/net-cgi, gets executed by the web server to handle most, if not all, HTTP requests.


$ file usr/sbin/net-cgi
usr/sbin/net-cgi: ELF 32-bit MSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked (uses shared libs), corrupted section header size

It's important to understand how net-cgi performs authentication. It's a little messy, but don't panic. I brought pictures.

In the executable's data section, there's a table of mime handler structures that describe, among other things, names or partial names of paths that can be requested via HTTP.

A C declaration for the mime handler structure might look approximately like:

struct mime_handler {
        char *pattern;
        char *mime_type;
        char *extra_header;
        void (*input)(char *path, FILE *stream, int len, char *boundary);
        void (*output)(char *path, FILE *stream);
        void (*auth)(char *userid, char *passwd, char *realm);
};

The main function for handling requests is handle_http_request(). Its function signature looks like:


handle_http_request(char *script_file, //requested object or cgi script
                      char *query_string,
                     char *request_method,
                     FILE * stdout,
                     FILE *stdin);


In http_handle_request(), there is logic to loop over each of the mime handlers to check if the requested object matches the handler's pattern string. It actually just does a strstr() to see if the pattern is a substring of the requested object.



If there's a match, then it checks to see if there's an authentication handler. If there is none for that mime handler, then no authentication is performed. Execution skips down the the block I call "pass go, collect $200."



When we look at the mime handler table, we see an entry for the pattern "BRS_". This entry's auth function pointer is NULL, meaning requested objects matching this pattern don't require authentication.


This is interesting because there are several BRS_ files available for the web server to serve up:


root/www (0) $ ls -1 BRS_*
BRS_01_checkNet.html*
BRS_01_checkNet_ping.html
BRS_02_genieHelp.html*
BRS_03A_A_noWan_check_net.html*
BRS_03A_A_noWan.html*
BRS_03A_B_pppoe.html*
BRS_03A_B_pppoe_reenter.html*
BRS_03A_C_pptp.html*
BRS_03A_C_pptp_reenter.html*
BRS_03A_D_bigpond.html*
BRS_03A_detcInetType.html*
BRS_03A_E_IP_problem.html*
BRS_03A_E_IP_problem_staticIP_A_inputIP.html*
BRS_03A_E_IP_problem_staticIP_B_macClone.html*
BRS_03A_E_IP_problem_staticIP_B_macClone_plzWait.html*
BRS_03A_E_IP_problem_staticIP.html*
BRS_03A_F_l2tp.html*
BRS_03A_F_l2tp_reenter.html*
BRS_03B_haveBackupFile_change_domain.htm
BRS_03B_haveBackupFile_fileRestore.html*
BRS_03B_haveBackupFile.html*
BRS_03B_haveBackupFile_ping.html
BRS_03B_haveBackupFile_wait_ping.html*
BRS_03B_haveBackupFile_waitReboot.html*
BRS_04_applySettings.html*
BRS_04_applySettings_ping.html*
BRS_04_applySettings_wget.html*
BRS_04_applySettings_wgetResult.html*
BRS_04_B_checkNet.html*
BRS_04_B_checkNet_ping.html
BRS_05_networkIssue.html*
BRS_check_manulConfig.html
BRS_index.htm*
BRS_netgear_success.html
BRS_ping.html*
BRS_ping_result.html
BRS_plzWait.html*
BRS_retry.htm
BRS_success.html*
BRS_top.html*
BRS_wanlan_conflict.html*

All of these files may be accessed without a password. Almost certainly there is something juicy in there, such as some diagnostic information, or maybe a page that will show you the WPA passphrases for the 2.4 GHz and 5GHz networks.

BRS_success.html. No authentication needed.

Excellent. Free wifi passwords!  Still though, full administrative pwnage would be cool. Good news....


Are You Really Sure We Need to Check Authentication?


When we look at how authentication is checked, there are a bunch of execution paths to the "pass go, collect $200" block. Lets take a look.

Lots of requested paths that result in authentication being skipped.

Above, you can see the first two strings, "unauth.cgi" and "securityquestions.cgi" are checked against the query string, not the requested file. If either of these strings are substrings of the query string, execution bypasses authentication. Since this substring check is against the entire query string, you can request something like http://router_address/protected_page.htm?foo=unauth.cgi. The "unauth.cgi" will match the substring check and execution will skip authentication.

I actually discovered this authentication bypass while double checking my research for the next vulnerability. It is the next one that is a much more powerful bypass and even more trivial to exploit. Plus, it's persistent.


So, Do We Really Super Double For Sure Need to Check Authentication?



After an authentication handler is located, but before the query string is checked for special unauthenticated strings, there is yet another interesting check. Let's have a look[1].

If hijack_process != "3", collect $200.

Above, we see a query of the NVRAM configuration for the hijack_process setting. If that setting is "3", then the authentication execution path is followed as normal. If it is something other than "3", execution skips down to "pass go, collect $200," authentication is bypassed. The purpose of this configuration setting is so that when first plugged in, the router will redirect all web requests to its own web-based administrative interface. In order to prevent users from having to know a default password, authentication is disabled until after the router is configured. The hijack_process setting is one among several that, together, determine whether the router is in an unconfigured state.

Where this gets interesting, however, is an unauthenticated page that will set the hijack_process setting to a value other than "3".

Grepping through the firmware's html files for "hijack_process" yields an interesting find.


$ grep -rn 'cfg_set("hijack_process"' *
BRS_02_genieHelp.html:12:<% cfg_set("hijack_process", "1") %>


The BRS_02_genieHelp.html file contains a command to set hijack_process to "1". The web interface uses a custom HTML templating language. Any text between '<%' and '%>'  is preprocessed by the web server and the entire directive replaced by the output of that processing. In the case above, the request handler processes the "cfg_set" directive to set the hijack_process configuration setting. As we discovered earlier, any web page beginning with "BRS_" does not require authentication. An unauthenticated request to BRS_02_genieHelp.html will have the effect of disabling authentication for the rest of the web interface.  Since the hijack_process setting is only one of several that mark the router as being unconfigured, this one setting alone has no noticeable effect for the user. No web requests are actually hijacked. Further, this setting is stored in NVRAM, which means it is persistent across reboots.

TL;DR


You skipped straight to the good stuff didn't you?  That's cool. Here's the deal. If you browse to http://<router address>/BRS_02_genieHelp.html, you are allowed to bypass authentication for all pages in the entire administrative interface. But not only that, authentication remains disabled across reboots. And, of course if remote administration is turned on, this works from the frickin' Internet.

Don't believe me?  Give it at try. Surf to your WNDR3700v4's web interface and request BRS_02_genieHelp.html. Don't have one of your own? No problem. Shodan's got you covered. (Just to be clear, don't go to Shodan and hack a router you don't own, okay? That's stupid, and it's not legal in most jurisdictions.)


With complete, persistent administrative access to the web interface, a huge attack surface is opened up. A malicious DNS server could be configured, exposing users to web browser exploits. Ports could be forwarded to devices on the LAN, exposing vulnerable services to attack. Or, a trojan horse firmware could be flashed onto the device that would give the attacker persistent root-level access to the router. Additionally, any command injection or buffer overflow vulnerabilities in the router's web interface become fair game once authentication is disabled.

In the next few posts, I will describe additional vulnerabilities that can be exploited once authentication is disabled on this device.

---------
[1] This is the the technical analysis of how this bug works. It's not how I found it. If you buy me a beer at a conference, I'll tell you how I actually found this vulnerability.