BOF/ ftz-level15

 

FTZ란 Free Training Zone의 약자로 hackerschool에서 배포하는 워게임입니다.
총 20문제로 구성되어 있으며 각 문제를 격파하여 다음 레벨의 패스워드를 얻는 형식입니다.
문제를 격파하고 다음 레벨의 bash를 획득하였다면 my-pass로 패스워드를 확인할 수 있습니다.

사용 linux 명령어
ls - 파일 내역 출력 cat - 파일 내용 확인
cp - 파일 또는 디렉터리 복사 명령
gdb - 디버깅 도구

send me email if you have any questions.


FTZ Level 15

ID : level15
PASSWD : guess what


1
2
3
4
5
6
7
8
9
10
11
12
13
14
  
#include <stdio.h>

main()
{ int crap;
  int *check;
  char buf[20];
  fgets(buf,45,stdin);
  if (*check==0xdeadbeef)
   {
     setreuid(3096,3096);
     system("/bin/sh");
   }
}


level11부터는 attackme라는 파일이 존재하며 hint는 attackme의 소스가 제공된다.

소스분석
buf앞에 선언된 check를 0xdeadbeef로 변조하면 level16의 shell을 실행시켜 주는 소스이다.
하지만 level14와 달리 check가 포인터로 선언.

ftz에는 ASLR(메모리 보호기법)이 걸려있다.

ASLR은 프로그램을 실행할 때 마다 메모리 주소가 변경되는 메모리 보호기법


그림1
권한문제가 없도록 tmp디렉터리에 attackme를 복사하고 attackme의 메모리 구조를 파악하기 위해 gdb를 사용하여 intel방식으로 디스어셈블 한다.

어셈블리 분석
main+3 line을 보면 esp에 56Byte의 공간을 할당하는것을 알 수 있다.
main+17,20 line을 보면 fgets함수에 buf를 인자로 전달하기 위한 연산을 수행하고 있음을 알 수 있다.
따라서 buf의 주소는 ebp-56.
main+26 line을 보면 fgets로 입력을 받은 후 0x10만큼 esp를 증가시킨다. main+6에서의 sub와 여러 push로 인해 72Byte만큼 esp가 감소한걸 56Byte로 보정.
main+29 line을 보면 ebp-16 를 0xdeadbeef와 비교연산하고 있음을 알 수 있다.
소스를 보면 check가 포인팅하는 주소의 값을 0xdeadbeef와 비교연산 하고 있으므로 ebp-16는 check의 주소이다.
buf와 check사이의 거리는 56-16 = 40Byte buf의 크기가 20Byte이므로 dummy값이 20Byte가 있다.

ebp는 stack base pointer로 stack의 최하단을 가리키는 포인터로 스택이 소멸되기 전까지 값이 변하지 않는다.

buf와 check사이에 20Byte의 dummy가 있고, 스택에 할당된 총 공간이 56Byte이므로 메모리 구조는 아래와 같다.

참고
esp는 스택의 최상단을 가리키는 포인터로 push, pop연산에 따라 4Byte씩 값이 변한다.
sfp(Saved Frame Pointer)는 이전 함수의 EBP주소를 저장하고 있는 공간이다.

메모리구조
buffer(20Byte)
dummy(20Byte)
check(4Byte)
crap(4Byte)
dummy(8Byte)
sfp(4Byte)
ret(4Byte)


그림2
main+32 line을 보면 check와 비교를 위해 deadbeef라는 값이 이미 스택에 있다.
따라서 저곳의 주소를 알아내어 check에 덮어씌우면 shell을 얻을 수 있다.

0x080484b0에 0xbeaf3881이라는 값 발견
2바이트 거리에 0xdeadbeef값 발견


그림4
포인터 check에 deadbeef값이 들어있는 주소를 덮어씌우는 페이로드를 작성하여 공격

페이로드

(python -c “print ‘A’*40 + ‘\xb2\x84\x04\x08’“;cat) | ./attackme