栈溢出的第一步进阶——ROP返回导向编程

前面两次栈溢出只是让我们学习原理,是最简单、最基本的栈溢出问题,一般的pwn题不会这么简单,溢出一次就可以到位。今天我们就做第一步进阶,正式进入ROP。

就我目前理解的ROP,就是要通过连续多次栈溢出,利用其原有的gadget(可以叫做代码片段),构造一次系统调用,调用execve()函数,就相当于获得了一个shell。首先我们要知道,系统调用的中断号是0x80,而execve的功能号是11,也就是16进制的0xb。所以在进行系统调用(即执行INT 80H这条语句)之前,我们先要构造出调用execve()函数所需要的参数。

首先,需要将EAX寄存器的内容赋值为0xb,因为系统调用的功能号是默认存储到EAX寄存器当中的,而且我们上面说了,execve()函数的功能号是0xb,所以第一步要先给EAX赋值。然后,将EBX寄存器的内容赋值为"bin/sh"字符串所在的地址,就如下面的0x80BE408.

除此之外,还要将ECX和EDX的内容置零。以上就是调用execve()函数所需要的条件,总结起来就是下面几条语句。

mov eax,0xb

mov ebx,["bin/sh"]

mov ecx,0

mov edx,0

int 80

=>execve("bin/sh")

因为代码中基本不可能会出现这样连续的语句,所以我们需要通过连续的栈溢出,使这样的gadget(代码片段)能够连续执行,从而达到我们目的。下面就是我们要构造的栈内容。

stack
                                                   int 0x80 address            
                                                                0
                                                pop_edx_ret address
                                                                0
                                                pop_ecx_ret address
                                                       "bin/sh" address
                                                   pop_ebx_ret address
                                                               0xb          
                                                  pop_eax_ret address

                                     

接下来我们就去具体完成一个题目。

经典开头,使用checksec命令查看一下文件类型和权限。

 发现是32位程序,小端模式,而且栈不可执行。和以前一样,我们先调试,查看一下覆写掉main函数的return address需要多少个字节的垃圾数据。

0x98-0x2c+4=112个字节数据。接下来我们就需要找我们要用到的gadget的地址了。

我们通过筛选的方式,找到可用的gadget,这里我们pop eax_ret选用0x80bb196

同样的方式找一个可用的pop_ebx_ret,这里我们选用0x806eb90

 找到中断的地址0x8049421.

然后就是去寻找"bin/sh"的地址,我们可以去ida里面寻找,通过shift+F12再ctrl+F搜索

或者也可以再ubuntu下使用pwntools工具进行查找

 找到以上的数据,我们的参数就已经凑齐了,接下来就可以去编写攻击脚本啦

执行以上脚本,我们就可以成功的获取到shell啦!

ROPgadget --binary ret2syscall --only "pop|ret" | grep ebx/

/ROPgadget工具 --binary ret2.. 指明二进制文件 --only "",要搜索内容 grep输出带有其后第一个参数的内容

flat()函数//接收一个列表参数,列表每一项转换成字节型数据,并且不足一个字的自动补齐

猜你喜欢

转载自blog.csdn.net/z671514087/article/details/124521236