05|Use-After-Free:钥匙还在你手里,柜子已经不是你的了

发布时间:2026/10/28 10:04:08
05|Use-After-Free:钥匙还在你手里,柜子已经不是你的了 Use-After-Free钥匙还在你手里柜子已经不是你的了UAFUse After Free翻译过来就是「释放后重用」。这个词听着抽象其实用一个场景就讲明白了——而且这个场景你八成真遇到过。先说这个场景去健身房办卡的时候管理员给你一个储物柜。1. 租柜子 → 管理员把 10 号柜给你钥匙在你手上 2. 放东西 → 你把衣服塞进 10 号柜 3. 退柜子 → 你跟前台说不用了系统里把 10 号柜标成空闲 4. 但你没还钥匙 → 你昨天下班顺手配了一把带着第二天另一个人来健身前台看 10 号柜空着就租给了他他把钱包手机放了进去。你拿着昨天配的那把钥匙开门了。抽屉里不是你的衣服是别人的钱包。这就是 UAF。柜子重新租出去了但钥匙没作废。对应到程序里储物柜 一块内存 钥匙 指向它的指针 退柜子 free() 配的钥匙 悬垂指针dangling pointer代码上就这么几行#include stdlib.h #include string.h #include stdio.h int main() { char *p malloc(100); strcpy(p, 正常的数据); free(p); /* 注意free 只告诉系统我不用了p 里存的地址一个字都没变 */ char *q malloc(100); strcpy(q, 别人的数据); printf(%s\n, p); /* 打印出来的是别人的数据 */ return 0; }跑一下就知道p和q大概率是同一个地址所以printf(p)打印的是q写进去的东西。这不是巧合。分配器为了效率永远优先复用刚释放的内存——你 free 完它立刻躺在回收站里等着被再分配。但关键不在复用了在被谁接手我之前一直以为 UAF 就是「用了已释放的内存」一句话讲完。后来才发现真正的分水岭是free 之后那块内存进了哪个 bin。因为它进了哪里你读出来的东西完全不一样进 tcache2.26尺寸 ≤ 0x4100x00 next ← 堆地址或者 NULL 0x08 key ← 2.29 起才有值是 tcache 结构地址进 fastbin尺寸 ≤ 0x800x00 fd ← 堆地址或者 NULL进 unsorted bin尺寸大于 fastbin/tcache 上限且后面没挨着 top0x00 fd ← main_arena 0x58/0x60是个 libc 地址 0x08 bk ← 同上所以块进 tcache / fastbin → 你泄到的是堆地址或者 NULL 块进 unsorted → 你泄到的是 libc 地址这就是「UAF 能用来泄 libc」的真正原因——不是 UAF 本身能泄什么是它 free 完之后恰好躺进了带 libc 指针的那个 bin。三种常见的来路第一种free 完忘了置空。free(p); /* 少了 p NULL; */最常见也最好查。第二种别名。char *a malloc(0x20); char *b a; free(a); strcpy(b, x); /* b 还在用 */这种最难查free(a)那行看起来毫无问题。第三种回调 / 闭包 / 观察者。cfg-on_event handler; /* 外部存了指针 */ config_free(cfg); /* cfg 释放了 */ /* 之后 handler 被触发 → UAF */真实软件里这种最多因为生命周期的管理权被分散到了两边。黑客拿它能干什么三个方向按门槛从低到高读——如果程序有show之类的功能能读已释放的块那就是一个泄露点。写——能往 freed chunk 里写就能改 next这就是 tcache poisoning / fastbin attack 的入口。改完再触发——不需要控制分配器只要程序后面还会用这块内存。比如typedef struct { char buf[0x18]; void (*fn)(void); } obj_t; obj_t *o malloc(sizeof(obj_t)); o-fn hello; free(o); /* 指针没清 */ o-fn pwned; /* UAF改写函数指针 */ o-fn(); /* 调用它 */这段能跑通输出pwned。没有任何堆布局可言就是纯改内存。一个容易忽略的细节进了 tcache 的块前 8 字节是 next、第 2 个 8 字节是 key。如果你想改的是别的字段比如上面那个fn在0x18那两个位置随便写都无所谓——不影响链也不影响你要改的地方。所以「UAF 能改什么」取决于两件事它进了哪个 bin以及你要改的字段在不在 bin 元数据的范围内。和 double free 别混UAF 是用一块已经不归你的内存double free 是再还一次。2.29 给 tcache 加的key检查只防 double freeif (__glibc_unlikely (e-key tcache)) /* 再遍历链表确认 → 报 free(): double free detected in tcache 2 */它完全防不住 UAF。所以 2.29 之后 UAF 照样能打只是 tcache 那条投毒的路变窄了。怎么修写代码的话就一条free(p); p NULL; /* 立刻作废这把钥匙 */后面再误用p程序会立刻崩在空指针上——崩溃比被利用好一百倍至少你知道出事了。语言层面的解法Java / Python / Go / C#GC 自动回收根本没有手动 free这一步Rust所有权 借用检查悬垂指针在编译期就过不去现代 Cstd::unique_ptr/std::shared_ptr生命周期结束自动释放并置空版本glibcUAF 能做什么≤ 2.25泄 libc、改 next、改结构体全都能2.26 ~ 2.28多了 tcache投毒更简单2.29能读能写但 double free 路径被 key 堵住2.32next 加密了改链要先泄堆地址2.34hook 被删落点得换_IO那条线变的是利用手法不是漏洞本身。UAF 一直都在。最后UAF 不是一个技巧是一个入口。它给你的只是「一块已经释放、但还能操作的内存」。至于拿它泄地址、改链、还是改个函数指针——取决于它此刻躺在哪个 bin 里。所以下次碰到 UAF先别急着写 payload先问一句这块内存现在归谁 相关阅读· 上一篇《04Large Bin Attack把一个堆地址写到任意位置》· 下一篇《06Chunk Extend改一个 size造出两块重叠内存》· 实战配套《18Use-After-Free 实战双档 bin 分离改函数指针》全部 exp 与 gdb 脚本GitHub 链接

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询