hint는 다음과 같다.
이번에는 실행 인자가 아니라 fgets로 입력을 받는다.
Level 9와 비슷하게 길이 제한이 buf보다 25바이트나 크기 때문에 버퍼 오버플로우가 발생할 수 있다.
그리고 Level 13과 비슷한 check라는 변수가 있는데, 변수의 값이 0xdeadbeef여야 Level 14의 쉘을 준다.
또, 쓸모 없는 crap이라는 변수가 있는데, 일종의 dummy 취급인 듯 하다.
setreuid 호출과 함께 system 함수로 /bin/sh를 실행하기 때문에 쉘코드 사용은 하지 않아도 된다.
프로그램을 다시 컴파일하고 gdb로 뜯어 보자.
<main+3>에서 스택에 0x38(56바이트)만큼의 공간을 할당한다.
<main+31>에서 fgets 함수를 호출하며, 윗줄에 있는 인자들을 살펴 보자.
소스 코드에서는 buf, 45, stdin의 순서로 인자를 전달했고, 스택에서는 거꾸로이기 때문에 <main+25>의 0x2d(10진수로 45)를 기준으로 <main+27>의 [ebp-56]이 buf가 위치한 지점이라는 것을 알 수 있다.
<main+39>의 cmp에서 [ebp-16]과 0xdeadbeef를 비교하기 때문에 [ebp-16]이 check가 위치한 지점이라는 것도 알 수 있다.
따라서 스택의 구조는 다음과 같다.
check와 crap의 크기를 더해도 8바이트가 남기 때문에 소스 코드에서 check가 crap보다 나중에 선언된 것을 참고해 check가 [ebp-16]에 위치하고, crap과 dummy는 check 위에 위치하게 그렸다.
이 문제의 목표는 check를 변조하는 것이기 때문에 check 위에 위치하는 crap과 dummy는 풀이와 관련이 없으며 40바이트만큼 의미 없는 문자로 채우고 나머지 4바이트를 0xdeadbeef로 채우면 된다.
공격 명령어는 다음과 같다.
(python -c 'print "A"*40+"\xef\xbe\xad\xde"'; cat) | ./attackme
아무 것도 표시되지 않지만 id를 입력하면 Level 15의 쉘을 얻은 것을 볼 수 있다.