쓸데없는 얘기 없이 바로 풀이로 들어가겠습니다.
아마(?) 풀이가 이미 드림핵에 나와있는 걸로 기억하기 때문에
풀이
문제는 다음과 같습니다.
환경은 다음과 같습니다. 리눅스에서 checksec 명령어 하면 뜨는 화면
32bit 파일이고 보호기법이 적용되지 않았습니다.(-fno-stack-protector)
cat basic_exploitation_000.c를 통해 소스코드를 읽어봅니다.
setvbuf에 관해선 이전 글을 참고하시면 좋습니다.
해당 소스코드는 buf라는 배열에 0x80바이트를 할당하고 buf의 주소값을 출력합니다.
(실행해보면 주소값이 계속 바뀌는 것을 알 수 있는데 ASLR이 적용되어 있기 때문입니다.)
이후 buf에 최대 141byte만큼 문자열을 입력받습니다. 141은 그닥 중요하지 않는데 0x80 < 141이므로 bof를 막기엔 그닥...
추가로 initialize함수에서 시작 후 30초가 지나면 프로그램을 종료하는 듯 합니다.
payload를 작성하기 위해서 목표를 세워보겠습니다. (gdb로 뜯기 전에)
우선 메모리에
buf | sfp | ret
이런 식으로 놓여있을텐데 buf에 오버플로우를 일으켜서 ret를 변조하는 것입니다.
여기서 RET를 무엇으로 변조하냐하면 buf의 시작주소로 변조합니다. 그리고 buf에 shellcode를 삽입하는 것이죠.
이 문제에서는 쉘을 실행시키는 get_shell같은 함수가 없기 때문입니다.
이에 관해 짧은 설명으로 canary 실습 마지막 부분에 적혀있는데,
코스에서는 스택 버퍼에 코드를 주입하여 실행했지만, 이 외에도 전역으로 선언된 버퍼나, 힙 버퍼 등에도 셸코드를 주입하여 실행시킬 수 있습니다. 특히, 전역 버퍼는 PIE가 적용되지 않으면 주소가 고정되기 때문에, 버퍼의 주소를 구하는 별도의 과정 없이도 해당 버퍼로 실행 흐름을 옮길 수 있습니다.
이 공격 기법은 다음 조건이 만족되면 사용할 수 있습니다.
코드를 삽입할 수 있는 임의의 버퍼가 있을 때, 해당 버퍼의 주소를 알거나, 구할 수 있다.실행 흐름을 옮길 수 있다. ← 스택 버퍼 오버플로우도 여기 포함됩니다.
컴퓨터 과학에서는 임의의 코드를 실행하는 것을 Arbitrary Code Execution(임의 코드 실행;ACE)라고 부릅니다. 그리고 원격 서버를 대상으로 ACE를 수행하는 것을 Remote Code Execution(원격 코드 실행; RCE)라고 부릅니다. 이번 코스에서 배운 Return to shellcode는 RCE 기법이라고 할 수 있습니다.
RCE는 서버를 대상으로 한 공격들 중, 매우 파괴적인 공격에 속하며, 컴퓨터 과학자들은 서버에서 RCE의 위험을 줄이기 위해 여러 보호 기법을 고안했습니다. 대표적으로 코드 섹션 외의 모든 섹션에 실행 권한을 없애는 NX(Not eXecutable)가 있으며, 바이너리를 실행할 때마다 임의의 주소에 스택과 힙을 할당하는 ASLR(Address Space Layout Randomization)이 있습니다.
네 드림핵에서 그렇다고 하네요.. 아 이제야 알게된거지만 문제 아래에 reference 부분은 꼭 읽어보고 문제를 푸는게 좋을 것 같습니다.
gdb로 실행파일을 뜯어보겠습니다.
음. ebp-0x80에 buf가 위치한다는 것을 느낌적으로 알 수 있겠습니다.
ebp에는 sfp 가 4바이트 존재하겠죠(32bit 이므로 4바이트)
그리고 ebp+4에는 RET가 존재할 것입니다. 이 RET를 실행할 때 나온 buf 의 주소값으로 덮어씌우면 됩니다.
Exploit
익스플로잇 한 코드는 다음과 같습니다.
RET를 변조할 값을 ret라는 변수에 대입합니다. recvuntil로 "buf = (" 부분까지를 무시하고 이후에 있는 buf의 주소를 10byte(10자) 받습니다.
그러면 0x~~~ 이런 식의 주소가 들어오는데 이를 int(숫자, 진법)을 사용해서 16진수로 바꾸어줍니다. 이 숫자는 이후 p32함수를 통해 형식에 잘 맞게 들어갈 예정입니다.
buf에 대입할 코드 앞부분에는 쉘코드를 입력합니다.(쉘코드는 그냥 인터넷에서 가져왔습니다) buf로 이동해서 쉘코드가 실행되도록 말이죠.
그리고 \x90을 나머지로 채우는데, 132인 이유는 16*8 = 128에 sfp 4byte 더해서 132입니다.
\x90인 이유는 제가 NOP sled를 찾아보면 되겠는데, 나머지를 그냥 문자열로 채우면 에러날거 같아서 어셈블리의 NOP를 의미하는 \90으로 채웠습니다.
이렇게 코드를 작성하면 됩니다!
짜잔