본문 바로가기

IT/System Hacking

libc-database 사용법

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

1. libc-database 서론

 libc-databse 라는게 무엇이냐하면, 주로 해킹대회에서 포너블(pwnable) 문제를 풀때 사용하는 유틸리티 프로그램인데, glibc 라이브러리버전별로 우리가 원하는 함수의 주소를 저장하고있는 데이터 베이스라고 보면될것이다.


 다운로드는 아래 URL에서 다운받을수 있다.


libc-database-master.zip


https://github.com/niklasb/libc-database


 일반적으로 요즘 linux 시스템은 대부분 ASLR을 적용하여 사용되고있는데, 그래서 매번 실행될때마다 함수의 주소가 랜덤하게 바뀌며 그 오차도 꽤큰편이어서, 브루트포스 하기에도 매우 어렵다. NX 또한 기본적으로 사용하고있어서 쉘코드를 실행할때 스택이나 힙을 사용하기에도 마땅치않다. 이것을 극복하기위해 보통 RTL(Return to library) 또는 ROP 기법을 사용하는데, 그럴때 libc-database를 사용하면 매우 편리하게 특정 함수의 주소를 찾아서 공략할수 있다.


2. libc-database 원리


 보통 특정 함수의 주소라면 system 함수 주소 , __libc_start_main_ret, "/bin/sh" 주소 등등이 대상이다.


 아래 보는바와 같이 리눅스 libc 라이브러리의 주소는 랜덤하게 잡혀있다.



여기서 libc 함수의 주소는 위에 보는 임의의 랜덤주소 + offset 만큼 떨어져서 존재하고, 이 offset 값은 고정이다.



즉 역으로 생각하면, 특정함수 예를들어 system 함수의 offset 값을 알고 libc가 바이너리에 위치한 주소를 알면(base주소)


libc(base주소) + offset 만큼 더해서 libc 내의 함수주소를 얻어올수있다. 추가적으로 특정 문자열이("/bin/sh")가 위치한 offset 값도 얻을수있음.


libc 버젼별로 offset값을 저장하고있는 database가 바로 libc-database이다, 즉 특정 함수의 마지막 12비트의 값을 데이터베이스화 하고있고, 그값이 주어졌을때 libc의 버전을 찾아내주는 유용한 툴이다.(저위에 그림을 예로 했을때는 마지막 주소 0xed0만 있으면 된다.)


 단 우분투 버전으로만 구성되어있는데, 사용자가 임의로 추가를 해줘서 별도의 db를 구축할수도있는점은 또다른 장점인것같다.

(우분투도 모든 버전에 대해 저장하고있는것은아니므로, 이점 유의해야함)


그럼 내가 사용하는 VMware 리눅스를 예로 들어보겠다.


아래 그림과 같이 하위 12비트 값은 0xed0인데, 실제 실행하여 gdb로 확인해도 하위 12비트 값을 실제로 일치한다.

(그냥 임의의 바이너리를 만들어서 gcc로 컴파일하면 된다, 단 32bit로해야편함)


(함수이름은 system, 주소 하위 12비트는 ed0)


그럼 내가 사용하는 libc의 버전은 몇일까?? 첨부한 파일을 압축을 풀어서 libc-database에 가면 사용할수 있는 명령어들이 있다.


3. libc-database 사용법


(README.md 내용을 정리한것)


- find 명령어


find 라는 명령어를 이용하여 libc의 버전 정보와 offset 값을 얻을수있다.


그런데 find 명령어를 사용해도 libc에 대한 버전과 정보가 나오지않는다...ㅡㅡ;



왜냐하면.. 내꺼 우분투 버전이 12.04로 꽤 오래전 버전이기 때문이다.


이럴때를 대비해서 add라는 명령어가 있다.


- add & get 명령어


나의 libc에 대한 정보를 데이터베이스에 추가하는것이다.. get 명령어만 입력하면 당신 데탑의 libc를 자동으로 추가해준다.


(나의 libc 버전은 2.15...)


이제 다시 찾아보자..



음.. 이제 잘나온다.


- dump 명령어


이제 libc의 주요 함수 offset 값에 대한 정보를 얻어오도록 하겠다. 인자값으로 find로 얻는 id값을 넘겨주면 된다.


(id값 뒤에 함수이름을 넘겨줘서 다른 함수에 대한 offset 값도 알수있다.)



보는 바와같이 libc 라이브러리 주요함수내의 offset 값을 확인할수있다.


실제 해킹대회 등에서 libc-database가 사용되는 시나리오는 아래와 같다.


버퍼 오버플로우 같은 취약점이 있는 프로그램이 있고, 이를 공략하려고 한다.


그런데 프로그램이 돌아가는 시스템이 ASLR 및 NX가 적용되어있어서 RTL등의 기법을 이용하여 system 함수를 호출하여 쉘을 띄우는게 어렵다.


이때 만약 메모리 릭(Memory leak)등을 통해, 프로그램 내의 함수 주소 몇개만 알아 낼수있다면 (예를들어 printf 함수 주소)

(Memory leak은 보통 got, plt 주소, 스택에 저장되어있는 __libc_start_main_ret 주소등이다.)


그함수 주소의 마지막 12비트값을 libc-database로 찾아서 libc의 버전을 알아낸다.


libc에 저장되어있는 offset 값을 구해서 system 함수 주소및 /bin/sh가 저장되어있는 문자열 주소를 구한다.


계산하여 얻은 system 함수 및 "/bin/sh" 주소(offset_str_bin_sh)를 사용하여 RTL을 이용하여 Exploit !!


4. 사용 예제


예를들어 내가 memory leak하여 printf 함수의 주소를 얻었다고 해보자.


printf 함수 주소 : 0xf7e6c480


그렇다면 libc-database를 통해 libc의 버전 정보 및 주요 함수에 대한 offset 정보를 얻을수있다.



offset값은 0x4c480 이다.


그렇다면 libc의 base 주소는??


0xf7e6c480 - 0x4c480 = 0xf7e20000 


이다.


그렇다면 system 함수및 "/bin/sh"의 주소는 아래와 같이 계산할수 있다. offset 값은 dump 명령어 그림에 나와있다.


libc의 base주소 + offset을 하면 실제 함수의 주소를 얻을수있다.


- system 함수 주소


0xf7e20000 + 0x0003eed0 => 0xf7e5eed0


- "/bin/sh" 문자열 주소(offset_str_bin_sh)


0xf7e20000 + 0x15d7ec => 0xf7f7d7ec


즉, printf 함수주소가 주어졌을때 실제 바이너리 실행되면서 내부에 저주소에 system 함수가 저장되어있다는 것이다. 


(실제 gdb로 확인한 정보와 동일함을 확인할수 있다)


libc-database의 기능을 웹브라우저로 제공하는 사이트가 있는데


http://libcdb.com/


https://libc.nullbyte.cat/


라는 사이트이다, 



사용법은 libc-database와 크게 다른건없지만 모든 libc 버전을 저장하고있는것은 아니니, 필요에따라 사용하는것이 좋을것이다.