fkillrra 2017. 12. 3. 17:39

해커스쿨 F.T.Z level 16 풀이를 해보았습니다.



로그인 후 힌트 파일을 확인하면..


이전 문제들과는 다르게 main() 외에 2개의 함수가 더 있는것을 확인할 수 있습니다.


void shell()에서 level 17의 권한을 걸고, system()를 이용하여 쉘을 띄어주는군요ㅎ


또 main()에서 


void (*call)() = printit;


이 부분을 중요하게 볼 필요가 있는데요.


c언어를 공부하신 분들은 아시겠지만 이는 함수 포인터라고 하는 녀석입니다.


말 그대로 함수를 담는 변수라고 할 수 있는데요.


아시다시피 포인터 변수는 주소값을 저장하는 변수입니다.


배열과 마찬가지로 printit 과 같이 함수의 이름을 넣어줌으로서 시작 주소를 call 이라는 함수 포인터에 넣어줍니다.


따라서 쉘을 띄우기 위해선..


fgets()의 버퍼오버 플로우 취약점을 이용해야합니다.


fgets()의 인자값을 보니 프로그래머가 지정해준 buf라는 이름의 배열의 크기는 20 byte이지만

총 48 byte를 입력값으로 받는다는 치명적인 실수가 존재하는 것을 확인 할 수 있습니다.


이 취약점을 이용하여 함수 포인터에 printit() 대신 shell() 의 시작주소로 덮어씌우면 쉘을 띄울 수 있을것 같습니다.


좀 더 정확하게 프로그램의 내부를 살펴보기 위해 gdb로 디스어셈블리를 해보았습니다.



역시 우리의 예상과는 다르게 총 버퍼가 56 byte 할당이 되어있는것을 확인 할 수 있는데요.


이 부분은 lea eax, [ebp-56] 부분을 보고 판단할 수 있었습니다.


또 쉘을 띄우기 위해서 필요한게 있었죠?


바로 printit()의 주소가 들어가는 위치와 shell()의 시작 주소입니다.


이는 mov DWORD PTR [ebp-16], 0x8048500 을 보면 mov라는 명령어로 ebp-16의 위치에 0x8048500를 넣어주는 것을 보고


printit()의 주소를 찾기 위해 disas printit 을 해보았습니다.



역시 printit()의 시작 주소였고, 이어서 shell()의 시작 주소를 찾기위해 disas shell 명령을 하였습니다.


이로서

 총 버퍼는 56 byte 

printit()가 들어가는 위치 ebp-16

shell()의 시작 주소는 0x080484d0


임을 알게되었네요.


따라서 페이로드를 작성해보면


(python -c 'print "A"*40+"shell()의 시작주소"';cat) | ./attackme


이와 같이 작성해주면 쉘을 띄울 수 있을것 같습니다.


/*A를 40개 입력해줌으로서 ebp-16의 위치까지 올라가고,

(ebp-16 / ebp-56 사이는 A로 덮어줍니다.)


그리고 ebp-16에 shell()의 시작 주소를 넣어줌으로서 쉘을 띄울것입니다.*/



공격에 성공하였고, id를 확인해보니 level17의 권한을 얻은 것이 보입니다.ㅎ


my-pass 를 입력해 level17의 password를 확인해보니


"king poetic" 이네요 :)


이상 level 16 풀이를 마칩니다.






반응형