FTZ Level 9

BrainInAVet·2021년 8월 8일
0

hint는 다음과 같다.

본격적으로 버퍼 오버플로우 문제가 나온다.
먼저 개념에 대해 설명한다.

버퍼 오버플로우란, 프로그램의 정해진 버퍼 사용량을 넘치게 만들어 의도되지 않은 동작을 일으키는 기법이다. 컴퓨터 구조와 관련된 문제이기 때문에 어셈블리어 등의 로우레벨 지식이 필요하다.

먼저 코드를 해석해 보자.

fgets 함수를 통해 40만큼의 데이터를 표준 입력으로 받아 buf라는 변수에 집어넣고 있다.
buf 배열 길이는 10인데 말이다. 여기서 버퍼 오버플로우 취약점이 생긴다.

그 아래에는 if문이 있는데, strncmp를 통해 문자열을 비교하고 있다. strncmp 함수는 문자열이 같으면 0, 다르면 양수 또는 음수를 출력하는 함수이다.
즉, buf2의 내용과 go를 비교하는 것이 되고, 조건이 참이라면 다음 레벨의 쉘을 획득하게 된다.

보통 버퍼 오버플로우에서는 스택 개념을 도입한다.
스택은 한 쪽에만 구멍이 있는 일반적인 통을 생각하면 된다.
먼저 들어간 것이 가장 나중에 나오고, 나중에 들어간 것이 가장 먼저 나오는 구조이다.
여기서는 buf2가 buf보다 먼저 선언되었으므로 buf2가 buf보다 위에 위치할 것이며, buf를 오버플로우시키면 buf2의 공간을 침범하게 될 것이다.

그 공간이 얼마나 되는 지를 알아야 하기 때문에, gdb를 통해 역어셈블을 진행한다.

/usr/bin/bof를 그대로는 쓰지 못한다. 권한이 없기 때문이다. 그러므로 힌트에 나온 코드로 다시 컴파일한 후 진행했다.

가장 먼저 ebp를 push한 후에 esp의 값을 ebp에 옮기고 있다. esp와 ebp는 스택 포인터라고 불리며, esp는 스택의 가장 위의 값을, ebp는 가장 아래의 값을 가리킨다. 값을 옮기는 과정을 통해 esp가 가리키는 값을 가장 스택의 가장 아랫 부분으로 만들어 기본적인 프레임을 형성한다.

그 이후, sub를 통해 스택에 0x28만큼의 공간을 할당한다.

<main+47>을 보면 fgets 함수를 호출하는 것을 볼 수 있는데, 소스 코드에서는 fgets의 인자가 3개였다. 따라서 <main+47>보다 앞에서 push된 것들을 보면 된다.

<main+46>, <main+41>, <main+35>로 총 3개이고, 소스 코드에서 보았던 buf, 40, stdin의 순서대로 인자가 들어가려면 스택에서는 거꾸로 배열되어야 한다. 따라서 0x8049698, 0x28, eax 순으로 들어가게 된다.

그러나 <main+43>에서 [ebp-40]의 주소를 eax에 넣었기 때문에 가장 마지막에 들어갈 것은 [ebp-40]의 주소가 되겠다.

또한, [ebp-40]은 fgets에 들어간 인자 3개 중 buf에 해당되기 때문에 buf의 지점이 [ebp-40]이라는 것도 알 수 있다.

<main+69>에서는 strncmp를 호출한다. 인자는 buf2, "go", 2였다.
따라서 스택에는 2, "go", buf2의 순서로 들어가게 될 것이다.
<main+69>보다 앞에서 push된 것들은 eax, 0x804856a, 0x2가 있다.
거꾸로 배열되므로 0x2, 0x804856a, eax의 순으로 스택에 들어갈 것이다.
여기서도 eax에는 [ebp-24]의 주소를 넣었기 때문에 buf2의 지점은 [ebp-24]라는 것을 알 수 있다.

지금까지 대략적으로 알아낸 스택의 구조를 그림으로 그리면 다음과 같다.

SFPRET은 각각 스택 베이스, 반환 주소를 뜻한다.
그림을 보면, 소스 코드에서 buf2 다음에 buf를 선언한 것의 반대 순서로 배열된 것을 알 수 있고, ebp의 크기가 처음에 0x28(10진수로 40)만큼의 공간을 할당한 것과 같음을 알 수 있다.
즉, 스택은 프로그램 소스 코드를 반대로 읽으면 쉽게 이해할 수 있다.

[ebp-40]과 [ebp-24] 사이의 간격은 16바이트이고, 이 16바이트 사이의 공간에는 buf가 10바이트를 차지하며, 남은 공간은 dummy 값이 들어가게 된다. [ebp-24]부터도 똑같이 buf2가 10바이트를 차지하고, 남은 14바이트를 dummy 값으로 채우게 된다.

소스 코드에서는 buf2와 go를 비교해 같으면 쉘을 획득할 수 있다고 했다.
그렇다면, [ebp-40]부터 [ebp-24]까지의 공간을 모두 의미 없는 문자로 채워버리고 마지막에 go를 넣으면 go만이 [ebp-24]를 넘어가 buf2에 들어갈 것이다.

프로그램을 다시 실행해 a 16자 + go를 입력했다.

level10의 쉘을 획득했다.

0개의 댓글