D-Link: A Firmware Vulnerability – Part 2

by | Aug 23, 2019

Exploit Identification

Turn Up the Technical

In part one of the Vulnerability assessment we talked about choosing a target, downloading the firmware, and submitting it to Centrifuge which was pretty simple.

Centrifuge provided a lot of useful information about the firmware, most importantly the potential flaws in the code analysis section.

In this post we will explore the technical side of identifying which of the flaws are potentially exploitable flaws, and which flaws are simply poor programming practices.

Starting the Search

The best place to begin the search: the administration server that runs at boot. The administration server will likely be exposed to the internet. Therefore, it’s our best starting point in my opinion.

If you can access to the actual camera, you can simply connect a UART cable, access the shell, and get a process listing to identify the server. But I prefer not to buy hardware until I have an actual vulnerability identified, and I actually have all the information I need at my fingertips from the unpacked firmware.

Centrifuge identified a startup script. I’ll start with that. But if you don’t have access to Centrifuge, then a good place to start is the /etc/init.d script and see what services run at boot.

Startup Commands

Startup Commands

At this point I like to use binwalk to unpack the firmware locally for easier browsing. Looking through the startup scripts requires reading comments and looking at function calls to identify what runs at boot. The start of /etc_ro/rcS is shown below. These files are usually commented pretty well, which is helpful with the identification process.


Working through all the scripts eventually leads to a mention of a webserver, alpahpd. The path to get there was:

/etc_ro/rcS -> /sbin/internet.sh -> /sbin/lan.sh -> /sbin/web.sh

So now we have our starting point!

The Target: alphapd Web Server

We can either dive right into disassembling alphapd in our tool of choice (IDA, Ghidra, Radare2, Binary Ninja, etc) or look at the Code Analysis section in Centrifuge to greatly narrow down our choices.

Centrifuge will call out both potential buffer overflows (if the destination is a stack variable) and command injections (if the parameter is a non-static string). This significantly reduces the number of function calls we need to examine from 300+ down to a respectable 32, which is a pretty substantial time save. The Code Analysis view gives all the information we need to start our analysis of the function calls in our disassembler of choice, which for me is IDA.

Code Analysis of alphapd

Code Analysis of alphapd

The server is already running by the time our exploit would be processed so we can skip any calls in main and calls prior to the message processing loop.

Buffer Overflow Requirements

For buffer overflows we look for two things. First, the destination must be a stack variable no matter the function call. If this is not true, then we won’t be able to overwrite the return address and control execution (more on this later). Second, we need to control some portion of the source data. Let’s look at an example.

Non-vulnerable strcpy Centrifuge

Non-vulnerable strcpy Centrifuge
Non-vulnerable strcpy IDA

Non-vulnerable strcpy IDA

The prototype for strcpy is:

strcpy(destination, source);

Function arguments in MIPS are $a0, $a1, $a2, $a3 for the first four args, then the stack if more arguments are required. To identify if the destination is on the stack, we need to see how $a0 is set.

MIPS executes instructions based on a 5 stage pipeline. As a call is executed, what comes after that call (the jalr) is also executed. This operation takes place in what is known as the delay slot.

In this example, the delay slot is where the first argument is set. It is simply $a0 = STACK ADDRESS + OFFSET. Therefore, the destination is on the stack.

Next is whether or not the source, $a1, is a user controllable string. From the code snippet noyes_select, it appears the string “No” or “Yes” will be the source. Since this is not a user controllable string, it will never result in a buffer overflow. This is simply a poor coding practice.

Command Injections

The same process is used to identify valid command injections. In this case, there is no destination. But we still need to control the source.

alphapd has a nice function, doSystem, that combines vsnprintf and system which means we are looking for an $a0 with some kind of format string in it. This method makes it quick and easy to identify potential vulnerabilities.

The example below is automatically discounted because a static string is passed as the argument with no chance of user controlled data.

doSystem Centrifuge

doSystem Centrifuge
doSystem IDA

doSystem IDA

User Controllable Buffer Overflow

Now let’s look at a buffer overflow that is user controllable:

strcpy Centrifuge

strcpy Centrifuge

strcpy Centrifuge

In this example, our destination is on the stack. The second parameter is set by the $s1 register. So now we travel back up the disassembly to see if it’s controllable.

$s1 Regsiter

$s1 Regsiter

The $s1 register is set to the contents of the $v0 register, which is used for return values from functions. In this case, the most recent function call is websGetVar, an alphapd function that retrieves variables from the URL. After some thought, we might end up with some pseudo code that looks like this.

sub_443138(request, argument_count, argument_value):
    char wep_encr[4]
    if (2 == argument_count):
        NVRAM_GET(wep_encr, "WEPEncryption")
        if ("WEPEncryption" in $URL)
            strcpy(wep_encr, $URL["WEPEncryption"])
        if (argv[1] == wep_encr):
            return "checked"
        return ""

Basically this function checks if supplied arguments are equal to the value of WEPEncryption from non-volatile random access memory or the URL. If they are equal, it returns the string “checked” or an empty string if they are not equal. If we can cause this function to be executed with WEPEncryption in the URL, then the contents of that value will be copied directly to the stack.

So the question is: who calls this function?

Lucky for us, there is only one reference to this function call.



Not a lot of information to go on here other than the string RadioOfWEPEncryWay. Doing a quick grep for that string shows it in one location.



By referencing our pseudo code, we can assume that the returned string “checked” or an empty string will be used to set the radio buttons on the wireless.htm page.

We also know that if wireless.htm is requested withWEPEncryption in the URL, it should copy that string directly to the stack.

There’s a little bit of handwaving going on here, but I did look through a lot of disassembly to figure out that websParaDefine function correlates the strings between %% and an associated processing function. Add all this together, and we end up with a request that looks something like this:


Those 'A's should end up on the function’s stack.


I cherry picked some nice examples for the purpose of this post. But the reality is, most of the potential vulnerabilities you examine are pretty complicated and take some time to understand, at least for me.

A good rule of thumb I follow: if it looks too complicated to execute the code path, then skip it. Have I missed vulnerabilities because of this rule? Probably. But I’m all about that low- to middle-of-the-tree-hanging-fruit.

Now that we have identified a potentially user controlled buffer overflow, next time we’ll dive in to how to emulate the server to determine if we can call this function and crash it.


Evan Walls is a vulnerability analyst and developer at Tactical Network Solutions, focusing on embedded system exploitation. When he isn’t searching for new exploits, he teaches training courses developed by TNS, including IoT Firmware Exploitation. Prior to working at TNS he was a Windows developer for the Department of Defense. Now that he’s seen the glory and freedom that is Linux, he vowed never to use another windows development machine again. You can follow him on LinkedIn, Twitter, or GitHub.

Recent Posts
Russians, Fancy Bears, and IoT Security

Russians, Fancy Bears, and IoT Security

During the 2019 Black Hat conference in Las Vegas, Nevada there was a massive announcement from Microsoft generating a lot of buzz. Their discovery? A malicious Russian hacker group has been targeting common IoT devices. Their goal? Widespread attacks on corporate...

D-Link: A Firmware Vulnerability – Part 1

D-Link: A Firmware Vulnerability – Part 1

Have you ever wanted to be like the super l33t hax0rs that you see in the movies? Sitting in a dark room pounding away randomly on a keyboard with the only light coming from the screen in front of you? The silence only broken by you saying “I’m in.”? Then this is the...

Do We Need to Watch the Eyes Watching Us?

Do We Need to Watch the Eyes Watching Us?

On May 21, 2019 the New York Times reported that the Trump Administration is considering a limit on Hikvision’s ability to buy American technology. Hikvision is one of the world’s largest surveillance camera manufacturers and is 42% owned by the Chinese...