写在前面:在 PWN 题中,你拿到的远程环境是什么 Ubuntu 版本,直接决定了你能用什么利用手法。很多新手拿着 Glibc 2.23 的老解法去打 2.35 的环境,撞得头破血流却说 Exploit 写错了。
从 2.23 到 2.35,Glibc 经历了数次“地震级”的安全加固。本文将为你梳理这条演进路线,并奉上 PWN 手必备的速查表。这是决定你能否在高版本环境生存的核心知识!
📑 目录
- 为什么 Glibc 版本是 PWN 的分水岭?
- 核心演进历程:从 2.23 到 2.35 发生了什么?
- 🚀 Glibc 2.23→2.35 PWN 关键变化速查表
- 地震级更新详读:Hooks 的陨落 (2.34)
- 高版本生存指南:没有 Hook 我们怎么打?
1. 为什么 Glibc 版本是 PWN 的分水岭?
不同的 Ubuntu LTS(长期支持版)默认绑定不同的 Glibc 版本:
- Ubuntu 16.04 -> Glibc 2.23
- Ubuntu 18.04 -> Glibc 2.27
- Ubuntu 20.04 -> Glibc 2.31
- Ubuntu 22.04 -> Glibc 2.35
Glibc 的更新主要针对堆管理和** Hook 机制**进行了疯狂修补。如果你不知道目标版本的查杀机制,你的 Double Free、你的 Hook 劫持统统会变成malloc(): unsorted double linked list corrupted然后无情退出。
2. 核心演进历程:从 2.23 到 2.35 发生了什么?
- 2.23 时代(古典时代):没有 Tcache,Fastbin 攻击是主流。
__malloc_hook和__free_hook像没人管的大门,随便覆写拿 Shell。 - 2.27 时代(Tcache 降临):引入了 Tcache(线程本地缓存)。堆块释放优先进入 Tcache,Fastbin 攻击大量失效。但 Tcache 早期没有任何检查,导致 Tcache Poisoning(投毒)横行。
- 2.29 ~ 2.31 时代(查杀加剧):Tcache 增加了
key字段用于检测 Double Free;Unsorted Bin 的链表检查变得极其严格,传统的 Unsorted Bin Attack 宣告死刑。 - 2.34 ~ 2.35 时代(至暗时刻):彻底移除了所有的 Hook 函数!
__malloc_hook、__free_hook、__realloc_hook全部变成了废弃符号。传统一招鲜吃遍天的打法彻底失效,强制要求转向 IO_FILE 攻击或栈劫持。
3. 🚀 Glibc 2.23→2.35 PWN 关键变化速查表
这是本篇的核心,建议截图或收藏备用:
| Glibc 版本 | 对应 Ubuntu | 核心安全机制变更 | 受影响的经典攻击手法 | 现行主流利用思路 |
|---|---|---|---|---|
| 2.23 | 16.04 | 无 Tcache;Fastbin 检查较弱 | Fastbin Double Free, __malloc_hook 覆写 | Fastbin Attack -> One_gadget |
| 2.27 | 18.04 | 引入 Tcache;Tcache 无 Double Free 检查 | Fastbin 攻击失效;Tcache Double Free 泛滥 | Tcache Poisoning -> __free_hook |
| 2.29 | 19.04 | Tcache 引入key字段防 Double Free;Unsorted Bin 加强 size 检查 | Tcache Double Free 失效;Unsorted Bin Attack 失效 | 破坏 key 字段 -> Tcache Poisoning -> Hook |
| 2.31 | 20.04 | 进一步加强各种链表完整性检查;Tcache 指针加密 (Protpor) | 堆指针伪造难度增加 | 同上,需绕过 key 检查 |
| 2.34 | 21.04 | 彻底移除__malloc_hook/__free_hook | 所有依赖 Hook 的利用手法全部报废 | 转向 IO_FILE (FSOP) 或_rtld_global劫持 |
| 2.35 | 22.04 | 巩固 2.34 变更;加强指针混淆与 TLV 检查 | IO_FILE 部分老手法受限 | House of Apple, 栈劫持 (ret2libc/ROP) |
4. 地震级更新详读:Hooks 的陨落 (2.34)
在 2.34 之前,我们只要有任意地址写,就能拿 Shell。比如:
- 申请一个堆块。
- 利用 UAF 或溢出,把
__free_hook的地址写进堆的fd指针。 - 把
__free_hook分配出来,写入system函数的地址。 - 程序下一次调用
free("/bin/sh")时,实际执行了system("/bin/sh")。
假设性说明(模拟 2.34 环境下的崩溃):
假设你在 Ubuntu 22.04 (Glibc 2.35) 下尝试老套路,用 GDB 查看目标地址:
pwndbg> p &__free_hook $1 = (<data variable, no debug info> *) 0x0你会发现,这个地址变成了0x0!它不再是全局变量了,相关的跳转逻辑在汇编层面也被直接删掉。你辛辛苦苦写进去的system地址,根本没有人去执行。这就意味着,一切依赖 Hook 的 Exploit 在 2.34 以上彻底成为废代码。
5. 高版本生存指南:没有 Hook 我们怎么打?
既然大门(Hook)被封死了,我们就得翻窗。在 2.34/2.35 环境下,通常有以下两种翻窗姿势:
5.1 IO_FILE (FSOP) 攻击
程序在退出或刷新缓冲区时,会调用_IO_flush_all_lockp,这个函数会遍历 IO 链表并调用虚函数表里的函数。
我们可以伪造一个恶意的_IO_FILE结构体,篡改它的虚函数表指针。当程序退出时,就会执行我们伪造的虚函数,从而劫持执行流(例如跳转到一个特定的 ROP Gadget,即 House of Apple 系列手法)。
5.2 栈劫持
如果我们能泄露到 Environ 指针(位于 libc 中,指向栈上的环境变量),我们就能得到栈地址。
有了栈地址,配合任意地址写,我们可以直接把 ROP 链写到栈上(比如覆盖__libc_start_main的返回地址),直接用 ROP 调用system或execve,绕过一切 Hook。
6. 结语
Glibc 的演进史就是一部攻防对抗史。作为 PWN 选手,我们必须牢记:拿到题目第一步,先看 Glibc 版本!
2.23 有 2.23 的打法,2.35 有 2.35的优雅。不要试图用过时的武器打新版本的补丁。
下一部分,我们将暂停宏观视角,回到微观的汇编世界,速查那些必须倒背如流的核心汇编指令。如果本文对你有帮助,请点赞收藏支持!🙏