白话二进制漏洞攻击方式第一部分
字数 1708 2025-08-06 08:35:14
二进制漏洞攻击方式详解:堆栈溢出与ret2libc攻击
0x00 前言
本教学文档将深入讲解二进制漏洞中的两种核心攻击方式:堆栈缓冲区溢出和return-to-libc(ret2libc)攻击。我们将从基础概念开始,逐步深入到攻击原理和实现细节。
0x01 应用程序内存基础
虚拟内存概念
- 程序执行时会被加载到内存中,为避免内存冲突使用虚拟内存技术
- 虚拟内存使每个程序认为自己独占整个内存空间
- 实际物理内存通过内存管理单元(MMU)进行映射管理
动态链接库(DLL)
- 程序常使用外部库函数(如libc中的printf)
- 这些库不存储在程序地址空间中,而是动态链接
- 在Linux中可用
ldd命令查看程序的共享库依赖 - 库函数在内存中有固定地址(如0xb7e99000)
0x02 堆栈基础
堆栈特性
- 采用后进先出(LIFO)原则的数据结构
- 用于存储函数调用信息、局部变量等运行时数据
- 主要操作:
- 压栈(Push):添加数据到栈顶
- 出栈(Pop):从栈顶移除数据
堆栈类比
想象一堆盘子:
- 只能从顶部添加或移除盘子
- 试图从中间移除会导致上方所有盘子掉落
- 类似地,不当的堆栈操作会导致程序崩溃
0x03 函数调用机制
函数调用过程
- 参数压栈:调用者将函数参数按顺序压入堆栈
- 返回地址压栈:call指令将下一条指令地址(返回地址)压栈
- 跳转执行:指令指针(EIP)跳转到被调用函数地址
- 函数返回:ret指令弹出返回地址并跳转回去
示例分析
int add(int A, int B) { return A+B; }
int main() { add(1, 2); }
对应的汇编操作:
- 压入参数2
- 压入参数1
- call add (压入返回地址)
- 执行add函数体
- ret (弹出返回地址)
0x04 堆栈缓冲区溢出攻击
基本原理
- 当程序不检查输入长度时,可覆盖相邻内存区域
- 通过精心构造的过长输入覆盖:
- 局部变量
- 函数返回地址
- 其他关键数据
实际案例
void vulnerable() {
char buffer[64];
int modified = 0;
gets(buffer); // 不检查输入长度的危险函数
if(modified != 0) {
printf("You've been hacked!\n");
}
}
攻击方法:
- 输入超过64字节的数据
- 溢出部分会覆盖modified变量
- 若继续溢出可覆盖返回地址
内存布局
[ buffer[64] ][ modified ][ 保存的EBP ][ 返回地址 ]
<--- 64字节 ---> 4字节 4字节 4字节
攻击者通过填充:
- 前64字节:任意数据填满buffer
- 接下来4字节:非零值覆盖modified
- 再接下来8字节:可覆盖EBP和返回地址
0x05 return-to-libc(ret2libc)攻击
攻击原理
- 利用堆栈溢出覆盖返回地址
- 将返回地址指向libc库中的函数(如system)
- 通过精心构造的栈布局传递所需参数
system函数利用
- system函数执行shell命令:
int system(const char *command) - 攻击步骤:
- 覆盖返回地址为system函数地址
- 在栈上布置参数(如"/bin/sh"字符串地址)
- 函数返回时执行system("/bin/sh")获得shell
栈布局构造
正常调用system("/bin/sh")的栈布局:
[ 返回地址 ][ 参数1 ]
ret2libc攻击构造的栈布局:
[ system地址 ][ 返回地址 ][ "/bin/sh"地址 ]
其中:
- system地址:libc中system函数的实际地址
- "/bin/sh"地址:内存中存在的该字符串地址(可在环境变量中布置)
攻击优势
- 不需要注入shellcode
- 绕过NX(不可执行栈)保护
- 利用现有库函数实现攻击
0x06 防御措施
针对堆栈溢出的防御
-
栈保护(Stack Canary)
- 在返回地址前放置随机值(canary)
- 函数返回前检查该值是否被修改
-
不可执行栈(NX/DEP)
- 标记栈内存为不可执行
- 防止执行栈上的shellcode
-
地址空间布局随机化(ASLR)
- 随机化库和程序的加载地址
- 增加预测返回地址的难度
针对ret2libc的防御
-
ASLR
- 随机化libc加载地址,使system地址不可预测
-
函数调用参数检查
- 确保返回地址指向合法位置
-
控制流完整性(CFI)
- 验证函数返回地址是否在合法调用路径上
0x07 总结
- 堆栈溢出:通过溢出缓冲区覆盖关键数据控制程序流
- ret2libc:利用现有库函数实现攻击,绕过简单防护
- 防御:需要多层防护(Canary+NX+ASLR+CFI)才能有效防护
理解这些基础攻击方式为进一步学习更复杂的漏洞利用技术奠定了基础。后续可以研究ROP(面向返回编程)、堆利用等高级技术。