#About


[그림 1] - darknight.c 소스 코드 확인




#Solution


[ + ] 소스 코드 해석


1. buffer라는 변수 선언 및 40바이트 할당


2. 사용자 입력 값인 argv[1]로부터  41바이트 만큼 복사 후 출력


해당 소스코드에서는 buffer로부터 41바이트를 읽으므로 EBP의 1바이트를 변조할 수 있으며 이를 FPO(Frame Pointer Overflow)라고 한다

이를 이해하기 위해서는 먼저, 함수의 에필로그 과정을 이해하여야한다. 에필로그 과정은 leave-ret 과정으로, esp를 ebp로 옮기며, 

ebp+4 부분인 리턴 값을 eip에 넣고 실행한다. 구체적인 방법은 아래의 과정을 통해 이해하길 바란다


[그림 2] - 임의의 페이로드 삽입 및 EBP 확인


임의의 페이로드를 삽입해줌으로써 core 파일을 얻을 수 있었으며 core 파일의 분석을 통해 빨간색 부분이 EBP 임을 확인할 수 있다. 


만약 EBP의 마지막 바이트를, buffer의 1byte 오버플로우를 통해 0x08048500이라는 값을 가지고 있는 주소인 0xbffffa7c로 변조하면 어떻게 될까?


함수의 에필로그 과정을 거치면서 esp는 ebp인 0xbffffa7c로 이동할 것이며 또한 리턴 값을 eip에 담기 위해 ebp+4의 값을 참조할 것이다


ebp+4의 값은 초록색부분으로 buffer의 시작 주소를 가리킨다. 이는 오버플로우가 가능하고 buffer에 셸 코드를 넣으면 셸을 획득할 수 있음을 의미한다


따라서 아래와 같이 buffer에 NOP과 셸 코드를 삽입해준 뒤 0xbffffa7c로 EBP를 변조해주었더니 셸을 획득할 수 있었다


[그림 3] - golem 권한의 셸 획득


이와 같은 방식으로 darkknight에 페이로드를 삽입해주면 역시 아래와 같이 셸을 획득할 수 있다


[그림 4] - 셸 획득



#About


[그림 1] - golem.c 소스 코드 확인




#Solution


[ + ] 소스 코드 해석


1. 외부 다른 소스파일에 있는 environ 포인터 변수 사용


2. buffer라는 배열에 40바이트 할당 및 정수형 변수 4바이트 i 할당


3. 조건문을 통해 전달 인자가 2개 미만이면 argv error를 출력 후 종료


4. 조건문을 통해 사용자 입력 값의 48번째 바이트가 \xbf가 아닐 시, stack is still your friend.를 출력 후 종료


5. 사용자 입력 값을 buffer에 복사 후 출력


6. memset 함수를 이용해 buffer 40바이트와 SFP 4바이트를 0으로 초기화


7. memset 함수를 이용해 RET 이후부터 0xbfffffff까지 0으로 초기화


6,7번 과정에 해당하는 stack destroyer! 때문에 더 이상 buffer와 argv[0] 등을 사용할 수 없으며 이를 우회하는 방법으로는 

buffer 보다 더 낮은 주소에 위치하는 공유 라이브러리 영역을 사용하는 것이며 메모리 구조도는 다음과 같다


| Code | Data | BSS | Heap | 공유 라이브러리 | buffer | SFP | RET | argc | argv | env | ... | kernel |


공유 라이브러리 영역을 사용하는 방법으로는 LD_PRERLOAD라는 특별한 환경변수를 사용하는 것이다


LD_PRELOAD


프로세스를 실행하는 중에 라이브러리를 로딩할 때, LD_PRELOAD 환경변수가 설정되어 있으면 해당 변수에 지정된 라이브러리를

먼저 로딩하고, 이중 libc 함수명과 동일한 함수가 있다면 해당 함수를 먼저 호출한다. 또한 이러한 특성을 이용해 후킹도 가능하다



먼저, 아무런 동작을 하지 않는 아래의 c 파일을 하나만든 뒤 NOP과 셸 코드를 넣은 이름으로 아래와 같이 컴파일 해주며


컴파일에 사용 된 -fPIC 옵션은 Position-Independent Code의 약자로 동적라이브러리 생성을 위함이며 -shared 옵션은 공유 라이브러리를 만들기 위함이다 


[그림 2] - 공유 라이브러리 영역에 사용할 셸 코드 등록


다음으로 LD_PRELOAD에 아래와 같이 NOP과 셸 코드를 담은 뒤 환경변수에 등록하며 


이 때 주의해야할 점은 프로그램의 절대 경로가 붙어야 오류가 나지 않는다고 한다 


[그림 3] - 환경변수 등록


다음으로 gdb를 이용하여 NOP과 셸 코드가 포함되어 있는 LD_PRELOAD의 시작 주소를 아래와 같이 확인했다


(gdb) r `python -c 'print"\xbf"*48'`

(gdb) x/300x $esp -3000


[그림 4] - LD_PRELOAD 시작 주소 확인


LD_PRELOAD가 시작되는 주소는 0xbffff4d7임을 확인했고 해당 주소를 RET 주소로 아래와 같이 변조해주면 셸을 획득할 수 있다


[그림 5] - skeleton 권한의 셸 획득


아래와 같이 위의 과정을 그대로 하여 셸을 획득할 수 있으며 환경변수의 길이와 프로그램 경로 변경으로 인한 메모리 주소가 변경된다면 


아래의 과정처럼 환경변수와 경로의 길이를 같게 해주어 core 덤프 파일을 분석하면 정확한 RET 주소 즉, LD_PRELOAD의 시작 주소를 확인할 수 있다   


[그림 6] - 셸 획득


[그림 7] - core 덤프 파일 생성


[그림 8] - core 덤프 파일을 통한 LD_PRELOAD 시작 주소 확인


+ 문제를 해결함에 있어 사용 된 셸 코드 


\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



#About


[그림 1] - skeleton.c 소스 코드 확인




#Solution


[ + ] 소스 코드 해석


1. 외부 다른 소스파일에 있는 environ 포인터 변수 사용


2. buffer라는 배열에 40바이트 할당


3. 변수 i와 saved_argc에 각 4바이트씩 할당


4. 조건문을 통해 전달 인자가 2개 미만이면 argv error를 출력 후 종료


5. 반복문을 통해 환경변수 전부 초기화


6. 사용자 입력 값의 48번째 바이트가 \xbf가 아닐 시, stack is still your friend.를 출력 후 종료


7. 사용자 입력 값의 길이가 48 바이트를 넘으면 argument is too long!을 출력 후 종료


8. 사용자가 사용한 인자의 개수를 saved_argc에 담음


9. 사용자 입력 값을 버퍼에 복사 후 출력


10. memset 함수를 통해 buffer 모두 초기화


11. 반복문과 memset 함수를 이용해 사용자가 전달한 인자의 개수와 그 길이만큼 전부 초기화


이번 문제는 buffer 및 사용자가 입력한 인자를 모두 초기화 시키기 때문에, 실행 파일 명은 메모리의 끝 부분에서 초기화 되지 않는 다는 점을 이용해서 문제를 해결할 수 있다


[그림 2] - 초기화 되지 않은 실행 파일 명 확인


실제로 위와 같이 임의의 값을 넣어 core 파일을 생성한 뒤, 스택의 끝 부분 즈음에 살펴보면 실행 파일 명은 초기화되지 않은 것을 확인할 수 있다


 따라서 실행 파일 명을 아래와 같이 NOP과 셸 코드로 작성한 뒤 링크를 걸어주고 core 파일을 생성했으며 사용한 셸 코드는 아래와 같다


\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


[그림 3] - core 덤프 파일 생성 확인


현재 메모리 구조인 | buffer (40) | EBP (4) | RET (4) |에서 RET 주소를 셸 코드가 담긴 링크 파일명의 시작 주소로 변조하기 위해 core 파일을 분석했다


[그림 4] - core 파일 분석


셸 코드가 담긴 실행 파일 명의 시작 주소는 위에서 확인할 수 있듯 0xbffffff01이며 파일의 실행을 위한 ./를 고려한다면 0xbffffff03이 될 것이다 따라서 RET 주소는 0xbfffffff03이 되며, 아래와 같이 RET 주소를 변조해주었을 때 셸을 획득할 수 있다


[그림 5] - vampire 권한의 셸 획득


위와 같은 방법으로 skeleton에 심볼릭 링크를 걸어준 뒤 진행하면 셸을 획득할 수 있고 /home/vampire/tmp/에서의 메모리


주소와 /home/vampire/에서의 메모리주소는 다르지만, 셸 코드 앞 100 바이트의 NOP 때문에 4 바이트의 tmp/는 상관없이 셸 획득 가능


[그림 6] - 셸 획득


+ Recent posts