FTZ level18 Write-Up
FTZ level19 풀어보자.
hint를 보면 코드의 길이가 이전 문제들보다 훨씬 길어지고 복잡해졌다.
#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) // count 변수가 100 이상이면 출력
printf("what are you trying to do?\n");
if(check == 0xdeadbeef) // check 변수 값이 0xdeadbeef면 shellout() 함수 호출
shellout();
else {
FD_ZERO(&fds); // fds 변수를 0x00 으로 초기화
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': // enter 입력
printf("\a"); // \a 출력
break;
case 0x08: // 0x08 입력
count--; // count 값이 1 감소
printf("\b \b"); // backspace
break;
default: // 나머지 문자 입력
string[count] = x; // string 배열에 x의 값을 저장
count++; // count 값이 1 증가
break;
}
}
}
}
}
}
void shellout(void) { // level19 계정의 쉘 실행
setreuid(3099,3099);
execl("/bin/sh","sh",NULL);
}
코드가 너무 길어서 따로 작성하였다. C 코드 상에서 알 수 있는 것은 check 변수의 값이 0xdeadbeef 이면 shellout 함수를 호출하여 level19 계정의 쉘을 실행한다. 그리고 변수 x에 입력된 값이 저장되고 어떤 값인지에 따라 실행되는 코드가 달라지는데, 입력된 값이 '\r', '\n', 0x08이 아니면 string 배열에 값이 저장된다.
그런데 string 변수는 check 변수보다 높은 주소에 있기 때문에 string 변수의 버퍼를 오버플로우 시켜도 check 변수의 값을 변조하는 것은 불가능하다. 따라서 입력된 값이 0x00일 때 count의 값이 감소하므로 이 점을 이용해야 한다.
먼저 attackme 파일을 /home/level18/tmp/ 디렉터리에 복사한 후 gdb를 통해 main 함수의 어셈블리어 코드를 보면 이전 문제들보다 훨씬 길어진 것을 알 수 있다. 또한 변수도 많아졌기 때문에 각 변수들의 정확한 위치와 크기를 알기 어렵다.
따라서 hint의 코드를 조금 수정해서 각 변수들의 주소를 출력해볼 것이다.
hint의 코드에서 변수들이 선언된 후 각 변수들의 주소와 fds 변수의 크기를 출력해주도록 수정해주면 된다.
그런 다음 실행을 시키면 변수들의 주소와 fds 변수의 크기를 알 수 있다. 이를 통해 형성된 스택 구조는 다음과 같다.
[사진 2]를 보면 스택 공간을 0x100 만큼 확보한다. 256bytes만큼 확보하는 것이다. 그런데 가장 높은 string 변수의 주소를 가장 낮은 fds 변수의 주소로 빼면 0x90 즉, 144bytes라는 결과가 나온다.
이 결과에 string 변수의 크기인 100bytes를 더해도 244bytes로 확보한 스택 공간보다 12bytes 부족하다. 따라서 string 변수와 ebp 사이에 12bytes의 dummy가 있는 것이다.
그리고 5개 변수의 이웃끼리 차이를 계산해보면 실제 변수의 크기보다 공간이 더 확보된 변수가 있다. 바로 fds 변수이다. count 변수의 주소에서 fds 변수의 주소를 빼면 0x84 즉, 132bytes인데 fds 변수의 크기는 128bytes이다. 따라서 fds 변수와 count 변수 사이에 4bytes의 dummy가 있는 것이다.
그 다음 C 코드 상에서 확인했던 0x08 값을 입력 시 count가 감소되는 것을 이용해야 한다. C 코드 상의 switch 문에 따라서 입력된 값이 '\r', '\n', 0x08이 아니면 string[0] 부터 저장될 것이다. string 변수보다 낮은 주소에는 check 변수가 존재하는데, count 값을 감소시켜서 음수로 만들면 check 변수를 가리키도록 만들 수 있다.
위와 같이 0x08 값을 네 번 입력해주면 count 값은 -4가 될 것이고 check 변수의 첫 번째 byte를 가리킬 것이다. 그 다음 0xdeadbeef 값을 입력해 주면 level19 계정의 쉘이 실행되고 my-pass 명령어로 level19 계정의 password를 확인할 수 있다.