이번 힌트는 너무 길어서 한 화면에 담을 수 없어서 직접 복사해 왔다.
#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)
printf("what are you trying to do?\n");
if(check == 0xdeadbeef)
shellout();
else
{
FD_ZERO(&fds);
FD_SET(STDIN_FILENO,&fds);
if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
{
read(fileno(stdin),&x,1);
switch(x)
{
case '\r':
case '\n':
printf("\a");
break;
case 0x08:
count--;
printf("\b \b");
break;
default:
string[count] = x;
count++;
break;
}
}
}
}
}
void shellout(void)
{
setreuid(3099,3099);
execl("/bin/sh","sh",NULL);
}
엄청나게 복잡해 보이는 코드들이다.
하지만 코드의 대부분이 오류확인하는 부분이다.
가장 이해하기 어려웠던 if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)의 경우
fds가 SET되어있는지 확인하는 구문으로 결국 위에서 FD_SET(STDIN_FILENO,&fds);을 해 줬으므로 return값은 1이상일 것이다.
결국 if문은 당연히 참이 나오는 것이고
이 코드가 관건이다.
read(fileno(stdin),&x,1);은 표준입력으로 x에다 1글자씩 넣는다는 뜻이다.
스택 구조상 string의 버퍼가 check보다 위쪽에 위치하므로 check를 덮어쓰기 위해서는 주소를 거슬러 올라가야한다.
마침 switch에 count--;과 string[count]=x;가 보인다.
즉 count--를 4번 실행하고 0xdeadbeef를 넣어주면 check가 덮어쓰여지게 될 것이다.
count--를 하기위해선 x가 0x08이어야 하므로
\x08을 4번 실행하고 0xdeadbeef를 리틀엔디안으로 넣어주면 쉘을 획득할 수 있다.