Belkin F9K1111 V1.04.10 Firmware Analysis

August 19, 2015
Vectra AI Security Research team
Belkin F9K1111 V1.04.10 Firmware Analysis


Recently, it came to our attention that HP DVLabs has uncovered at least ten vulnerabilities in the Belkin N300 Dual-Band Wi-Fi Range Extender (F9K1111). In response to this, Belkin released firmware version 1.04.10. As this is the first update issued for the F9K1111 and there were not any public triggers for the vulnerabilities, we thought it would be interesting to take a deeper look.

Unpacking the update

To begin our analysis, we downloaded the firmware update from the vendor. We used a firmware tool called binwalk to unpack the update:

$ binwalk -Me F9K1111_WW_1.04.10_upg.bin

Our result is a pretty standard looking extracted SquashFS filesystem representing the root of the device as seen below.

Screenshot of binwalk SquashFS filesystem extraction representing the root of the device

Now in order to perform the bindiff, we will need to interact with the hardware a bit to get the files in their pre-patched state.

Getting the base firmware

To analyze the base firmware, we will need some way to dump the data off the physical device. To do this, we must first remove the device from its casing.

Belkin F9K1111 Firmware removed from its casing

Highlighted in red and blue are possible avenues for retrieving the firmware, the SPI flash chip and the UART interface respectively. Although we have seen some level of activity on the UART, we will proceed by analyzing the base image on the SPI flash chip. The pinout for the chip we are dealing with, MX25L1606e, is readily available from Macronix.

Macronix MX25L1606e pin configurations

After grabbing this sheet and removing the chip, we are ready to wire up up our GoodFET with respect to the above generic 8-pin pinout.

After bridging pins 7 and 8, we verify that everything is hooked up correctly with

$ python goodfet.spiflash info

Next we can run goodfet.spiflash dump  to obtain the contents of the chip.

$ python goodfet.spiflash dump s

screenshot of the goodfet.spiflash dump

Finally, we can do a quick strings on the resulting file to ensure that the dump looks legit (ie contains at least some readable strings).

screenshot of the resulting file to ensure that the dump looks legit

The resulting binary file can be unpacked nicely via binwalk as before.

Diffing the update

Moving both unpacked filesystems over to a Windows box and dropping them into WinMerge, we can see that really not much has changed.

screenshot of WinMerge

The files compiler_data, version, and FUNCTION_SCRIPT do not contain any interesting changes (apart from perhaps for some data which might be useful for fingerprinting). The change to util_system.asp is not too interesting either. So, pretty much we will be looking at Belkin's modifications to webs, the GoAhead Webserver.

Analysis of webs

HP's Zero Day Initiative has named the vulnerabilities with what appear to be affected function names or inputs. They are as follows:

  • formWpsStart pinCode Remote CodeExecution Vulnerability
  • formWlanSetupWPS wps_enrolee_pin Remote Code Execution Vulnerability
  • formWlanMP Remote Code Execution Vulnerability
  • formBSSetSitesurvey Remote Code Execution Vulnerability
  • formHwSet Remote Code Execution Vulnerability
  • formConnectionSetting Remote Code Execution Vulnerability
  • formAccept Remote Code Execution Vulnerability
  • formiNICWpsStart Remote Code Execution Vulnerability
  • formUSBStorage Remote Code Execution Vulnerability

So, after loading the patched version of webs into IDA, we searched for formHwSetin the list of functions and found nothing. In fact many of these functions weren't found. Pulling up Bindiff, we can see that 7 functions were removed during the update:

screenshot of list of functions

These correspond well to the data from the ZDI bulletin. In fact, every function listed in the ZDI advisories has been removed except for formWlanSetupWPS and formBSSetSitesurvey. Let's take some time to look at the removed functions.


The first function we consider is formUsbStorage. After giving the function a quick read, it is pretty obvious what the problem is here. The POST variable sub_dir which is accessed via the GoAhead webs API function websGetVar is then being used in a call to system, allowing for command injection.

Screenshot of formUsbStorage

This code could be triggered via:

wget --post-data="sub_dir=vectra;reboot" http://belkin.range/goform/formUSBStorage


A similar error can be found in form actionformWlanMP. Tracing the calls to websGetVar, we see a few possibilities.

Screenshot of an error found in actionformWlanMP

Following forwared, we see that these few possibilities will all work as avenues for injection into the system call -- we chose ateFunc.

Screenshot of an error found in ateFunc

This code could be triggered via:

wget --post-data="ateFunc=;reboot;" http://belkin.range/goform/formWlanMP


There is more command injection here, this time we are using the variable [sic] Anntena.

Screenshot of anntena variable injection in command

This code could be triggered via:

wget --post-data="Anntena=;reboot;" http://belkin.range/goform/formHwSet


Here, we've got command injection in the timeOut parameter in the formConnectionSetting function.

screenshot of command injection in the timeOut parameter in the formConnectionSetting function

This code could be triggered via:

wget --post-data="timeOut=1;reboot;" http://belkin.range/goform/formConnectionSetting


At this point, we've beaten the deleted function horse to death. Let's take a look at the more significant of the functions that Belkin decided not to delete - formBSSetSitesurvey. Here is an overview:

formBSSetSitesurvey overview

After recoiling in horror, we can zoom in and see that the major change is that Belkin has added a function called strcat_escape which is used throughout this function on sources originating form websGetVar.

Belkin has added a function called strcat_escape

This strcat_escape function takes 3 buffers - dst, src, and tokens. The function uses nested loops to search the src string for existence of any of the tokens to be escaped, if found they are escaped before being copied into dst. In the pictured case token_of_none_quotation is passed as tokens which is defined as"\\\"'$()<>` #&*

We reimplemented this function in C from the webs binary and can see expected output:

output from function in webs binary

This (presumably correctly) escaped string is then passed as normal to system via sprintf as before.

screenshot sprintf function

The effectiveness of this patch relies on a few factors:

  • strcat_escape function works completely as intended
  • strcat_escape does not unintentionally cause buffer overflows ;-)
  • strcat_escape is used on all user input which ends up at system
  • We have been in contact with Belkin about a few of these bullet points.


We are all already aware that the security maturity embedded device code is a problem. Here we see that even in devices released in 2014, it remains a problem.