Wednesday, 27 November 2013

D-Link! What's wrong with you?

Craig Heffner has recently discovered an easy-to-exploit backdoor in several D-Link and Planex routers. As usual guys at devttys0.com made a great work!
One week later a similar backdoor was discovered on some NETGEAR devices.

I am not surprised. Authentication Bypass, Remote Code Execution, Cross-Site Scripts, Cross-Site Request Forgery affect a large number of Small Office Home Office (SOHO) devices. For example in February 2010 I disclosed an unauthenticated root console on LINKSYS access point here's a comment by John Strand at pauldotcom.

Late in 2009 I had the opportunity to setup a brand new D-Link DAP 1522 access point and I discovered a telnet interface with hardcoded credentials in the firmware. I have never disclosed the vulnerability to the vendor or publicly. Four years later the issue is still there on most D-Link SOHO network devices.

Although neither binwalk nor firmware-mod-kit were released in 2009 I want to show you how simple is to discover the vulnerability with these tools.

Let's starts the show.

I have a brand new D-Link DAP 1522 on my table. I turn on the device and perform a port scan on the default IP address. The port scan result shows a telnet service, neither the credentials nor the service are documented on the manual.

So it's time to reverse engineering the firmware and retrieve the credentials. The firmware is available here.

An hexdump shows that the first firmware bytes contain the access point model and the vendor name. In this firmware the string is “wapnd01_dlink_dap1522”.


$ hexdump -C dap1522_v1.41b02.bin | less

00000000 77 61 70 6e 64 30 31 5f 64 6c 69 6e 6b 5f 64 61 |wapnd01_dlink_da|
00000010 70 31 35 32 32 00 00 00 00 00 00 00 00 00 00 00 |p1522...........|
00000020 20 02 04 20 20 02 04 20 20 d0 31 00 00 00 00 00 | ..  ..  .1.....|
00000030 2f 64 65 76 2f 6d 74 64 62 6c 6f 63 6b 2f 32 00 |/dev/mtdblock/2.|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 70 34 46 7b cc 7e f2 7b 67 40 49 06 b0 c7 61 4d |p4F{.~.{g@I...aM|
00000060 5d 00 00 80 00 00 80 1a 00 00 00 00 00 00 08 3e |]..............>|
00000070 1c 1b 9b 07 3a 92 44 0a 50 ce 55 4e ad 8c 32 c5 |....:.D.P.UN..2.|
00000080 39 4c 17 49 25 40 13 ef d7 3c 14 d3 30 30 e5 72 |9L.I%@...<..00.r|
00000090 51 ce 74 2f 2d 98 75 69 35 0d 9a a0 a3 48 61 94 |Q.t/-.ui5....Ha.|
000000a0 ba 8a 97 6f 55 20 d9 c3 cc 38 3c f2 f3 06 1f 58 |...oU ...8<....X|
000000b0 6d 0c 38 b5 b7 90 21 16 3c 0d da d7 9a f0 0e 39 |m.8...!.<......9|
000000c0 27 fd 65 89 fa 25 91 86 0f b3 fd 93 5f 0b fe 4b |'.e..%......_..K|
000000d0 ce 74 15 d3 07 7d 48 a5 bc 72 cf 87 3c 8f a5 92 |.t...}H..r..<...|

000000e0 35 78 61 9c 19 b5 f2 37 07 97 69 eb 12 26 07 0d |5xa....7..i..&..|

According to binwalk the firmware contains a Squashfs filesystem.

$ binwalk dap1522_v1.41b02.bin

DECIMAL HEX DESCRIPTION
-----------------------------------------------------------------------------------------
96 0x60 LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 1736704 bytes
589920 0x90060 PackImg section delimiter tag, little endian size: 13641728 bytes; big endian size: 2674688 bytes
589952 0x90080 Squashfs filesystem, little endian, version 2.0, size: 2670897 bytes, 910 inodes, blocksize: 65536 bytes, created: Fri Feb 24 06:39:22 2012

Binwalk can easily extract the filesystem with the following command:

$ binwalk -Me ./dap1522_v1.41b02.bin

I can finally check into all files for the “telnet” string:

$ cd ./squashfs-root/
$ grep -irn "telnet" ./

Binary file ./lib/libcrypto.so.0.9.8 matches
./www/locale/en/tools_misc.php:8:$m_telnet ="Telnet";
./www/__adv_port.php:21: <option value='Telnet'>Telnet</option>
./www/tools_misc.php:257: <input type=radio name=console_pro value=1 onclick="CheckConsoleStatus()"><?=$m_telnet?>
Binary file ./usr/lib/tc/q_netem.so matches
Binary file ./usr/sbin/telnetd matches
Binary file ./usr/sbin/rgbin matches
./etc/defnodes/S11setnodes.php:45:set("/sys/telnetd","true");
./etc/templates/console.sh:5: echo -n "Stopping telnetd ... "
./etc/templates/console.sh:6: if test -r /var/run/telnetd.pid; then
./etc/templates/console.sh:7: kill `cat /var/run/telnetd.pid` 2> /dev/null && rm -f /var/run/telnetd.pid || echo " failed."
./etc/templates/console.sh:19: /etc/scripts/misc/telnetd.sh
./etc/scripts/misc/telnetd.sh:3:TELNETD=`rgdb -g /sys/telnetd`
./etc/scripts/misc/telnetd.sh:4:if [ "$TELNETD" = "true" ]; then
./etc/scripts/misc/telnetd.sh:5: echo "Start telnetd ..." > /dev/console
./etc/scripts/misc/telnetd.sh:8: telnetd -l "/usr/sbin/login" -u Alphanetworks:$image_sign -i $lf &
./etc/scripts/misc/telnetd.sh:10: telnetd &
./etc/scripts/system.sh:23: echo "start telnet daemon ..." > /dev/console
./etc/scripts/system.sh:24: /etc/scripts/misc/telnetd.sh > /dev/console

Now it is time to search for the “image_sign” value.

$ grep -irn "image_sign" ./

./usr/sbin/sys:2:image_sign=`cat /etc/config/image_sign`
./usr/sbin/sys:23: echo -ne "$image_sign\0" > /var/config.bin
Binary file ./usr/sbin/cli matches
./etc/defnodes/S10setnodes.sh:5:imagesign=` cat /etc/config/image_sign`
./etc/defnodes/S10setnodes.sh:25:rgdb -i -s "/runtime/layout/image_sign" "$imagesign"
./etc/templates/httpd/webs_run.php:22: fwrite2($httpdloop, "httpd -s ".query("/runtime/layout/image_sign")." -f /var/etc/httpd.cfg\n");
./etc/scripts/config.sh:3:image_sign=`cat /etc/config/image_sign`
./etc/scripts/config.sh:34: xmldb -n $image_sign -t > /dev/console &
./etc/scripts/misc/telnetd.sh:2:image_sign=`cat /etc/config/image_sign`
./etc/scripts/misc/telnetd.sh:8: telnetd -l "/usr/sbin/login" -u Alphanetworks:$image_sign -i $lf &

$ cat /etc/config/image_sign

wapnd01_dlink_dap1522

Now I have the password.

"Good news, everyone!" The password is in clear-text in the firmware. Do you remember?

$ hexdump -C dap1522_v1.41b02.bin | less

00000000 77 61 70 6e 64 30 31 5f 64 6c 69 6e 6b 5f 64 61 |wapnd01_dlink_da|
00000010 70 31 35 32 32 00 00 00 00 00 00 00 00 00 00 00 |p1522...........|
00000020 20 02 04 20 20 02 04 20 20 d0 31 00 00 00 00 00 | .. ..   .1.....|
00000030 2f 64 65 76 2f 6d 74 64 62 6c 6f 63 6b 2f 32 00 |/dev/mtdblock/2.|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

I have played with several D-Link firmwares. There is a variant in some firmwares were the password is stored as in the following example:

$ hexdump -C dap1522_revB_FW_200.bin | less

00000000 5e a3 a4 17 00 00 00 20 00 00 00 00 73 69 67 6e |^...... ....sign|
00000010 61 74 75 72 65 3d 77 61 70 6e 64 31 35 5f 64 6c |ature=wapnd15_dl|
00000020 6f 62 5f 64 61 70 31 35 32 32 62 00 5e a3 a4 17 |ob_dap1522b.^...|
00000030 00 00 00 24 00 36 70 20 77 fb 25 8c 0e b3 7c 47 |...$.6p w.%...|G|
00000040 bd 27 7c 35 2c 1f 64 86 64 65 76 3d 2f 64 65 76 |.'|5,.d.dev=/dev|
00000050 2f 6d 74 64 62 6c 6f 63 6b 2f 32 00 74 79 70 65 |/mtdblock/2.type|
00000060 3d 66 69 72 6d 77 61 72 65 00 00 00 5d 00 00 00 |=firmware...]...|

Vulnerable D-Link devices:
In my opinion all D-Link firmwares that contain "signature=" o a string as first bytes.

5 comments:

  1. Nice step-by-step procedure, keep it up.

    ReplyDelete
  2. I'm curious, did you actually try the credentials? They might be there but not active for all we know from the article :)

    ReplyDelete
  3. And again ... http://seclists.org/fulldisclosure/2013/Dec/6

    ReplyDelete