f1ower's Blog

邪法面前,我亦无畏

ret2syscall的学习

最近在学习Pwn-stack-ROP基础。

在ctf-wiki上面有讲解到:bamboofox中的ret2syscall这道题。

不过我没有明白原文中的一些操作,很迷茫,所以就仔细去分析一下。

首先file和checksecimage.pngimage.png

有用的信息:

0x0:ELF 32-bit LSB executable //32位的ELF文件

0x1:NX: NX enabled //开启了NX保护,不能采用跳板入栈执行代码了,因为不可执行,所以栈溢出我们采用ROP技术。

然后我进行程序功能分析:image.png

一个输入点,和之前的题一样,然后使用IDA32来分析分析:

image.png

和前ctf-wiki前面的题一样,gets()栈溢出,不过我们这次不能像以前一样利用自己写在栈里面的代码获取shell了,因为上面已经提到的NX保护措施,那我们就用ROP。

所以我们对付这个程序就使用系统调用来获取shell,正常情况下,是这样的:

execve("/bin/sh",NULL,NULL)

按照ctf-wiki中的详细叙述是这样的:

这个程序是32位的,所以我们需要让:

0x0:系统调用号即eax应该为0xb

0x1:第一个参数即ebx应该指向/bin/sh的地址,其实执行sh的地址也可以

0x2:第二个参数即ecx应该为0

0x3:第三个参数edx应该为0

后面我就不太明白为什么要那样去写exp了,比较迷惑,所以:

打开了我以前画过的堆栈变化图以及我以前写的小程序片段(Win7/vc6.0环境):

image.png

image.png

在C语言的程序的函数调用的时候,栈会有一个平衡的概念,我称之为:堆栈平衡。而在vc6.0中默认使用的__cdcel就是典型的外平栈。

如果这个程序在call完了之后add esp,number 来进行堆栈平衡的话,就是__cdcel调用方式。

image.png

但是我在IDA和gdb进行反汇编之后在main函数中并没有发现类似于:

add esp,number

这样去平衡堆栈的汇编指令,那我只能认为这里使用的是__stdcall,也就是我称之为“内平栈”,那我们要证实这一点,需要去gdb里面找到证据:

image.png

Bingo,这里我也彻底明白了内外平栈和函数调用的一个小关系。


那么这个gets()函数在ret之后,esp指向的就应该是原esp+0x4,而我们控制ret地址也能将其指向其他的地址去继续执行。

等等…我刚才研究了那么久的内外平栈,我ret了之后和他们没有关系了啊!(震惊

没事,继续。

这里引用一句名言“在Pwn的时候,你的脑子就要像CPU和内存一样执行下面的代码并预知结果 ——鲁迅”

再看一遍我们需要的目标

0x0:系统调用号即eax应该为0xb

0x1:第一个参数即ebx应该指向/bin/sh的地址,其实执行sh的地址也可以

0x2:第二个参数即ecx应该为0

0x3:第三个参数edx应该为0

那我们去找需要控制eax的gadgets:image.png

0x080bb196是pop_eax_ret的gadgets,将这个地址放到ret_addr中,跳过去,那么pop的值就是ret+0x4,我们写上0xb,而pop_eax_ret执行完之后,会ret到当前esp中存放的值(解析为地址)去,所以我们直接在ret+0x8放入下个gadgets的地址。

我们需要改变ebx,ecx,edx,如法炮制:image.png

幸运的是,0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

可以直接操作三个pop,都正好是我们需要的,但是我们一定要注意我们放参数的顺序。

edx,ecx,ebx应该这样。

对应的参数是:

0,0,/bin/sh的地址

那么我们现在的Payload是这样了:

image.png

我们需要找到字符串为/bin/sh的gadgetsimage.png

然后我们需要遵从linux中系统调用的结束中断:image.png

让最后是int 0x80:

image.png

刚刚好,0x08049421 : int 0x80有了。那么程序在我们脑子里面就是这样:

image.png

应该这就能有shell了,我的exp学习了ctf-wiki中的写法:

image.png

然后:

image.png