跨机器缓冲区溢出漏洞利用可行性及远程攻击实现疑问
缓冲区溢出相关问题及代码排查
一、跨机器溢出利用代码有效性与内存地址一致性
- 针对某缓冲区溢出漏洞编写的exploit无法直接在其他机器上执行,内存地址也不会保持一致。现代操作系统普遍启用Address Space Layout Randomization(地址空间布局随机化,ASLR),每次程序启动时都会随机分配栈、堆、库等内存区域的基地址,即便同一机器多次运行,地址也无法固定。
二、ASLR环境下Geohot的缓冲区溢出攻击实现
Geohot能在ASLR环境下完成本地到远程的溢出攻击,核心思路包括:
- 信息泄露:先通过漏洞读取目标进程内存中的关键地址(如某库函数的实际加载地址),计算出ASLR的随机偏移量,再修正exploit中的跳转地址。
- ROP(返回导向编程):利用内存中已存在的代码片段(gadgets)拼接攻击逻辑,无需直接跳转到固定shellcode地址,通过栈上的返回地址链执行指令,绕开ASLR限制。
- 动态适配payload:本地环境完成漏洞验证和偏移分析后,远程攻击时先通过信息泄露获取目标随机基地址,再生成适配的payload。
三、服务器代码未触发缓冲区溢出的问题排查
代码与测试情况
服务器代码:
#include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #define PORT 1337 int main(int argc, char const* argv[]) { int server_fd, new_socket, valread; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); static char buffer[1024] = { 0 }; char buffer2[8] = { 0 }; char* hello = "Hello from server"; // 创建socket文件描述符 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 强制绑定端口 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定socket到端口 if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } while (1){ if (listen(server_fd, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } valread = read(new_socket, buffer, 1024); fprintf(stderr,"%d\n",valread); strcpy(buffer2, buffer); printf("%s\n", buffer); send(new_socket, buffer2, strlen(buffer2), 0); printf("Hello message sent\n"); close(new_socket); } }
测试发送的payload:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
程序输出:
8 AAAAAAAA Hello message sent
问题原因分析
- 实际读取长度不足:输出显示
valread为8,说明服务器仅读取到8个字符,可能是发送工具默认截断了数据(比如以换行符作为结束标志),导致buffer中只有8个'A',strcpy复制时未超出buffer2的临界范围,无法覆盖栈帧关键区域。 - 栈保护机制拦截:现代编译器默认启用Stack Canary(栈金丝雀),即便存在少量溢出,只要破坏金丝雀值,程序会直接触发栈检查并终止,而非触发段错误。
- 缓冲区初始化特性:
buffer是静态初始化的全0数组,若仅读取8个'A',后续字节仍为0,strcpy会在遇到第一个0时停止复制,进一步限制了溢出范围。
解决建议
- 确保发送完整长payload:用命令行工具发送足够长度的数据,比如
python3 -c 'print("A"*100)' | nc localhost 1337,覆盖buffer2及后续栈帧区域。 - 编译时关闭栈保护:使用
gcc -fno-stack-protector -z execstack -o server server.c,禁用金丝雀和栈执行保护,便于触发段错误。 - 检查发送逻辑:确保发送工具不会截断数据,发送完成后主动关闭socket,让
read读取到完整payload。
内容的提问来源于stack exchange,提问作者Mixalis Navridis
相关产品推荐
相关产品推荐

