[어설픈] 해커스쿨 FTZ Level 18 문제풀이(late upload)

2019. 4. 16. 22:15System Hacking/FTZ

안녕하세요!

올린다는 걸 깜빡하고

이제서야 늦게 올립니다!


오늘은 FTZ level18번 문제를 

풀어보겠습니다!


main에 변수들도 많이 선언되어있고!

코드도 상당히 기네요!


우선 변수들 먼저 볼까요?

char형의 string이 제일 먼저 선언되어있네요!

그 다음에 check 변수, x변수, count 변수, fd_set 구조체의 fds 하나

많이도 선언되어 있네요!


다음으로, 밑에 이제 함수들을 보면!

printf로 command를 입력하라는 문장을 출력하고

stdout과 관련된(저희가 입력할 때 쓰는 stdin과 반대) 

출력 버퍼를 fflush함수로 청소하네요!

fflush는 버퍼를 클리닝, 초기화하는 함수입니다!


그리고 while(1)이니 무한 반복인 상태가 되는데

어떤 함수들이 계속 실행 되는지 볼까요?

 

제일 먼저 if문으로 조건을 확인하네요!

위에서 선언된 count변수의 값을 100보다 크거나 같은지 확인하네요!

그리고 100보다 크거나 같으면

 

"자네 지금 뭐하려고 하는건가?"


라는 문장을 출력하네요!

근데 exit(0); 함수와 같은건 안보이네요!

count가 100보다 커도 

계속 실행시켜주네요 ㅋㅋㅋ


그리고 위에서 선언한 check 변수의 값도

확인하네요!

0xdeadbeef값과 같으면!

shell이 실행됩니다! (=문제 풀림)


근데 check값이 0xdeadbeef값이 아니면

막 이상한 함수가 실행돼요!

fd_zero랑 

fd_set랑 이것저것!


저도 구글링해봤는데 fd_zero는 뭐 

zero값으로 초기화 할 때 쓰는거고

fd_set은 구조체 같은거여서 

저도 이해하려했는데 제대로는 이해못했습니다!

통신할 때 쓰이는 것 같았습니다!


근데 밑에 저희가 이해할 수 있는 부분으로 가면

(fd 함수부분은 setting해주기 위해 존재하는 것으로 이해하고)

read( )함수가 있죠!


read(fd 값, 읽은 값 여기에 저장, 이 크기만큼 읽을꺼야)


stdin은 저희가 입력할 때 쓰는 것으로

리눅스는 모든 게 파일로 되어 있기에

stdin의 파일숫자면 0이지 않을까 싶네요

항상 read함수로 표준입력 받을 때

read(0, ,) 이런 꼴로 썼잖아요!


정리하자면 read함수가 1byte를 표준입력 받아서 변수 x에 저장한다!


그리고 switch( x )문이 있네요!

x에 들어간 값에 따라서 다르게 동작하는 거죠!

여기서 별로 특이점이 없어 보이지만!

수상한 구간이 있습니다!


바로 case 0x08: 

count --; 

부분입니다!


왜냐구요? 

count가 줄어들 때마다 문제가 발생하거든요!


어디서 발생하냐구요?

default에서 count가 어떻게 쓰이는지 확인해보세요!


위에 3가지 case가 아니면

기본 설정으로 (디폴트로)

string 배열에 값이 저장돼요!

string 배열은 저희가 위에서 선언한

string[100]을 말하는 거에요!


즉 string[0] ----->string[99]까지 

계속 입력이 되는거죠!


결론부터 말씀드리자면 count는 

string 배열의 index로써 

string[-1] string[-2] 이런 식으로 된다면

의도치 않은 것들을 할 수 있습니다!


저희는 밑에 있는 shellout( )함수를 실행시키고 싶은데,

일반적인 bof로는 이 문제를 풀 수 없죠!

왜냐하면 stack에서


check

string[100]

ebp

ret_addr


뭐 이런 식으로 있기에 check은 overflow를 해봤자

영향을 안 받기 때문이죠!


그래서 이번 문제는 0x08을 입력했을 때

index가 -1 -2 -3 -4  이렇게 줄어들다가

check 변수의 위치를 찾아서


  check 변수의 위치에 0xdeadbeef를 입력하면

shellout( )함수가 call 되면서 문제가 풀리게 되는 것 입니다!


그러면 저희가 뭘 알아야 하죠?

저희가 알고 있는 건 strings 배열의 index인 count를

계속 빼야 check 변수에 언젠가 도달하는 것을 알고 있죠? 


근데 정확히 얼마나 빼야 check에 도달하는 지 알아야 합니다!

 

그래서 아래와 같이 gdb를 통해서 

check의 위치와 count의 위치를 통해서

string[100]의 위치를 유추 할 수 있습니다!


cmpl $0x63,0xffffff90(%ebp) 부분이 

count가 100보다 크거나 같은 지 확인하는 부분입니다!

(0x63 = 99)


그리고 밑에 보시면 

cmpl $0xdeadbeef,0xffffff98(%ebp) 부분이

check이 deadbeef과 같은 지 확인하는 부분입니다!



여기서 알 수 있는 것!

count = 0xffffff90(%ebp)

check = 0xffffff98(%ebp)


당연하죠! 

stack 상에서 check이 count보다 밑에 있으니까요!

%ebp+0xffffff90 < $ebp+0xffffff98



그러면 stack상에서 제일 밑에 있는(ebp,ret 빼고요) strings는

제일 큰 값을 가지고 있겠죠? 


그래서 찾아보니 0xffffff9c(%ebp)가 있네요!

얘가 제일 큰 놈이니 

얘가 strings의 시작 주소겠네요!




그래서 보기 좋게 스택 상황과

check변수와 string[100]과의 거리를 구해보았습니다!


%ebp+0xffffff98 = check

%ebp+0xffffff9c = string의 시작 주소


둘이 빼면 4밖에 차이 안나죠? (4byte) 




그러면 4번 올라가면 check 변수의 주소가 되겠네요!

그래서 0x08을 4번 넣고!

그러면 index가 -4가 되겠죠?


그 뒤로 0xdeadbeef를 

little endian으로 넣으면!


아래와 같이 문제가 풀리게됩니다!

고생하셨습니다!