为何含空字节的栈溢出Payload无效?如何填充0x00001292至返回地址
缓冲区溢出中空字节导致失败的问题分析与解决
问题背景
我在学习缓冲区溢出技术,编写的silly_password.c程序中,check_authentication函数因使用strcpy存在栈溢出漏洞,可通过命令行参数覆盖函数返回地址。目前已成功用AAAA...将返回地址填充为0x41414141,但需要将返回地址填充为目标指令地址0x00001292时遇到问题:
- 使用命令
gdb -q --args silly_password $(perl -e 'print "\x92\x12" x 20;')时,栈被0x12921292填充且溢出成功; - 但使用含空字节的命令
gdb -q --args silly_password $(perl -e 'print "\x92\x12\x00\x00" x 20;')时,栈未发生溢出。
相关代码:
int check_authentication(char *password) { char password_buffer[16]; volatile int auth_flag = 0; strcpy(password_buffer, password); if(strcmp(password_buffer, "brillig") == 0) auth_flag = 1; if(strcmp(password_buffer, "outgrabe") == 0) auth_flag = 1; return auth_flag; // 此处为栈布局中的返回地址位置 }
原因分析
1. Shell命令行参数的截断行为
Shell在处理命令行参数时,会将空字节\x00视为字符串结束符。当你用$(perl ...)生成含\x00的字符串时,shell只会传递\x00之前的内容给程序,也就是\x92\x12,后续的\x00\x00完全被截断,导致传入程序的payload长度不够,无法覆盖返回地址。
2. strcpy的停止复制特性
即使程序能收到含\x00的参数,strcpy函数本身也会在遇到\x00时立即停止复制,不会继续往缓冲区后方(包括返回地址所在的栈位置)写入数据,自然无法触发溢出覆盖。
解决方法
方法1:利用栈原有空字节,仅覆盖低地址位
32位程序中,栈的高位地址通常默认是0。如果目标地址0x00001292的高两位本来就是栈上的原有值(空字节),你只需要覆盖返回地址的低两位即可:
- 先计算从缓冲区到返回地址的填充长度:比如
password_buffer是16字节,加上auth_flag的4字节,再加上ebp的4字节,总共需要填充16+4+4=24个字节才能到达返回地址位置; - 生成payload:
perl -e 'print "A"x24 . "\x92\x12"',这样返回地址的低两位被设为0x1292,高两位保留栈上原有的0x0000,最终组合成目标地址0x00001292。
方法2:绕过命令行,用文件/标准输入传递payload
命令行无法传递含空字节的字符串,改用标准输入或文件输入可以完整传递payload:
- 生成包含空字节的payload到文件:
(注意:这里的24是示例填充长度,需要根据实际栈布局调整)perl -e 'print "A"x24 . "\x92\x12\x00\x00"' > payload.bin - 在gdb中运行程序,从文件读取payload:
这种方式下,程序会读取文件中的所有字节(包括gdb -q silly_password run < payload.bin\x00),strcpy会完整复制直到缓冲区溢出,覆盖返回地址。
方法3:寻找无空字节的等价指令地址
如果目标地址0x00001292的空字节无法绕过,可以查找目标指令的其他可访问地址——比如程序的代码段中,有没有其他偏移指向同一个指令,且地址不含空字节;或者利用跳转指令(如jmp esp、call eax等)的非空地址,再构造后续payload跳转到目标指令。
内容的提问来源于stack exchange,提问作者lafinur
相关产品推荐
相关产品推荐

