当前位置:首页 > 焦点

缓冲区溢出攻击实例

2003年,缓冲SQL Slammer蠕虫在10分钟内感染了全球约75,区溢000台服务器。它的出攻传播速度之所以恐怖,是击实因为攻击载荷只有376字节,却能通过一个缓冲区溢出漏洞完全接管目标机器。缓冲这个漏洞出在Microsoft SQL Server 2000的区溢解析代码里,一个固定长度的出攻栈缓冲区被塞进了超长数据,返回地址被覆盖,击实CPU随即跳转到攻击者指定的缓冲位置执行代码。整个过程不需要用户点击任何东西,区溢数据包到达即中招。出攻

缓冲区溢出说穿了就是击实程序对内存的写入越了界。你声明了一个数组char buf[64],缓冲结果拷贝了128字节进去,区溢多出来的出攻64字节不会凭空消失,它会顺着栈往下写,把相邻的变量、保存的寄存器值、函数返回地址全部覆盖掉。栈的布局是从高地址往低地址增长的,局部变量在低地址,返回地址在高地址。当你往buf里写入超过64字节的数据时,超出的部分恰好朝着返回地址的方向蔓延。

写个最简单的例子。假设有这样一个函数:

void vulnerable(char *input) {  char buf[16]; strcpy(buf, input); } 

strcpy不做长度检查,input有多长它就拷多长。如果传入32个'A',buf装不下,后面16个'A'会覆盖掉栈上保存的EBP和返回地址。函数执行完ret指令时,CPU从栈顶弹出返回地址——但这个地址已经被改成了0x41414141('A'的ASCII码)。程序直接崩溃,因为0x41414141不是合法指令地址。

攻击者不会只填'A'。他们要做的是精确控制覆盖后的返回地址,让它指向自己注入的shellcode。实际操作中,需要先确定从buf起始位置到返回地址的偏移量。用Metasploit的pattern_create生成一串唯一字符序列,触发崩溃后看EIP寄存器的值,就能反推出偏移。比如偏移是28字节,那payload的结构就是:28字节填充 + 4字节跳转地址 + shellcode。

那个4字节跳转地址指向哪里?常见做法是跳到栈上shellcode的起始位置。但栈地址在不同环境下会变,ASLR(地址空间随机化)让这件事变得麻烦。早期的Windows XP SP2之前,栈地址基本固定,直接硬编码0x7FFA4512这类JMP ESP指令的地址就行。SP2之后ASLR默认开启,就得配合堆喷射或者信息泄露漏洞来绕过。

我曾在实验环境里复现过一个Linux下的经典栈溢出。关掉ASLR(echo 0 > /proc/sys/kernel/randomize_va_space),编译时加-fno-stack-protector -z execstack,然后用gdb定位偏移。payload里shellcode用的是execve("/bin/sh")的23字节版本,返回地址指向环境变量里shellcode的地址。运行后拿到一个shell,那种感觉比打游戏通关还爽。但现实世界里,现代编译器默认开启栈保护(Canary)、NX位和ASLR,裸的栈溢出已经很难直接利用了。攻击者转向堆溢出、格式化字符串、ROP链这些更复杂的手法。

SQL Slammer能成功,是因为那个年代的SQL Server既没开ASLR也没开DEP,栈保护也不存在。一个376字节的UDP包就能让整个互联网抖三抖。现在你写C代码,编译器会警告strcpy不安全,推荐用strncpy或者更安全的strcpy_s。但老代码、嵌入式设备、IoT固件里,这类漏洞依然一抓一大把。缓冲区溢出没有消失,它只是从聚光灯下退到了阴影里。

分享到: