상세 컨텐츠

본문 제목

[Dreamhack] pwn-library

SYSTEM HACKING/Dreamhack

by koharin 2024. 1. 10. 21:38

본문

728x90
반응형

사전지식

chunk size

- fast chunk: 32~128byte

- small chunk: < 1024byte

- large chunk: >= 1024byte

 

free된 청크를 관리하는 bin

- Fastbin : 해제된 chunkfastbinsY에서 관리될 경우 해제된 Heap과 크기 동일하게 할당

- Unsorted bin : 해제된 chunkunsorted bin에서 관리되는 small, large chunk인 경우, 해제된 Heap의 크기와 같거나 작은 크기를 할당

- small bin: small chunk 관리

- large bin: large chunk 관리

 

풀이과정

int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  v3 = 0;
  puts("\n[*] Welcome to library!");
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  while ( 1 )
  {
    while ( 1 )
    {
      menuprint();
      printf("[+] Select menu : ");
      __isoc99_scanf("%u", &v3);
      if ( v3 != 275 )
        break;
      steal_book();
    }
    if ( v3 > 0x113 )
      goto LABEL_15;
    if ( v3 == 4 )
    {
      printf("Good Bye!");
      exit(0);
    }
    if ( v3 > 4 )
    {
LABEL_15:
      puts("Wrong menu...");
    }
    else
    {
      switch ( v3 )
      {
        case 3u:
          return_book();
          break;
        case 1u:
          borrow_book();
          break;
        case 2u:
          read_book();
          break;
        default:
          goto LABEL_15;
      }
    }
  }
}

- Select menu에서 275 입력 시 steal_book() 함수 호출됨

 

__int64 steal_book()
{
  size_t size; // [rsp+Ch] [rbp-124h] BYREF
  unsigned int v2; // [rsp+14h] [rbp-11Ch]
  FILE *stream; // [rsp+18h] [rbp-118h]
  char filename[8]; // [rsp+20h] [rbp-110h] BYREF
  __int64 v5; // [rsp+28h] [rbp-108h]
  __int64 v6; // [rsp+30h] [rbp-100h]
  __int64 v7; // [rsp+38h] [rbp-F8h]
  __int64 v8; // [rsp+40h] [rbp-F0h]
  __int64 v9; // [rsp+48h] [rbp-E8h]
  __int64 v10; // [rsp+50h] [rbp-E0h]
  __int64 v11; // [rsp+58h] [rbp-D8h]
  __int64 v12; // [rsp+60h] [rbp-D0h]
  __int64 v13; // [rsp+68h] [rbp-C8h]
  __int64 v14; // [rsp+70h] [rbp-C0h]
  __int64 v15; // [rsp+78h] [rbp-B8h]
  __int64 v16; // [rsp+80h] [rbp-B0h]
  __int64 v17; // [rsp+88h] [rbp-A8h]
  __int64 v18; // [rsp+90h] [rbp-A0h]
  __int64 v19; // [rsp+98h] [rbp-98h]
  __int64 v20; // [rsp+A0h] [rbp-90h]
  __int64 v21; // [rsp+A8h] [rbp-88h]
  __int64 v22; // [rsp+B0h] [rbp-80h]
  __int64 v23; // [rsp+B8h] [rbp-78h]
  __int64 v24; // [rsp+C0h] [rbp-70h]
  __int64 v25; // [rsp+C8h] [rbp-68h]
  __int64 v26; // [rsp+D0h] [rbp-60h]
  __int64 v27; // [rsp+D8h] [rbp-58h]
  __int64 v28; // [rsp+E0h] [rbp-50h]
  __int64 v29; // [rsp+E8h] [rbp-48h]
  __int64 v30; // [rsp+F0h] [rbp-40h]
  __int64 v31; // [rsp+F8h] [rbp-38h]
  __int64 v32; // [rsp+100h] [rbp-30h]
  __int64 v33; // [rsp+108h] [rbp-28h]
  __int64 v34; // [rsp+110h] [rbp-20h]
  __int64 v35; // [rsp+118h] [rbp-18h]
  unsigned __int64 v36; // [rsp+128h] [rbp-8h]

  v36 = __readfsqword(0x28u);
  size = 0LL;
  *(_QWORD *)filename = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  v8 = 0LL;
  v9 = 0LL;
  v10 = 0LL;
  v11 = 0LL;
  v12 = 0LL;
  v13 = 0LL;
  v14 = 0LL;
  v15 = 0LL;
  v16 = 0LL;
  v17 = 0LL;
  v18 = 0LL;
  v19 = 0LL;
  v20 = 0LL;
  v21 = 0LL;
  v22 = 0LL;
  v23 = 0LL;
  v24 = 0LL;
  v25 = 0LL;
  v26 = 0LL;
  v27 = 0LL;
  v28 = 0LL;
  v29 = 0LL;
  v30 = 0LL;
  v31 = 0LL;
  v32 = 0LL;
  v33 = 0LL;
  v34 = 0LL;
  v35 = 0LL;
  puts("[*] Welcome to steal book menu!");
  puts("[!] caution. it is illegal!");
  printf("[+] whatever, where is the book? : ");
  __isoc99_scanf("%144s", filename);
  stream = fopen(filename, "r");
  if ( stream )
  {
    fseek(stream, 0LL, 2);
    HIDWORD(size) = ftell(stream);
    fseek(stream, 0LL, 0);
    printf("[*] how many pages?(MAX 400) : ");
    __isoc99_scanf("%u", &size);
    if ( (unsigned int)size <= 0x190 )
    {
      if ( HIDWORD(size) > (unsigned int)size ) 
        HIDWORD(size) = size;
      ptr = malloc((unsigned int)size);
      memset(ptr, 0, (unsigned int)size);
      v2 = fread(ptr, 1uLL, HIDWORD(size), stream);
      if ( v2 == HIDWORD(size) )
      {
        memset(&secretbook, 0, 0x20uLL);
        strcpy((char *)&secretbook, "STOLEN BOOK");
        puts("\n[*] (Siren rangs) (Siren rangs)");
        puts("[*] Oops.. cops take your book..");
        fclose(stream);
        return 0LL;
      }
      else
      {
        printf("[*] result : %u\n", v2);
        puts("[*] it is locked..");
        return 1LL;
      }
    }
    else
    {
      puts("[*] it is heavy!!");
      return 1LL;
    }
  }
  else
  {
    puts("[*] we can not find a book...");
    return 1LL;
  }
}

힙에 size만큼 청크를 할당하고, 입력받은 filename을 읽어서 내용을 힙 청크에 적음. 이때 file 크기는 0x190까지 가능 

=> 플래그가 /home/pwnlibrary/flag.txt 경로라고 했으니, /home/pwnlibrary/flag.txt 입력하면 힙 청크에 플래그 내용 적을 수 있음

 

 

borrow_book 함수

__int64 borrow_book()
{
  unsigned int v1; // ebx
  unsigned int v2; // ebx
  unsigned int v3; // ebx
  int v4; // [rsp+4h] [rbp-1Ch] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  if ( (unsigned int)booksize > 0x4F )
  {
    puts("[*] book storage is full!");
    return 1LL;
  }
  v4 = 0;
  puts("[*] Welcome to borrow book menu!");
  booklist();
  printf("[+] what book do you want to borrow? : ");
  __isoc99_scanf("%u", &v4);
  switch ( v4 )
  {
    case 1:
      strcpy((char *)&listbook + 40 * (unsigned int)booksize, "theori theory");
      v1 = booksize;
      ***((_QWORD *)&unk_4060 + 5 * v1) = malloc(0x100uLL);
      memset(*((void **)&unk_4060 + 5 * (unsigned int)booksize), 0, 0x100uLL);
      strcpy(*((char **)&unk_4060 + 5 * (unsigned int)booksize), "theori is theori!");
      break;
    case 2:
      strcpy((char *)&listbook + 40 * (unsigned int)booksize, "dreamhack theory");
      v2 = booksize;
      *((_QWORD *)&unk_4060 + 5 * v2) = malloc(0x200uLL);
      memset(*((void **)&unk_4060 + 5 * (unsigned int)booksize), 0, 0x200uLL);
      strcpy(*((char **)&unk_4060 + 5 * (unsigned int)booksize), "dreamhack is dreamhack!");
      break;
    case 3:
      strcpy((char *)&listbook + 40 * (unsigned int)booksize, "einstein theory");
      v3 = booksize;
      *((_QWORD *)&unk_4060 + 5 * v3) = malloc(0x300uLL);
      memset(*((void **)&unk_4060 + 5 * (unsigned int)booksize), 0, 0x300uLL);
      strcpy(*((char **)&unk_4060 + 5 * (unsigned int)booksize), "einstein is einstein!");
      break;
    default:
      puts("[*] no book...");
      return 1LL;
  }
  puts("book create complete!");
  ++booksize;
  return 0LL;
}

unk_4060 전역변수에 빌린 책 주소가 적히고, bss 섹션의 unk_4060 다음에 booksize(빌린 책 수), 그리고 secretbook 주소가 적힌다.

책 크기는 0x100, 0x200, 0x300 

 

return_book 함수

__int64 return_book()
{
  puts("[*] Welcome to return book menu!");
  if ( booksize )
  {
    if ( !strcmp((const char *)&listbook + 40 * (unsigned int)(booksize - 1), "-----returned-----") )
    {
      puts("[*] you alreay returns last book!");
      return 1LL;
    }
    else
    {
      free(*((void **)&unk_4060 + 5 * (unsigned int)(booksize - 1)));
      memset((char *)&listbook + 40 * (unsigned int)(booksize - 1), 0, 0x20uLL);
      strcpy((char *)&listbook + 40 * (unsigned int)(booksize - 1), "-----returned-----");
      puts("[*] lastest book returned!");
      return 0LL;
    }
  }
  else
  {
    puts("[*] no book here..");
    return 1LL;
  }
}

책을 반납하면 빌린 책 제목을 적는 listbook 전역변수에 빌렸던 책 제목 부분에 returned를 적는다.

이때 책 크기는 0x100, 0x200, 0x300으로 모두  small chunk에 해당하고, small chunk 크기를 해제하면 unsorted bin에 들어간다. unsorted bin에 해제된 주소가 적히고, 해제된 Heap의 크기와 같거나 작은 크기를 할당는 경우 해당 청크가 재사용된다. (UAF, Use After Free)

 

따라서 1번 할당 및 해제 후, steal_book 메뉴에서 256 크기를 지정하면, 해제되었던 1번 청크에 플래그가 적히게 된다.

이후 2번으로 읽으면 플래그를 출력할 수 있다.

 

728x90
반응형

'SYSTEM HACKING > Dreamhack' 카테고리의 다른 글

[Dreamhack] off_by_one_001  (0) 2024.02.19
[Dreamhack] bof  (0) 2024.02.16
[Dreamhack] shell-basic  (2) 2024.01.10
[Dreamhack] master_canary  (0) 2022.01.05
[Dreamhack] welcome  (0) 2021.02.09

관련글 더보기