2017/01/12 - [Study :)/FTZ] - [해커스쿨] FTZ level17 풀이과정
#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) { if(FD_ISSET(fileno(stdin),&fds)) { 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); }
그동안의 hint와 길이적으로 차이가 나는 힌트이다... 뜨허억.
천천히 읽어보니 check 변수에 0xdeadbeef 가 있으면 shellout()함수를 실행시키면서 권한 상승이 일어난다. 그럼 끝 아닌가...? 일단.. 결론..
int main() { char string[100]; int check; int x = 0; int count = 0; fd_set fds; printf("Enter your command: "); fflush(stdout); while(1) { // } }
변수 선언 부분을 보니... string, check, x, count 순으로 선언되었다.
이말은 즉, string배열을 이용해서 check값에 값을 넣지 넣지 못한다는것.
왜냐고?
메모리에는 stack으로 쌓이기 때문에, string배열이 먼저 쌓이고, 그 위에 check변수가 쌓이게 된다.
그러기 때문에 아무리 string배열을 Buffer OverFlow 시켜도... check변수는 건들지도 못한다는 거지..
그래도 이해 안되는 사람들을 위해..
위 사진 처럼 메모리 구조가 되기 때문에, string에 아무리 값을 넣어도 SFP와 RET에 값을 넣는건 가능해도, 그 위로 값을 넣긴 힘들다. 따라서 다른 방법을 찾기 위해 코드를 좀 더 살펴보자.
while(1) { if(count >= 100) printf("what are you trying to do?\n"); if(check == 0xdeadbeef) shellout(); else { ////생략 } } }
무한 루프를 돌리고, count가 100이상이 되면 "what are you trying to do?"라는 문자를 출력한다.
check변수의 값이 "0xdeadbeef"면 shellout()함수를 실행시킨다.
if(check == 0xdeadbeef) shellout(); else{ FD_ZERO(&fds); //변수선언부에서 선언한 소켓구조체인 fds를 초기화한다. FD_SET(STDIN_FILENO,&fds); //소켓구조체에 STDIN 즉, 표준출력 넘버를 셋팅한다. if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1){ /* select는 여러개의 fd구조체를 모니터링하는 함수이다. FD_SETSIZE는 1024로 1024개의 소켓구조체를 모니터링 하며, 다른 인자들은 NULL로 넣고 두번째 인자인 입력에 &fds를 주어 fds구조체에 '입력'되는 이벤트에만 true값이 셋되어 fi문 안으로 들어간다. 즉, 표준입력을 입력해야 if문 안으로 들어갈 수 있다.*/ if(FD_ISSET(fileno(stdin),&fds)){ /* FD_ISSET은 fds구조체가 stdin으로 전달되는 적절한 fd정보를 가지고 있는지 확인하는 부분이다. 즉, fds에 위에서 FD_SET으로 설정한 파일디스크립터 넘버가 저장되어 있는지 검사하는 부분이다.*/ read(fileno(stdin),&x,1); //표준출력으로부터 1Byte만큼 읽어온다. switch(x) { ..... } } } } }
자, 이제 switch 부분을 살펴보자.
switch(x) { case '\r': case '\n': printf("\a"); break; case 0x08: count--; printf("\b \b"); break; default: string[count] = x; count++; break; }
표준출력으로 가져온 1Byte값이 '\r', '\n'이면 비프음('\a') 울리고 종료된다.
1Byte값이 "0x08"인 경우 count값이 감소된다.
dafault값으로 string[count]값에 x값을 넣고, count값을 증가시킨다.
여기서 중요한 부분은 count값을 감소시킨다는 것이다.
앞에서 string배열에 값을 넣어서 check값을 변경 할 수 없다고 하였는데, string[-4]라는 값을 넣으면 메모리상에서 위로(낮은주소) 올라 갈 수 있지 않을까?
check값은 string배열 바로 위에 있으므로, 4바이트 올라가서 값을 "0xdeadbeef"를 넣으면 될 것 같다.
즉, "0x08"값을 4번 입력하고 "0xdeadbeef"값을 넣어보자.