코드가 깁니다.
엄청 깁니다.
힌트를 보면 함수를 순차적으로 call 하랍니다.
함수를 살펴보면
MO, YUT, GUL, GYE, DO는
check를 검사하고 각 값이 맞으면 "welcome to~"를 출력하고
check 값을 바꿉니다.
check 값이 틀린 경우는 프로그램을 종료합니다.
MO에서는 parameter로 string을 받고
해당 문자열을 parameter로 사용해 system()을 call 합니다.
check값은 전역변수로 0으로 초기화 되어있습니다.
main 함수를 살펴보겠습니다.
argv[1]에 \x40이 있으면 메시지를 출력하고 종료합니다.
ret 값을 검사해서 DO 주소가 아닌경우 DO를 사랑하라고 출력하고 종료합니다.
DO를 제일 먼저 call 해야됩니다.
strcpy()를 통해 overflow가 발생하고
stack을 초기화합니다.
하지만 ret 뒤쪽으로 100-byte는 그대로 놔둡니다.
환경변수도 다 지워줍니다.
풀이는 간단합니다.
힌트 그대로 순차적으로 DO, GYE, GUL, YUT, MO를 call 하면 됩니다.
ret부터 각 함수의 주소를 덮어씌워주면 순차적으로 call이 됩니다.
각 함수의 주소를 찾겠습니다.
각 함수의 주소는 위와 같습니다.
순차적으로 call이 되는지 확인해보겠습니다.
정상적으로 call이 됩니다.
이제 MO에서 system으로 /bin/sh를 실행하도록만 하면 됩니다.
libc.so.6의 "/bin/sh"를 사용하고 싶지만
\x40이 있으면 프로그램이 종료됩니다.
buffer 끝에 "/bin/sh"를 넣고 해당 주소를 사용하겠습니다.
buffer의 시작 주소를 찾고
입력한 byte 수 만큼 더하면 "/bin/sh"의 주소를 구할 수 있습니다.
소스코드를 바꿔서 buffer 주소를 출력하도록 합니다.
buffer의 시작주소는 0xbffffa80입니다.
MO에서는 parameter로 system()에서 사용할 string을 넘겨받습니다.
MO를 call하는 ret에서 MO 종료 후 return 할 ret 위치 다음에 "/bin/sh" 주소를 넣으면 됩니다.
Stack 구조
Low addr
buffer (40-byte) |
sfp (4-byte)
|
ret (to DO)
|
ret (to GYE)
|
ret (to GUL) |
ret (to YUT)
|
ret (to MO)
|
ret (4-byte) |
&"/bin/sh" (4-byte) |
"/bin/sh" |
High addr
"/bin/sh"의 주소는 buffer 시작주소인 0xbffffa80에서 72-byte만큼 떨어져있습니다.
"/bin/sh"의 주소는 0xbffffac8입니다.
공격해봅니다.
안됩니다.
아왜
실행주소 때문일겁니다.
"/bin/sh" 주소를 16-byte 당겨줍니다.
"/bin/sh"의 주소는 0xbffffab8이 됩니다.
다시 공격해봅니다.
공격에 성공했습니다.