FTZ Level 18
hint의 코드가 상당히 길고 어려워서 아래에 따로 옮기고 주석을 붙였다.
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main()
{
char string[100]; // string 변수 100바이트
int check; // check 4바이트
int x = 0;
int count = 0;
fd_set fds; // File Descripter 구조체 128바이트
printf("Enter your command: ");
fflush(stdout); // stdout 버퍼 비우기
while(1) // 무한루프
{
if(count >= 100) printf("what are you trying to do?\n"); // count가 100보다 작으면 질문
if(check == 0xdeadbeef) shellout(); // check가 0xdeadbeef면 Level 19 쉘 부여
else // check가 0xdeadbeef가 아니면
{
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: // 0x08 입력 시 count 감소, 글자 지우기
count--;
printf("\b \b");
break;
default: // 그 외에는 string에 저장, count 증가
string[count] = x;
count++;
break;
}
}
}
}
}
}
void shellout(void)
{
setreuid(3099,3099);
execl("/bin/sh","sh",NULL);
}
해석이 어렵지만, 초반의 변수 선언 부분을 보면 이전까지와는 다르게 check가 string보다 나중에 선언되고 있기 때문에 check를 일반적인 방법으로 변조할 수 없다. 그러나 switch문의 default에서 count를 음수로 만든다면 string보다 앞에 있는 메모리에 접근할 수 있고, 결과적으로 check를 변조할 수 있게 된다.
gdb로 까보자.
main 함수가 길기 때문에 첫 페이지 먼저 살펴 보자.
<main+3>에서 0x100(256바이트)만큼의 공간을 스택에 할당한다.
<main+91>을 보면 [ebp-104]와 0xdeadbeef를 비교하고 있다. 즉 check가 위치한 지점은 [ebp-104]라는 것을 알 수 있다.
첫 페이지에서는 string이 위치한 지점이 나오지 않는 듯 하니 switch문이 나오는 부분까지 넘긴다.
<main+444>에서 0x8이 나왔기 때문에 switch문의 3번째 케이스라는 것을 알 수 있고, 따라서 <main+455>가 default가 되겠다.
다음 페이지를 살펴 보자.
jmp를 통해 0x8048770으로 이동하는 부분이 많이 보인다. 이것들이 break문이다.
또한 printf 함수가 여러 번 호출되고 있는 것이 보이는데, default에서는 printf 함수가 호출되지 않았으므로 <main+489>의 아랫쪽을 살펴 보자.
위에서 말한 break문의 위치와 printf 함수가 호출되는 위치를 고려했을 때 default의 내용은 <main+499>에서 <main+538>까지라는 것을 알 수 있다.
위에서 말한 구간에서 [ebp-xxx]를 모두 살펴 보자. 이 문제에서는 check가 string보다 나중에 선언되었다. 따라서 string은 check가 위치한 지점인 [ebp-104]보다 더 ebp에 가까운 지점에 위치해야 하는데, 여기서는 [ebp-100]밖에 없다. 무엇보다 check가 4바이트이기 때문에 4바이트 차이나는 지점이 [ebp-100] 하나 뿐이다. 따라서 string이 위치한 지점은 [ebp-100]이다.
공격법을 생각해 보자.
0x08이 입력되면 count가 감소해 string 인덱스가 1바이트씩 줄어든다.
check는 string보다 4바이트 앞에 위치하기 때문에 count가 -4가 되면 default에서 string[-4]를 참조하게 되므로 표준 입력으로 들어가는 x의 값이 check의 자리를 채우게 될 것이다.
공격 명령어는 다음과 같다.
(python -c 'print "\x08"*4+"\xef\xbe\xad\xde"'; cat) | ./attackme
질문이 그대로 나오는데, 당황하지 말고 id를 입력하면 Level 19의 쉘을 얻은 것을 알 수 있다.
Author And Source
이 문제에 관하여(FTZ Level 18), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@bin3635/FTZ-Level-18저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)