Hacking the DSP-W215, Again, Again, Again

So far, thevulnerabilitiesfound in the DSP-W215 have only been practically exploitable from the LAN, unless someone was foolish enough to make their smart plug remotely accessible on the Internet.

The typical way for external attackers to target internal web servers, such as the one running on the DSP-W215, is through CSRF. The problem is that any web browser used for a CSRF attack will URL encode binary values, such as our return addresses, but thus far the vulnerabilities we’ve exploited don’t URL decode our data (note that the replace_special_char function exploited in the last vulnerability only URL decodes a small range of ASCII values).

The my_cgi.cgi binary, which has been our primary target for exploitation, contains a decode function which is responsible for URL decoding POST data. This function accepts only two arguments, which are a pointer to the encoded data and a pointer to a destination buffer to store the decoded data:

void decode(char *encode_buf, char *decode_buf);

The decode function simply loops through all of the bytes in encode_buf, decoding/copying them blindly into decode_buf:

If a calling function is not careful to allocate a large enough buffer to store all the decoded data, the decode_buf could be overflowed by a large POST parameter.

There is only one place in my_cgi.cgi where the decode function is called, which is from the get_input_entries function:

Only the “path” POST parameter is decoded

We can see that the decode function is only called if the POST parameter name is “path”, and from the memset we can infer that the decode_buf passed to the decode function is only a 0x400 byte stack buffer:

This means that providing a POST “path” value greater than 0x400 bytes will overflow the decode_buf stack variable in the get_input_entries function. What’s more, we have no bad bytes, because the decode function will helpfully URL decode any offending bytes (NULL bytes become “%00” in our POST request, for example) before copying them to the stack.

However, we have to take care in crafting our exploit buffer such that we don’t trigger the previously described stack overflow in the replace_special_char function, which is called before get_input_entries returns.

Luckily, the data passed to replace_special_char is actually strcpy’d from decode_buf first. If we put a NULL byte near the beginning of our POST data, replace_special_char will only be passed a very small string (everything up to the first NULL byte) instead of the entire POST data that has been decoded onto the stack.

A “path” POST value greater than 1060 bytes will overflow everything in the get_input_entries stack frame up to the saved return address:

The get_input_entries stack layout

And, since we have no bad bytes, we can use the return address of 0x00405CEC that was used in previousexploits in order to call system() with a pointer to the stack ($sp+0x28):

system() call at 0x00405CEC

Here’s some PoC code in Python that overflows the get_input_entries saved return address with the address of the call to system() at 0x00405CEC and puts a command to execute on the stack at $sp+0x28:

10 Responses to Hacking the DSP-W215, Again, Again, Again

Hello. I recently started reading your blog, and I must say I’m very impressed by your skills. 🙂

Perhaps this is a bit off-topic, but I was wondering if you would be interested in looking into hacking printers. According to the Electronic Frontier Foundation, many color printers secretly add microscopic dots to the document that can be used to identify the user: https://www.eff.org/issues/printers

The EFF has been trying to decode the dots but has not had much success:

Craig, Great article.
Is it possible from now on, you put all the decoded firmwares of your blog posts in your github?
Then it is easier to reanalyse the same codes for researchers, because personally I sometimes stuck in some of your blog posts because of decoding problems I have with firmwares.
That would be very very helpful.

I just bought a new DSP-W215/E which came with firmware 2.02 and a quick peek at web_cgi.cgi with IDA got me nowhere.
To be honest, I’m a newbie to IDA so I’m sure I’ve overlooked something.
Any chance I could tempt you into having another look at this new version?