/dev/mtd1
flash partition. Considering this a win, we'll now circle back to analyzing upnpd
.In this and the next part, we'll compare the way
upnpd
parses and validates the firmware header to that of httpd
. Having developed a baseline understanding of how the header is parsed by httpd
, analyzing upnpd
is much easier.Updated Exploit Code
As in previous installments, the exploit code has been updated. Since we're switching back toupnpd
in order to analyze how it validates the firmware, the repository contains separate modules for that. Look for janky_ambit_header.py
and build_janky_fw.py
. You can find the updated code and README in the part_8
directory. Now is a good time to do a pull or to clone the repository from:https://github.com/zcutlip/broken_abandoned
More Firmware Parsing, Pretty Much Like Before
As we discovered in part 4, a firmware larger than 4MB will crashupnpd
due to an undersized memory allocation. Obviously we won't be able to strap a header to the front of a stock TRX image like we did with httpd
; it's way too big. Shrinking the firmware will be a challenge for later. If it turns out that we can't even get so far as writing the firmware to flash memory without crashing, it won't matter that you were able to shrink and re-pack the firmware. Instead, just dd out a little less than 4MB of random data from /dev/random
and prepend a header to it. If you can get upnpd
to write that image to flash, you win this stage and may advance to the next level.Once we get past the undersized
malloc()
at 0x00423C24 in sa_parseRcvCmd()
, the firmware is successfully base64 decoded out of the SOAP request. Then, at 0x00423C98, a function named sa_CheckBoardID()
is called.This function should be familiar. It's nearly identical to the
abCheckBoardID()
function I described in part 5. So identical, in fact, that the buffer overflow via memcpy()
I described previously is in this function as well.Buffer overflow due to memcpy() using header size field. Sad trombone. |
Even the Buffer Overflow is the Same
To recap, the
memcpy()
is bounded only by the size value from the header. Since we control that value, we get precise control over how many bytes are copied into the destination buffer.I didn't go into detail about the buffer overflow before, because I wanted to wait until I could discuss it in the context of
upnpd
. In the HTTP server, this isn't an interesting vulnerability. In that case, it is a post-authentication vulnerability. You would need to bypass authentication or trick a user into uploading your malicious firmware. If you've accomplished either of those, there are much more useful things you can be doing with your time than exploiting buffer overflows.In the case of
upnpd
, this same vulnerability doesn't require authentication, making it much more interesting. Here's what's neat about it:- No authentication required.
- The payload is base64 encoded and decoded for free, so there are no bad bytes to avoid related to the transport protocol.
- The buffer overflow is via
memcpy()
rather than a string handling function. There are no bad bytes to avoid related to string handling. - The buffer being overflowed is on the stack, making it easy to overwrite the function's return address.
However, as I said in the first part of this series, one of my self-imposed goals was to avoid exploiting bugs along the way. We're trying to flash a firmware without crashing, and any bugs along the way are obstacles to overcome.
Working through this function reveals the same header fields that we discovered in its
httpd
counterpart: The magic number, the size and checksum of the header, and the board ID string. These fields are found at the same header offsets as before.Mystery Header Gets a Name
There is one new piece of information, however.At 0x00423088 there is an error message that we didn't see in
httpd
: "Not Ambit image ... reject!!!". This is the first indication of any sort of name for this file format. This explains why you may have noticed references to "ambit" or "ambit header" in previous code fragments I've posted.In the next part, we get close to writing the firmware image to flash memory. We'll have to do some binary patching to work around the fact that QEMU doesn't actually have flash memory.