修改RSP栈指针:格式化字符串漏洞利用返回地址覆盖问题咨询
看起来你混淆了格式化字符串漏洞和缓冲区溢出的payload构造逻辑,这正是导致返回地址被错误覆盖成0x909090909040089a的原因。我来帮你理清思路,一步步解决这个问题:
为什么你的当前payload会出错?
你现在写的'%24d' + '\x9a\x08\x40\x00\x90\x90\x90\x90\x90\x90ABCDEFGHIJJKLMNOPQRST'是缓冲区溢出的思路:试图填充固定字节数到返回地址位置,再覆盖地址。但格式化字符串漏洞的处理逻辑完全不同:%24d会被printf解析为格式化指令,输出一个宽度为24的十进制数(比如对应参数为0时,会输出23个空格+1个0,共24字节),而不是直接填充24字节的垃圾数据。后面的\x90和字符也会被直接输出,最终这些内容混杂在一起写入栈,导致返回地址被\x90字节污染。
正确的格式化字符串漏洞利用步骤(覆盖返回地址为0x40089a)
格式化字符串漏洞的核心是通过格式化指令(如%n)直接修改栈上的指定位置,而不是靠填充字节覆盖。下面是具体步骤:
1. 定位返回地址在参数列表中的偏移
首先要找到main函数返回地址在格式化参数中的位置。在GDB中输入以下payload来探测栈上的各个参数:
python -c "print '%p.'*20"
执行后,查看输出的一串指针,找到那个对应main返回地址的条目(通常是类似0x40xxxx或0x7ffffxxx的地址)。假设这个地址是第6个参数,那我们就用%6$p来引用它。
2. 构造写入payload
要把返回地址的值改成0x40089a,你需要:
- 先把返回地址所在的栈内存地址(比如假设是
0x7fffffffe018,64位程序下小端序为\x18\xe0\xff\xff\xff\x7f\x00\x00)放在payload开头,让它成为格式化字符串的一个参数。 - 计算需要输出的字节数:因为
%n会将当前已输出的字节数写入指定位置,所以我们需要让总输出字节数等于目标值0x40089a。假设开头的栈地址占了8字节,那么还需要输出0x40089a - 8 = 4196594字节。 - 用格式化指令快速填充并写入:用
%4196594c来填充所需字节,再用%1$lln(1$表示第一个参数,也就是我们放在开头的栈地址;lln表示写入8字节,适配64位程序)完成写入。
最终的payload命令类似:
python -c "print '\x18\xe0\xff\xff\xff\x7f\x00\x00' + '%4196594c%1$lln'"
注意事项
- 如果是32位程序,目标地址
0x40089a是32位,栈地址只需4字节,用%1$n即可(n写入4字节)。 - 直接输出几十万字节可能效率很低,实际利用中通常会分阶段写入低4字节和高4字节,减少payload长度。
如果你其实是想做缓冲区溢出?
如果你的目标程序实际存在缓冲区溢出漏洞(比如用gets读取输入),那应该回到缓冲区溢出的payload逻辑:用纯垃圾数据填充到返回地址位置,再覆盖目标地址。正确的命令应该是:
# 64位程序:24字节垃圾 + 小端序的0x40089a + NOP sled + shellcode python -c "print 'A'*24 + '\x9a\x08\x40\x00\x00\x00\x00\x00' + '\x90'*20 + '你的shellcode内容'"
这样就能精准覆盖返回地址为0x40089a,不会混入\x90字节。
内容的提问来源于stack exchange,提问作者re3el

