관리 메뉴

c0smicb0y

[Lord of BOF] Level 12 (golem -> darkknight) 본문

정보보안/Lord of BOF

[Lord of BOF] Level 12 (golem -> darkknight)

2015. 2. 10. 22:22

1. 문제의 힌트 확인

level12(golem)에 접속하여 ls명령어로 디렉토리의 파일 목록을 살펴보면


[그림1] golem 계정의 디렉토리목록

 

 

darkknight 파일에 suid가 걸려 있는 것을 확인할 수 있다. 이 파일을 이용하여 쉘을 획득해야 할 것이다. vi를 통해 darkknight의 소스를 살펴보자.


[그림2] darkknight.c의 내용

 

 

힌트에 FPO라고 적혀있다. 그럼 우선 FPO에 대해 알아보아야 할 것이다.


2. FPO (Frame Pointer Overflow/Overwrite)

FPO는 간단하게 설명하여 서브루틴이 있는 프로그램에서 서브루틴의 SFP를 조작하여 mainebp를 다른 곳으로 보내버려서 mainleave가 수행될 때 다른 곳으로 보내진 ebpesp가 이동하여 거기에서 ret을 수행하여 쉘을 작동시키는 공격방법이다.

 

일반적으로 버퍼오버플로우는 RET주소를 뒤집어씌워서 공격을 감행하는데 어떻게 SFP만을 조작하여 공격할 수 있는지 본격적으로 알아보자.

 

FPO를 이해하기 위해서는 먼저 SFPespebp, 어셈블리어 leave, ret의 동작을 알아야 한다. espstack pointer로서 현재 스택의 가장 위에 들어있는 데이터를 가리키는 포인터이다. ebpbase pointer로서 현재 스택의 가장 바닥을 가리키는 포인터이다. SFPsaved frame pointer로서 함수의 프로시저에서 이전단계의 스택에 있던 ebp의 값을 저장하는 포인터이다.

leave 명령어는

mov ebp, esp

pop ebp

이 두 명령을 수행한다.

ret 명령어는

pop eip

jmp eip

두 명령을 수행한다.

서브루틴에서 SFP를 조작하여 ebp를 다른 곳으로 보내버린다면 mainleave가 수행될 때 esp가 조작된 ebp로 이동하여 거기서부터 작동을 하여 ret명령을 수행하게 된다. 그림으로 나타내면


[그림3] 서브루틴이 실행되기 전 main의 스택상황

 

 

main 수행되기 전 스택의 상황이 이러하면 서브루틴의 프로시저까지 실행된 후에는


[그림4] 서브루틴의 프로시저까지 실행된 후의 스택상황

 

 

mainebp값이 서브루틴의 스택의 SFP에 들어갈 것이다. SFP의 값을 조작하여 leave명령을 수행하면


[그림5] 조작된 SFP로 인해 엉뚱한 곳을 가리키고 있는 ebp

 

 

이 상태에서 mainleave명령이 수행되면


[그림6] mainleave명령이 수행되고 엉뚱한 곳으로 가버린 esp

 

 

이곳에서 ret명령에 의해 eip값을 얻어 다음 명령을 수행하게 되는데 변경되어버린 ebp+4에 쉘코드의 주소가 있다면 이 프로그램은 마치 그것이 return address인양 eip로 꺼내어 쉘코드를 수행하게 되는 것이다.

 

3. 실제

다시 darkknight의 소스를 보자.


[그림7] darkknight의 소스

 

 

서브루틴에서 입력인자의 값을 41바이트만큼 40바이트 변수에 복사하고 있다. 이 부분에서 1바이트 오버플로우가 일어나서 SFP1바이트를 뒤집어씌울 수 있다.


[그림8] overflow되어 조작된 SFP

 

 

이렇게 되면 앞에서 설명했던 것처럼 엉뚱한 곳으로 ebp가 이동할 것이다 1바이트정도의 변위밖에 못가지지만 말이다.

gdb를 통해 mainebp의 값과 problem_childebp값을 알아보자. mainebp를 어디로 보내서 그 부분에 쉘코드의 주소를 넣어야하니 말이다.


[그림9] gdb로 디스어셈블한 darkknightmain

 

 

problem_child를 호출하기 전인 *main+45problem_child가 호출된 후 프로시저가 진행된 *problem_child+3에 브레이크 포인트를 걸어주자


[그림10] gdb로 브레이크 포인트를 거는 모습

 

 

 

각 브레이크 포인트에서의 ebpespeip의 값을 살펴보자.


[그림11] 각 브레이크 포인트에서의 ebp esp eip

 

 

ebp의 값이 마지막 두 자리, 1바이트만 다르다. 이렇게 된다면 problem_child의 변수인 buffer에 쉘코드의 주소를 집어넣고, problem_childSFP의 마지막 한 바이트만 변조하여 buffer의 주소로 mainebp를 가리키게 하면 mainret이 수행되면서 쉘코드가 실행될 것이다.

 

인자에 A41개 집어넣고 buffer의 주소를 알아보자.

 

problem_childprintf함수를 실행하는 부분에 브레이크 포인트를 걸어주고,


[그림12] printfcall하는 부분에 브레이크 포인트를 걸어주는 모습

 

 

인자에 A41개 집어넣고 프로그램을 다시 실행한다.


[그림13] 인자에 A41개 집어넣고 프로그램을 다시 실행

 

 

여기서 esp의 값을 살펴보면


[그림14] esp의 값을 분석한 모습

 

 

0xbffffac4에서부터 buffer의 내용이 시작되는 것을 볼 수 있다. 그리고 buf앞에는 0xbffffac4, buffer의 주소를 가리키는 주소가 보인다. nop+shellcodebuffer에 넣어주고, "\xbc"로 오버플로우 시키면 ebp0xbffffabc가 되고, main 함수의 leave, ret 명령에 의해 0xbffffabc, 0xbffffac0 순서로 움직이고 pop eip에 의해 eip0xbffffac0에 있는 값(0xbffffac4)가 저장된 후, 0xbffffac4로 점프할 것을 예상할 수 있다. 그러므로 공격코드는

[golem@localhost golem]$ ./darkknight `perl -e'print"\x90"x15,"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80","\xbc"'`


[그림15] 공격코드

 

 

이렇게 구성될 것이다.

 

공격해보자.


[그림16] 공격코드를 실행한 후 쉘이 작동한 장면

 

 

쉘이 작동하였다!

 

id 명령어를 통해 권한을 살펴보면


[그림17] id 명령어로 권한 조회 

 


권한이 상승된 것을 볼 수 있다.

 

my-pass 명령어로 다음 단계의 비밀번호를 획득하자.


[그림18]다음 단계의 비밀번호

 

Comments