
栈溢出漏洞利用的核心是通过构造精心设计的payload覆盖函数返回地址,绕过地址空间布局随机化(ASLR)和数据执行保护(DEP),最终控制程序执行流程,实现提权或执行任意代码。
老师口吻:栈溢出利用的本质是“覆盖函数返回地址,劫持程序执行流程”。函数调用时,栈从高地址向低地址增长,保存返回地址(函数返回后跳转的地址)、参数、局部变量。当缓冲区(如局部变量buffer)溢出时,输入数据会覆盖返回地址,导致函数返回时跳转到恶意地址。
利用步骤可拆解为三步:
buffer占8字节,返回地址在栈上偏移24字节,偏移量为24)。0x90指令,跳过验证时自动跳转);%8$n等获取可执行区域地址),计算绝对地址(如libc基址+system函数偏移);execve)绕过,或通过返回到已加载的库函数(如printf)跳过DEP)。类比:把栈想象成“堆叠的盘子”,函数调用时放返回地址(盘子1)、参数(盘子2)、局部变量(盘子3)。缓冲区溢出就像把盘子3的菜(数据)堆到盘子1上,导致盘子1的位置被覆盖,下次函数返回时跳到错误地址。
| 比较项 | 栈溢出利用 | 堆溢出利用 |
|---|---|---|
| 栈布局 | 固定偏移(函数内局部变量位置固定) | 堆偏移不固定(由malloc分配,位置随机) |
| 返回地址位置 | 固定(函数内偏移量固定) | 需分析堆布局(更复杂) |
| 绕过ASLR | 需信息泄露找地址,计算绝对地址 | 需堆布局分析,难度更高 |
| 绕过DEP | 用shellcode或系统调用 | 同样需要shellcode,但堆布局分析更复杂 |
以一个存在缓冲区溢出的C函数为例:
void vulnerable_function(char *input) {
char buffer[8];
strcpy(buffer, input); // 溢出点
printf("Input processed\n");
}
int main() {
vulnerable_function("A" * 24); // 输入24字节,覆盖返回地址
return 0;
}
利用步骤:
buffer占8字节,返回地址在栈上偏移24)。0x90*4);system("/bin/sh")的shellcode(约31字节);libc基址(如%8$n泄露),计算system函数绝对地址(libc基址 + system偏移);/bin/sh。“栈溢出利用的核心是通过构造payload覆盖函数返回地址,绕过ASLR和DEP。首先,确定缓冲区大小和返回地址偏移,比如函数中局部变量buffer大小为8字节,返回地址在栈上偏移24字节,所以偏移量是24。然后构造payload:前4字节NOP滑板(跳过验证),中间是system("/bin/sh")的shellcode(约31字节),后8字节是偏移量调整后的返回地址(指向shellcode)。绕过ASLR时,通过格式化字符串漏洞获取libc基址,计算system函数的绝对地址;绕过DEP时,用shellcode替换返回地址,因为shellcode在非执行内存(通过信息泄露或漏洞),或者利用系统调用(如execve)绕过。比如,假设通过漏洞获取libc基址为0x7f000000,system地址是0x7f000100,那么返回地址设置为0x7f000100,程序就会跳转到system函数执行,最终执行/bin/sh。这样就能利用栈溢出漏洞获取shell。”