Lord of the BufferOverflow Fedora Core3


[ Level1_Gate -> Iron_golem ]


바로 시작합니다.

다음은 fc3의 iron_golem.c의 소스입니다.



주어진 힌트는 fake ebp 기법을 사용하여 풀이 할 수 있다고 합니다.



iron_golem 바이너리를 cp 명령어로 melog_nori로 복사 한 후 gdb로 main 함수를 disassemble 해보았습니다.

main+3의 위치를 확인 해 보면 esp 레지스터를 0x108만큼 빼주는 것을 알 수 있습니다. 이는 곧 선언 된 변수를 위해 메모리에 공간을 할당해주는 작업이라는 뜻입니다. 0x108은 10진수로 264로, buffer변수에서 할당했던 256byte에 8byte만큼의 dummy 값이 포함 된 만큼의 공간이라고 볼 수 있습니다.



또한, 위의 esp 값을 보면 실행 할 때마다 esp 레지스터의 위치가 바뀌는 것으로 보아 스택이 랜덤하게 변하는 것을 알 수 있습니다. 게다가 스택의 시작 주소가 0xfe인 것부터 redhat6.0 환경과 다르기에, 페도라 코어3와의 차이를 알아보았습니다.


 

 Redhat 9.0

Fedora Core3

 Random Stack

 O

 O

 Non Executable Stack

 X

 O

 Libc Address

 16MB↑

 16MB↓


스택에서의 실행 권한을 주지 않는 것을 보니, 쉘코드를 직접 실행하기는 어려울 것 같습니다. 힌트로 제시 된 Fake EBP를 이용한 Beist's Execl 방법으로 풀이하도록 하겠습니다.

일반적인 RTL을 이용한 풀이도 해 보았지만, 위의 표에서도 알 수 있듯이 라이브러리 함수들의 주소가 16MB 미만이기 때문에 불가능하였습니다.

16MB는 16진수로 0x01000000인데, 그 미만이기 때문에 0x00ffffff까지밖에 존재하지 않습니다. 따라서 RTL을 위해서는 system()과 같은 함수의 주소를 포함하여 '/bin/sh'의 주소를 프로그램의 파라미터로 넘겨주어야 하는데 system 함수의 주소에 0x00(NULL Byte)가 포함되어 뒤의 값들을 인식하지 못해 exploit을 할 수 없습니다. 이와 같이 첫 바이트에 0x00이 존재하는 주소를 ASCII-Armor라고 합니다. 이를 우회하기 위한 방법이 Beist's Execl 방법입니다.

Beist's Execl 방법은 Beist LAB의 Beist님께서 Fedora Core의 Exec-Shield를 우회하기 위해 제시 한 기법입니다. Execl 함수가 내부적으로 Execve 함수를 호출하는데, 이 때 ebp+8의 위치를 참조하는 특성을 이용하여 shell을 얻어내는 방법입니다. 이를 위해서는 아래와 같은 준비가 선행되어야 합니다.


- execl함수의 주소

- Data Section 내의 특정한 주소


execl함수는 gdb 내에서 print 명령어를 통해 간단하게 구할 수 있지만, 그 아래 있는 Data Section 내의 주소는 무엇일까요?

이 것을 설명하기 전에 execl 함수의 원형을 살펴보도록 하겠습니다.


 int execl( const char *path, const char *arg, ...); 


execl 함수는 위와 같이 최소 3개의 파라미터를 가집니다. 그렇다면 가장 적은 인자를 가질 때는 아래와 같을 것입니다.

execl(*path, *arg, NULL);

즉, execl 함수의 가장 마지막 인자는 NULL(0)이 되어야 한다는 뜻입니다.


그럼 다시 본론으로 돌아오겠습니다. 페도라 코어에서는 스택 내에서의 실행을 제한 할 뿐만 아니라 스택의 주소 자체가 랜덤하게 배정되기 때문에 argv[1]과 같은 파라미터에 데이터를 넣어 주어도 해당 데이터가 있는 주소를 특정하기 어렵기 때문에 실행 할 때마다 변하지 않는 주소를 이용하여야 합니다. 그 중 하나가 Data Section인 것입니다. Data Section 중에서도 GOT 부분은 항상 NULL Byte로 끝나기 때문에 이 부분을 이용하면 execl 함수의 인자 조건을 만족 시킬 수 있는 것이죠.



gdb를 이용하여 execl 함수의 주소를 먼저 구해줍니다.


- execl의 주소 : 0x007a5720



readelf 명령어를 이용하여 got section을 찾아보면 0x0804618인 것을 확인 할 수 있습니다. 이를 gdb에서 찾아보도록 하죠.



0x8049634 부분을 확인하면 GOT의 끝부분이기 때문에 0x00000000이 있는 것을 확인 할 수 있습니다. 그렇다면 8byte 전인 0x804962c를 확인해봅시다.

가리키고 있는 주소는 0x0804830e로, 찾아가보면 0x00001068이 있습니다. 바로 이 부분이 우리가 전달하는 포인터로 사용할 부분입니다. 첫 번째 파라미터는 실행하고자 하는 프로그램의 이름이기 때문에 우리는 심볼릭 링크를 이용하여 실행하려는 파일의 이름을 0x1068로 바꿔 줄 필요가 있습니다.


- 특정 주소 : 0x804962c


말 나온 김에 실행 할 프로그램부터 살펴보도록 하겠습니다.



setreuid, setregid를 이용하여 권한을 재설정하고, execl을 이용해 쉘을 띄워주는 간단한 코드입니다. 이를 shell이라는 이름으로 컴파일 한후, 심볼릭 링크를 이용하여 0x1068로 바꿔주도록 합니다.



프로그램 또한 정상적으로 작동하는 것을 확인했습니다!

이제 exploit만 남았습니다. payload를 살펴보기 전에, 이 기법의 원리부터 간단하게 알아보도록 하겠습니다.

beist's execl을 처음 소개할 때 언급했듯이, execl 함수는 내부적으로 execve 함수를 호출합니다. 이 때 ebp는 우리가 넘겨주는 '첫 번째 파라미터의 위치'입니다. 여기서 ebp+8의 위치를 참조한다고 하였으므로, 결과적으로 우리가 넘겨주어야 할 첫번째 파라미터의 주소는 8만큼 빼 주어야 한다는 말이 됩니다.

아까부터 첫 번째 파라미터를 넘겨준다고 설명하였는데, 이 파라미터는 또 어떻게 넘겨준다는 것일까?

여기서 FakeEBP 기법이 이용됩니다. 현재 iron_golem의 스택 구조는 다음과 같습니다.


[buffer][dummy][SFP][RET]

--(256)----(4)-----(4)---(4)--


main 함수에서 leave;ret을 하게 되면 SFP가 EBP로 이동되고, RET의 주소로 jmp하게 됩니다. 그리고 jmp 한 주소가 함수라면, 함수 프롤로그를 통해 main함수의 SFP를 push하고 EBP가 main함수의 ESP 값을 가지게 되죠. 하지만 이 때, RET가 함수의 주소이지만, 함수+3의 위치로 return을 하게 되면 어떻게 될까요?

바로 함수의 프롤로그를 뛰어 넘고 함수를 실행하게 됩니다. 즉, EBP(main 함수의 SFP)의 값을 그대로 가지고 함수를 실행 할 수 있다는 뜻이 됩니다.

여기서 내부적으로 execve가 실행되고, 그 파라미터로 ebp+8의 값이 넘어가면서 ebp+8이 참조하고 있는 위치의 문자열(파일 경로)을 실행하게 되는겁니다.

이런 생각은 대체 어떻게 하는건지... 정말 대단한 것 같습니다.

그럼 설명한 내용을 토대로 payload를 작성 해 보도록 하겠습니다.


./iron_golem $(python -c "print 'A'*264+[0x804962c - 8]+[0x007a5720 + 3]") ->

./iron_golem $(python -c "print 'A'*264+'0x8049624'+'0x007a5723'")



shell이 띄워졌으며, id 명령어를 통해 iron_golem으로 권한이 상승한 것을 확인 할 수 있습니다.







References

- FEDORA CORE2에서 EXEC-SHIELD를 우회하여 STACK 기반 OVERFLOW 공격 기법 한번에 성공하기(beist)

- 해커 지망자들이 알아야 할 Buffer Overflow Attack의 기초(달고나)

- Redhat 9 부터 Fedora 5 까지의 Return into Libc 기법(이경호)

+ Recent posts