hint에 나와있는 코드를 살펴보려고 한다.
가장 먼저 shell()이 선언되어있고 이는 권한을 level17로 상승시키며 이 권한으로 쉘을 실행한다.
다음으로 printit()이 선언되어있고 이는 "Hello there!"을 출력하는 함수이다.
다음으로 main함수를 살펴보자.
int형의 crap이 선언되었고, void형의 함수 포인터 call에 printit 함수의 주소를 대입한다.
char형의 buf를 20byte 만큼 할당하고, fget 함수를 사용하여 표준 입력 방식으로 48byte를 buf에 대입한다.
call 함수를 호출하며 main 함수가 끝난다.
buf는 20byte이나 48byte를 대입하므로 오버플로우가 발생할 수 있다.
* 함수 포인터
- 반환값자료형 (*함수포인터이름)();
- void ( *fp )(); // 반환값과 매개변수가 없는 함수 포인터 fp 선언
- 선언시에 함수 포인터와 저장될 함수의 반환값 자료형, 매개변수 자료형과 그 개수가 일치해야 함
- 매개변수가 없을 경우에도 fp와 같이 ()를 붙여주어야 함
ex) fp = hello; // hello 함수의 메모리 주소를 함수 포인터 fp에 저장
fp(); // 함수 포인터 fp로 해당 함수를 호출
앞선 문제들과 달리 attackme.c 코드가 존재하여 권한을 알아보았으나
level15의 권한으로는 read도 불가하므로 attackme를 분석하고자 한다.
attackme를 tmp 디렉토리로 복사하였다.
gdb 명령어로 attackme 파일을 분석하였다.
한 눈에 보이는 차이점은 <main+43>부터 nop(No Operation)으로 채워져 있다는 것이다.
buf(20) + *call(4) + crap(4) = 28인데 0x38(56byte)가 할당되었으므로 28byte는 dummy다.
<main+6> 의 0x8048500이 printit() 함수가 시작되는 주소이므로 함수 포인터의 위치는 여기이다.
shell 함수의 시작 주소를 찾기 위해 gdb를 사용하였다.
각 함수의 시작 주소를 알아냈고 buf 의 시작점에서 *call() 함수까지의 거리는
40byte 이므로 이 곳을 A로 채워 넣고 shell 함수의 시작주소 값을 대입하면 된다.
[ 메모리 구조 ]
RET // 4byte
SFP // 4byte
dummy // 8byte
crap // 4byte
*call() // 4byte
dummy // 20byte
buf // 20byte
[ 페이로드 ]
buf ( 40byte ) + dummy ( 40byte ) + shell 함수의 시작주소값
" A " * 40 + "\xd0\x84\x04\x08"
shell 창이 닫히기 전에 권한 확인과 다음 패스워드를 얻기 위해
cat 명령어를 붙여서 사용하였고, my-pass로 패스워드를 얻었다.
level17의 패스워드는 " king poetic "