__malloc_hook以及__free_hook劫持原理

__malloc_hook以及__free_hoook的作用

  • The GNU C library lets you modify the behavior of malloc(3), realloc(3), and free(3) by specifying appropriate hook functions. You can use these hooks to help you debug programs that use dynamic memory allocation, for example. from die.net

  • 在调用malloc或者free的时候,如果 malloc_hook 和free_hook的值存在,则会调用malloc_hook或者free_hook指向的地址,假设在使用one_gadget的时候满足one_gadget的调用条件,当overwrite malloc_hook和free_hook的时候,便可以getshell,执行malloc的时候,其参数是size大小,所以overwrite malloc_hook的时候使用one_gadget的地址可以getshell。执行free的时候,可以将__free_hook的值overwrite为system的地址,通过释放(/bin/sh\x00)的chunk,可以达到system(/bin/sh)来getshell

劫持原理

  • malloc_hook位于main_arena上方-0x10的位置,可以通过fake chunk来overwrite该值实现getshell

  • free_hook 位于libc上_free_hook上,可以通过fake chunk来overwrite该值达到劫持程序流的目的

malloc_hook

  • malloc_hook的调用
1
2
3
4
  2907   void *(*hook) (size_t, const void *)
2908 = atomic_forced_read (__malloc_hook);
2909 if (__builtin_expect (hook != NULL, 0))
2910 return (*hook)(bytes, RETURN_ADDRESS (0));
  • 对应的汇编代码为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.text:00000000000730AE                 push    rbp             ; Alternative name is '__libc_malloc'
.text:00000000000730AF push rbx
.text:00000000000730B0 sub rsp, 8
.text:00000000000730B4 mov rbp, rdi
.text:00000000000730B7 mov rax, cs:__malloc_hook_ptr #取malloc_hook的位置
.text:00000000000730BE mov rax, [rax] #取malloc_hook的值
.text:00000000000730C1 test rax, rax
.text:00000000000730C4 jz short loc_730D2
.text:00000000000730C6 mov rsi, [rsp+18h]
.text:00000000000730CB call rax
malloc_hook的存储地址
.got:0000000000388EF8 __malloc_hook_ptr dq offset __malloc_hook
malloc_hook的存储位置
.data:0000000000389B10 __malloc_hook dq offset malloc_hook_ini
.data:0000000000389B10 ; DATA XREF: LOAD:000000000000A380↑o
.data:0000000000389B10 ; .got:__malloc_hook_ptr↑o
.data:0000000000389B18 align 20h
.data:0000000000389B20 ; malloc_state main_arena
.data:0000000000389B20 main_arena malloc_state <0, 0, 0, 0, 0, 0, 0, offset main_arena, 0, 1, 0, 0>
  • 利用思路:在执行malloc时,会检测__malloc_hook的值,如果malloc_hook的值存在,将调用malloc_hook指向的地址call rax,如果我们将该值overite 为one_gadget,当程序执行malloc的时候,便可以getshell

  • 实际利用过程

    • malloc_hook的存储的位置
1
2
3
4
gdb-peda$ p &__malloc_hook
$2 = (void *(**)(size_t, const void *)) 0x7ffff7dd5b10 <__malloc_hook>
gdb-peda$ p &main_arena
$3 = (struct malloc_state *) 0x7ffff7dd5b20 <main_arena>
  • 在malloc_hook 上方查找满足条件的fastbin 的size
1
2
3
4
5
gdb-peda$ x/8gx  0x7ffff7dd5b00-0x20
0x7ffff7dd5ae0 <_IO_wide_data_0+288>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd5af0 <_IO_wide_data_0+304>: 0x00007ffff7dd4260 0x0000000000000000
0x7ffff7dd5b00 <__memalign_hook>: 0x00007ffff7ac0305 0x00007ffff7ac02c6
0x7ffff7dd5b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
  • 查找符合条件的fake_fastbin,pwndbg内置了查找符合条件的fake_fast_bin的命令
1
2
3
4
5
6
7
8
9
10
gdb-peda$ find_fake_fast 0x7ffff7dd5b10 0x7f
FAKE CHUNKS
Fake chunk | Allocated chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA
Addr: 0x7ffff7dd5aed
prev_size: 0xfff7ac02c600007f
size: 0x7f
fd: 0x00
bk: 0x7f
fd_nextsize: 0xfff7ac0305000000
bk_nextsize: 0xfff7dd4260000000

伪造的fastbin的addr为0x7ffff7dd5aed,当程序可以在该位置申请chunk的时候,便可以达到程序流劫持getshell

free_hook

  • free_hook的调用
1
2
3
4
5
6
7
  2939   void (*hook) (void *, const void *)
2940 = atomic_forced_read (__free_hook);
2941 if (__builtin_expect (hook != NULL, 0))
2942 {
2943 (*hook)(mem, RETURN_ADDRESS (0));
2944 return;
2945 }
  • 对应的汇编代码为
1
2
3
4
5
6
7
8
9
10
11
12
汇编代码
.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 的存储地址
.got:0000000000388F00 __free_hook_ptr dq offset __free_hook ; DATA XREF: ptmalloc_lock_all+EA↑r
free_hook 的存储位置
.bss:000000000038B7A8 __free_hook dq ? ; DATA XREF: LOAD:0000000000005190↑o
  • 利用思路:

    • 通过改写main_arena中的top_chunk的地址,将top_chunk的值改写到free_hook上方指定的位置,通过不断向top_chunk申请chunk,最终可以分配到包含free_hook的区块,从而可以改写__free_hook的值。
    • 通过改写global_max_fast的值,可以在free_hook的上方找到一个足够大包含free_hook的块,当改写了global_max_fast后,向heap申请的块都将按照fastbin来处理
    • 通过unsorted bin attack 在free_hook上方伪造 0x7f大小的chunk,再通过fastbin attack 来修改free_hoook的值
  • 实际利用过程

    • 修改top_chunk

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

        1
        2
        3
        4
        5
        6
        gdb-peda$ p &__free_hook
        $5 = (void (**)(void *, const void *)) 0x7ffff7dd77a8 <__free_hook>
        gdb-peda$ x/8gx 0x7ffff7dd77a8 -0xb58
        0x7ffff7dd6c50 <initial+16>: 0x0000000000000004 0x697b5349788728d7
        0x7ffff7dd6c60 <initial+32>: 0x0000000000000000 0x0000000000000000
        0x7ffff7dd6c70 <initial+48>: 0x0000000000000000 0x0000000000000000
      • 伪造的fastbin的addr为0x7ffff7dd6c50,当程序可以在该位置申请chunk的时候,通过重复向top_chunk申请,最终覆写__free_hook的值为system的值通过free /bin/sh\x00的chunk,达到程序流劫持getshell。

    • 修改global_max_fast,但是这依赖于可以malloc的chunk的大小是可控的

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

        1
        2
        3
        4
        5
        gdb-peda$ x/200gx 0x7ffff7dd77a8-0xC00
        0x7ffff7dd6ba8 <lock+8>: 0x0000000000000000 0x0000000000000000
        ....
        0x7ffff7dd6c48 <initial+8>: 0x0000000000000001 0x0000000000000004
        0x7ffff7dd6c58 <initial+24>: 0xbf378114d045c300 0x0000000000000000
    • 通过unsorted_bin attack 来写入free_hook上方0x7f的值,可以在free_hook上方伪造出0x7f大小的chunk,再可以通过fastbin attack 来malloc得到包含free_hook的chunk,写入system的地址,通过free(/bin/sh)来实现利用。