문제분석 & 풀이
코드 분석
1. crap, *check, buf[]를 선언했다.
2. fgets()를 이용하여 사용자에게 45바이트를 입력받는다.
3. (*check == 0xdeadbeef) 인 경우에 level16의 셸을 얻는다.
이전 문제와 비교하면 1가지가 달라졌다.
level14에서는 check 변수가 int 타입 변수였지만, 이번에는 포인터 변수로 선언됐다.
무슨 차이인지 짧게 설명하도록하겠다.
[그림]은 level14 때 사용한 attackme의 스택 그림이다.
check 변수가 int형이고 check==0xdeadbeef 여야 한다. 따라서 노란색 박스 영역에 0xdeadbeef 값이 들어가야 level14를 클리어 할 수 있었다.
그러나 이번 문제는 check 변수가 포인터 변수이다. 따라서 위 그림과 같이 노란색 박스 영역에 들어간 값이 가리키는 주소의 값을 가리키게 된다. (위 그림은 예제로 노란색 박스가 빨간색 박스를 가리킨다.)
따라서 위 예제를 기준으로 노란색 영역이 아닌 빨간색 영역에 들어간 값이 0xdeadbeef인지 확인하는 것이 level14와 다른 level15의 핵심이다.
해결 방법은 간단하다.
페이로드 구성시 buf의 앞부분에 0xdeadbeef를 삽입하고 *check에 buf의 주소를 저장하면 문제가 해결된다.
본격적으로 페이로드 구성을 해보자.
페이로드 구성을 위해 attackme를 tmp 폴더로 복사하여 GDB로 분석했다.
1. buf, *check, crap을 위한 0x38(56) 만큼의 공간을 할당한다.
2. [ebp-16] 값을 eax에 넣는다.
3. eax에 들어간 주소 값에 위치한 데이터 값을 0xdeadbeef 와 비교하고 같으면 level16의 셸을 부여한다.
분석한 것을 토대로 스택 구조를 구성하면 다음과 같다.
[ebp-16]를 0xdeadbeef 와 비교한다. 따라서 [ebp-16] 주소가 *check의 주소임을 알 수 있다.
우리는 buf부터 시작해서 *check에 0xdeadbeef가 위치한 주소 값을 넣는 페이로드를 완성해야한다.
따라서 0xdeadbeef 값의 주소도 구해야한다.
asm 코드 일부분을 보면 0xdeadbeef를 직접적으로 언급하면서 값을 비교하고 있다.
즉, 0xdeadbeef 값이 0x080484b0 주소 근처에 저장됐을 것이다.
위와 같이 0x080484b0 근처의 값들을 확인해보니, 0xdeadbeef가 있는 것을 확인 할 수 있다.
이제 0xdeadbeef의 정확한 주소를 구해보자.
확인해보면, 2바이트 뒤인 0x080484b2가 deadbeef의 주소임을 확인 할 수 있다.
페이로드를 만들어보자.
"A"*40+"\xb2\x84\x04\x08"
(python -c 'print "A"*40+"\xb2\x84\x04\x08"';cat) | ./attackme
완성한 페이로드를 실행해보자.