level 16
buffer에 48글자 입력받고, 스택주소 사용 X, LD_PRELOAD 사용 X, 인자 한 개 X
제약 조건엔 별로 없네요.
FPO를 쓸까해서 봤더니 사용자 정의 함수가 없기 때문에 불가능하고,
FS는 당연히 아니고, RET Sled를 쓸까했더니 딱 RET 덮을 때까지만 받기 때문에 안되네요.
문제 위를 보면 FEBP라고 나와있습니다.
이는 Fake EBP로, EBP를 속인다는 뜻이죠.
원리는 이렇습니다.
함수의 에필로그는 leave와 ret으로 이루어지죠.
mov esp, ebp
pop ebp leave
--------------------------
pop eip ret
jmp eip
여기서 leave는 SFP에 지정된 주소를 사용합니다.
만약, SFP에 쉘코드 주소가 있다면 어떻게 될까요?
이 점을 이용하는 겁니다.
첫번째 leave를 만나게 되면 정상적인 실행 흐름과 같이 esp를 ebp있는 곳으로 당깁니다.
하지만, SFP에 있는 값을 가지고 POP ebp를 실행하게 되면 ebp는 쉘코드가 있는 곳으로 이동합니다.
이 다음으로 RET의 값 즉, leave의 주소를 POP해서 eip가 leave로 이동하고, JMP를 실행하면 다시 leave가 실행됩니다.
두번째 leave에서 MOV esp, ebp로 인해 esp 또한 쉘코드가 있는 주소로 이동하게 되고, POP ebp에 의해 esp와 ebp가 +4 됩니다.
esp가 쉘코드 위에 올라갔다면 쉘코드가 실행되겠죠?
이 다음으로 RET가 실행되면서 쉘코드가 실행되는 원리입니다.
즉, SFP에는 buffer의 시작주소 -4, RET에는 leave의 주소가 들어가야합니다.
그리고 중요한 점은 쉘코드를 직접 작성해 사용하는 경우, buffer의 첫 시작 4바이트는 쉘코드의 시작 주소를 가리켜야 합니다.
이 문제를 푸는데에는 두 가지 방법이 있습니다.
첫째, 공유 라이브러리 내에 있는 system 함수와 exit 함수, "/bin/sh\x00" 문자열을 활용한다.
둘째, 직접 쉘코드를 작성한다.
두 가지 모두로 풀어보겠습니다.
직접 쉘코드 작성하기
먼저, leave의 위치를 알아봅시다.
0x80484df라고 하네요!
buffer의 위치를 알아볼까요?
이렇게 했습니다.
나왔네요!
buffer - 4는 0xbffffa8c이고, 쉘코드의 시작 주소는 0xbffffa94네요.
buffer의 첫 4바이트는 0xbffffa94로 채워야겠습니다.
또한, SFP는 0xbffffa8c로 채워보겠습니다.
payload를 작성하겠습니다.
"\x94\xfa\xff\xbf" + "\x90"*11 + "~~"(ShellCode) + "\x8c\xfa\xff\xbf" + "\xdf\x84\x04\x08"
성공!
공유 라이브러리 내 함수 이용하기
함수의 위치를 알아내고,
위 소스코드를 컴파일한 뒤 실행해 문자열의 위치를 알아냅니다.
이후에 buffer의 위치도 알아냅니다.
0xbffffa8c가 buffer - 4의 주소네요.
payload는 이렇습니다.
함수를 사용하는 경우에는 쉘코드가 없으니 buffer의 첫 4바이트에 바로 함수의 주소를 입력합니다.
"\xe0\x8a\x05\x40" + "\xe0\x91\x03\x40" + "\xf9\xbf\x0f\x40" + "\x90"*28 + "\x8c\xfa\xff\xbf" + "\xdf\x84\x04\x08"
굿굿!