What the Ridiculous Fuck, D-Link?!

As mentioned in an update to my post on the HNAP bug in the DIR-890L, the same bug was reported earlier this year in the DIR-645, and a patch was released. D-Link has now released a patch for the DIR-890L as well.

The patches for both the DIR-645 and DIR-890L are identical, so I’ll only examine the DIR-890L here.

Although I focused on command injection in my previous post, this patch addresses multiple security bugs, all of which stem from the use of strstr to validate the HNAP SOAPAction header:

  1. Use of unauthenticated user data in a call to system (command injection)
  2. Use of unauthenticated user data in a call to sprintf (stack overflow)
  3. Unauthenticated users can execute privileged HNAP actions (such as changing the admin password)

Remember, D-Link has acknowledged all of the above in their security advisories, and thus were clearly aware of all these attack vectors.

Continue reading

Hacking the D-Link DIR-890L

The past 6 months have been incredibly busy, and I haven’t been keeping up with D-Link’s latest shenanigans. In need of some entertainment, I went to their web page today and was greeted by this atrocity:

D-Link's $300 DIR-890L router

D-Link’s $300 DIR-890L router

I think the most “insane” thing about this router is that it’s running the same buggy firmware that D-Link has been cramming in their routers for years…and the hits just keep on coming.

Continue reading

Reversing Belkin’s WPS Pin Algorithm

After finding D-Link’s WPS algorithm, I was curious to see which vendors might have similar algorithms, so I grabbed some Belkin firmware and started dissecting it. This particular firmware uses the SuperTask! RTOS, and in fact uses the same firmware obfuscation as seen previously on the Linksys WRT120N:

0             0x0             Obfuscated Arcadyan firmware, signature bytes: 0x12010920, see https://github.com/devttys0/wrt120n/deobfuscator
666624        0xA2C00         LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 454656 bytes

Being a known obfuscation method, binwalk was able to de-obfuscate and extract the compressed firmware image. The next step was to figure out the code’s load address in order to get a proper disassembly in IDA; if the code is disassembled with the wrong load address, absolute memory references won’t be properly resolved.

Continue reading

Reversing D-Link’s WPS Pin Algorithm

While perusing the latest firmware for D-Link’s DIR-810L 80211ac router, I found an interesting bit of code in sbin/ncc, a binary which provides back-end services used by many other processes on the device, including the HTTP and UPnP servers:

Call to sub_4D56F8 from getWPSPinCode

Call to sub_4D56F8 from getWPSPinCode

I first began examining this particular piece of code with the hopes of controlling part of the format string that is passed to __system. However, this data proved not to be user controllable, as the value placed in the format string is the default WPS pin for the router.

Continue reading

A Code Signature Plugin for IDA

When reversing embedded code, it is often the case that completely different devices are built around a common code base, either due to code re-use by the vendor, or through the use of third-party software; this is especially true of devices running the same Real Time Operating System.

For example, I have two different routers, manufactured by two different vendors, and released about four years apart. Both devices run VxWorks, but the firmware for the older device included a symbol table, making it trivial to identify most of the original function names:

VxWorks Symbol Table

VxWorks Symbol Table

The older device with the symbol table is running VxWorks 5.5, while the newer device (with no symbol table) runs VxWorks 5.5.1, so they are pretty close in terms of their OS version. However, even simple functions contain a very different sequence of instructions when compared between the two firmwares:

strcpy from the VxWorks 5.5 firmware

strcpy from the VxWorks 5.5 firmware

strcpy from the VxWorks 5.5.1 firmware

strcpy from the VxWorks 5.5.1 firmware

Of course, binary variations can be the result of any number of things, including differences in the compiler version and changes to the build options.

Despite this, it would still be quite useful to take the known symbol names from the older device, particularly those of standard and common subroutines, and apply them to the newer device in order to facilitate the reversing of higher level functionality.

Continue reading

Hacking the D-Link DSP-W215 Smart Plug

The D-Link DSP-W215 Smart Plug is a wireless home automation device for monitoring and controlling electrical outlets. It isn’t readily available from Amazon or Best Buy yet, but the firmware is up on D-Link’s web site.

The D-Link DSP-W215

The D-Link DSP-W215

TL;DR, the DSP-W215 contains an unauthenticated stack overflow that can be exploited to take complete control of the device, and anything connected to its AC outlet.

Continue reading

Cracking Linksys “Encryption”

Perusing the release notes for the latest Linksys WRT120N firmware, one of the more interesting comments reads:

Firmware 1.0.07 (Build 01)
– Encrypts the configuration file.

Having previously reversed their firmware obfuscation and patched their code to re-enable JTAG debugging, I thought that surely I would be able to use this access to reverse the new encryption algorithm used to secure their backup configuration files.

Boy was I giving them way too much credit.

Here’s a diff of two backup configuration files from the WRT120N. The only change made between backups was that the administrator password was changed from “admin” in backup_config_1.bin to “aa” in backup_config_2.bin:

OFFSET        backup_config_1.bin              backup_config_2.bin
0x00001468    9E 9B 92 96 91 FF FF FF |........| / 9E 9E FF FF FF FF FF FF |........|

Two things to note here:

  • The first letter of each password (“a”) is encrypted to the same value (0x9E)
  • The same letter (“a”) is encrypted to the same value (0x9E), regardless of its position in the password

I immediately suspected some sort of simple single-byte XOR encryption. If true, then XORing the known plain text (“a”, aka, 0x61) with the known cipher text (0x9E) should produce the XOR key:

0x61 ^ 0x9E = 0xFF

Applying the XOR key of 0xFF to the other characters in the password gives us:

0x9E ^ 0xFF = a
0x9B ^ 0xFF = d
0x92 ^ 0xFF = m
0x96 ^ 0xFF = i
0x91 ^ 0xFF = n

And XORing every byte in the config file with 0xFF gives us a decrypted config file:

00000000  33 34 35 36 00 01 df 60  00 00 46 ec 76 31 2e 30  |3456...`..F.v1.0|
00000010  2e 30 37 00 00 00 00 00  00 00 00 00 00 00 00 00  |.07.............|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 57 52 54 31  |............WRT1|
00000030  32 30 4e 00 00 00 00 00  00 00 00 00 00 00 00 00  |20N.............|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000080  61 64 6d 69 6e 00 00 00  00 00 00 00 00 00 00 00  |admin...........|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000a0  00 00 00 00 00 00 00 00  61 64 6d 69 6e 00 00 00  |........admin...|
000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000100  00 00 00 00 00 00 00 00  30 2e 30 2e 30 2e 30 00  |........|
00000110  00 00 00 00 00 00 00 00  01 01 01 00 00 00 00 01  |................|
00000120  00 00 00 01 00 00 00 00  00 00 00 08 32 39 34 38  |............2948|
00000130  33 31 30 35 00 01 00 00  00 31 39 32 2e 31 36 38  |3105.....192.168|
00000140  2e 31 2e 31 00 00 00 00  00 32 35 35 2e 32 35 35  |.1.1.....255.255|
00000150  2e 32 35 35 2e 30 00 00  00 00 00 00 04 00 02 00  |.255.0..........|
00000160  01 00 00 00 00 00 00 00  00 00 00 00 00 00 4c 4f  |..............LO|
00000170  4f 50 42 41 43 4b 00 00  00 00 31 32 37 2e 30 2e  |OPBACK....127.0.|
00000180  30 2e 31 00 00 00 00 00  00 00 32 35 35 2e 32 35  |0.1.......255.25|
00000190  35 2e 32 35 35 2e 32 35  35 00 00 00 00 00 00 00  |5.255.255.......|
000001a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001b0  00 00 00 00 49 52 51 3d  30 20 50 4f 52 54 3d 30  |....IRQ=0 PORT=0|
000001c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

This is truly atrocious. Given that “encrypting” the backup configuration files is done presumably to protect end users, expecting this to thwart any attacker and touting it as a product feature is unforgivable.

OK, I don’t really care that much. I’m just disappointed that it took longer to write this blog post than it did to break their “crypto”.

Re-enabling JTAG and Debugging the WRT120N

After de-obfuscating the WRT120N’s firmware, I started taking a closer look at the code, which runs the now-defunct SuperTask! RTOS.

Thanks in no small part to copious debug strings littered throughout the code and some leaked Atheros datasheets, I made good progress in statically disassembling the code. The next step was to start debugging the system while exercising some of the router’s services.

The WRT120N does have a JTAG port (labeled J8), which appears to conform to the MIPS EJTAG standard header:

The WRT120N JTAG header

The WRT120N JTAG header

It didn’t work right out of the box though:

$ sudo openocd -f flyswatter2.cfg -f wrt120n.cfg 
Open On-Chip Debugger 0.7.0 (2014-01-05-12:41)
Licensed under GNU GPL v2
For bug reports, read
Info : only one transport option; autoselect 'jtag'
adapter speed: 6000 kHz
trst_and_srst separate srst_gates_jtag trst_push_pull srst_open_drain connect_deassert_srst
trst_and_srst separate srst_nogate trst_push_pull srst_open_drain connect_assert_srst
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
Info : max TCK change to: 30000 kHz
Info : clock speed 6000 kHz
Error: JTAG scan chain interrogation failed: all ones
Error: Check JTAG interface, timings, target power, etc.
Error: Trying to use configured scan chain anyway...
Error: mips.cpu: IR capture error; saw 0x1f not 0x01
Warn : Bypassing JTAG setup events due to errors
Error: Error writing unexpected address 0xffffffff
Error: Error writing unexpected address 0xffffffff
Error: Error writing unexpected address 0xffffffff
Error: Error writing unexpected address 0xffffffff

It turns out that JTAG has been disabled in hardware and in software on the WRT120N. Luckily both were relatively easy to fix.

Continue reading

Reversing the WRT120N’s Firmware Obfuscation

It was recently brought to my attention that the firmware updates for the Linksys WRT120N were employing some unknown obfuscation. I thought this sounded interesting and decided to take a look.

The latest firmware update for the WRT120N didn’t give me much to work with:

Binwalk firmware update analysis

Binwalk firmware update analysis

As you can see, there is a small LZMA compressed block of data; this turned out to just be the HTML files for the router’s web interface. The majority of the firmware image is unidentified and very random. With nothing else to go on, curiosity got the best of me and I ordered one (truly, Amazon Prime is not the best thing to ever happen to my bank account).

Continue reading


I’ve previously written some examples of how to exploit MIPS stack overflows using ROP techniques. The problem is that finding suitable MIPS ROP gadgets manually can be quite tedious, so I have added a new IDA plugin – mipsrop.py – to my github repository.

This plugin searches the code segment(s) of your IDB looking for potentially controllable jump instructions. You can then search the code surrounding these controllable jumps for useful instructions that you might need in your ROP chain.

“Controllable jumps” are defined as jumps whose destination addresses are loaded from the stack, or from other registers (typically during a stack overflow you control several, if not all, of the MIPS subroutine registers, for example).

The plugin’s searches are “dumb” in that they don’t follow code branches, but none-the-less it has proven to be quite effective. As a quick example, let’s look inside a Linux MIPS libc library for ROP gadgets that will let us call sleep(1) in order to force a cache flush.

Continue reading