ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • UAF - pwnable.xyz
    Write-ups/pwnable.xyz 2020. 2. 13. 22:22

    Prob Info


    Prob Info
    Checksec

     

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      Game *v3; // rsi
      const char *v4; // rdi
      __int64 savedregs; // [rsp+10h] [rbp+0h]
    
      setup();
      initialize_game();
      printf("Name: ", argv);
      v3 = cur;
      v4 = 0LL;
      read(0, cur, 0x7FuLL);
      while ( 1 )
      {
        print_menu(v4, v3);
        read_int32();
        switch ( (unsigned int)&savedregs )
        {
          case 0u:
            return 0;
          case 1u:
            ((void (*)(void))cur->f_calc)();
            break;
          case 2u:
            save_game();
            break;
          case 3u:
            delete_save();
            break;
          case 4u:
            v3 = cur;
            v4 = "Save name: %s\n";
            printf("Save name: %s\n", cur);
            break;
          case 5u:
            edit_char();
            break;
          default:
            v4 = "Invalid";
            puts("Invalid");
            break;
        }
      }
    }

     

    prev_size 0xa1
    name: 0x80 bytes
    *data calc
    00000000 Game            struc ; (sizeof=0x90, align=0x8, copyof_13)
    00000000 name            db 128 dup(?)
    00000080 data            dq ?                    ; offset
    00000088 f_calc          dq ?                    ; offset
    00000090 Game            ends

    cur, saves를 분석해 위와 같은 구조체를 만들었다.

     

    Game *initialize_game()
    {
      Game *result; // rax
    
      cur = (Game *)malloc(0x90uLL);
      cur->data = malloc(0x90uLL);
      cur->f_calc = calc;
      result = cur;
      saves[0] = cur;
      return result;
    }

    이 함수에서 cur를 초기화하고 saves를 세팅한다.

     

    int save_game()
    {
      Game *v0; // rbx
      __int64 v1; // rdx
      Game *v2; // rax
      signed int i; // [rsp+Ch] [rbp-14h]
    
      for ( i = 1; ; ++i )
      {
        if ( i > 9 )
        {
          LODWORD(v2) = puts("No space.");
          return (signed int)v2;
        }
        if ( !saves[i] )
          break;
      }
      saves[i] = (Game *)malloc(0x90uLL);
      v0 = saves[i];
      v0->data = malloc(0x90uLL);
      if ( !saves[i]->f_calc )
        saves[i]->f_calc = cur->f_calc;
      printf("Save name: ");
      read(0, saves[i], 0x80uLL);
      v1 = i;
      v2 = saves[v1];
      cur = saves[v1];
      return (signed int)v2;
    }

    save_game에서 이름을 최대 0x80 바이트 저장할 수 있다.

     

    int edit_char()
    {
      int result; // eax
      unsigned __int8 v1; // [rsp+6h] [rbp-Ah]
      char v2; // [rsp+7h] [rbp-9h]
    
      puts("Edit a character from your name.");
      printf("Char to replace: ");
      v1 = getchar();
      getchar();
      printf("New char: ");
      v2 = getchar();
      result = getchar();
      if ( v1 && v2 )
      {
        result = (unsigned __int64)strchrnul(cur->name, v1);
        if ( result )
          *(_BYTE *)result = v2;
        else
          result = puts("Character not found.");
      }
      return result;
    }

    이 함수는 이름에서 한글자 바꾸는 기능을 하는데, strchrnul을 사용한다. 이 함수의 설명을 봐보자.

    man strchrnul

    이 함수는 문자열을 찾지 못하면 널을 반환하는 것이 아니라 문자열의 끝 ("\x00")을 가리키는 포인터를 반환한다. 그러므로 문자열을 찾든 못찾든 아래 조건을 만족한다.

    if ( result )
          *(_BYTE *)result = v2;

    힙 구조상 name을 0x80바이트 입력한 뒤 널바이트를 특정한 값으로 변경할 수 있어 Game::data 부분과 Game::f_calc 부분을 변경할 수 있어 함수 포인터를 win함수로 덮으면 된다.

    'Write-ups > pwnable.xyz' 카테고리의 다른 글

    fclose - pwnable.xyz  (0) 2020.02.15
    message - pwnable.xyz  (0) 2020.02.15
    iape - pwnable.xyz  (0) 2020.02.13
    J-U-M-P - pwnable.xyz  (0) 2020.02.13
    SUS - pwnable.xyz  (0) 2020.02.13

    댓글

Designed by Tistory.