라이브러리 공격
Language/C 2011. 9. 8. 10:15공유라이브러리(Shared library)는 돌아가는 리눅스 상에서 프로그램의 크기를 작고 가볍게 하기 위하여 필요할때만 가져다 쓸수 있도록 만들어진 라이브러리로 일반적으로 gcc -o project project.c 이와같이 컴파일 하게 되는데 이렇게 컴파일을 하게 되면 자동적으로 공유라이브러리를 참조하게 된다.
우리가 일상적으로 쓰는 printf, scanf와 같은 함수도 모두 공유라이브러리를 참조해서 사용하는것이다.
만약 project.c를 gcc -o project project.를 컴파일 하게 되면 이와같은 과정을 거쳐서 만들어 지고 기본 공유라이브러리인 lib.so.6를 참조 하게 만들어 놓는다.
project.obj같은 경우에는 -o로 컴파일 할경우에는 쓰고 지우기 때문에 눈으로는 확인할수 없고 결과물인 project.c와 project만 보일것이다. obj를 보기 위해서는 -c옵션으로 컴파일 해주시면 된다.
libc.so.6가 연동되어 있는것은 objdump를 이용해서 확인할수 있다. -p옵션으로 헤더만 확인해 보자. gcc로 일반적으로 컴파일한 파일 아무거나를 가져와서 보게되면
동적할당 된것중에 libc.so.6 라이브러리가 링크되어 있는것을 볼수 있다. 그리고 이렇게 보지 않더라도 오류같은것이 나게 되어서 gdb로 보게되면 어디 함수에서 걸려서 그렇다고 하면서 옆에 libc.so.6가 보이는데 그것으로도 이 함수는 저 라이브러리에 의해서 실행되고 있구나 하는것을 알수 있다.
//ldd와 readelf를 이용해서도 라이브러리 링크를 확인할수 있다.
//ldd - print shared library dependencies
공유라이브러리 의존성을 확인하는데 특화된 프로그램
//readelf - ELF 파일 정보 보기 -d 옵션 == dynamic
2. 공유라이브러리의 취약점을 이용한 공격라이브러리 만들기
원래 어떤 함수를 실행하기 위해서는 공유 라이브러리를 들려야만한다.
아래와 같은 코드가 공격코드가 있다고 하자.
//chal2.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
int main(void){
uid_t uid = getuid(); // 공유 라이브러리 참조 함수들
gid_t gid = getuid();
uid_t euid = geteuid();;
gid_t egid = geteuid();
if(uid != 1209 | euid != 1209){
printf("You are not 1209!!. GET AWAY! \n");
exit(EXIT_FAILURE);
}
if(gid != 1209 | egid != 1209){
printf("You are not 1209!!. GET AWAY!! \n");
exit(EXIT_FAILURE);
}
int i,j;
char password[] = "abcdefghij";
for(i=0;i<10;i++)
for(j=0;j<i;j++)
password[i]++;
printf("Password is :: %s\n",password);
return 0;
}
위의 프로그램을 돌려보면
./chal2
You are not 1209!!. GET AWAY!
이와 같이 나온다. 당연하다. 난 id를 확인해보면
id
uid=500(whdudgn) gid=500(whdudgn) groups=500(whdudgn)
context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
500이라는것을 알수 있다. 그러므로 프로그램 password를 따낼수 없다. 그렇다고 직접 id가 1209인 아이디를 찾아내서 그 아이디로 바꿀수 있는 setuid걸린 프로그램도 없고 확인해 본 바 id가 1209인 유저 자체가 없다. 매우 난감하기 짝이 없다. 이럴때에는 위에서 사용된 getuid() geteuid()같은 함수를 공략할수 밖에 없다.
이때 공유라이브러리의 취약점을 사용할수 있게된다.
항상 프로그램은 어떤 함수를 쓸떄 공유라이브러리 함수를 들르게 되어있다.(정적라이브러리로 프로그램에 라이브러리를 합쳐놓지 않는 이상..... 프로그램에 라이브러리가 합쳐져 있다면 대부분 용량으로 파악이 가능하다. 라이브러리가 프로그램에 같이 섞여 있다면 엄청나게 용량은 늘어날수밖에 없기 때문이다. 그러나 합쳐져서 공유라이브러리를 참조 하지 않기때문에 공유라이브러리 취약점은 사용할수 없게 된다. 정적 라이브러리는 ar rc libaaa.a aaa.o 를 써서 ar의 r은 라이브러리 아카이브에 새로운 오브젝트를 추가하고 c는 그것이 없을때 생성하라는명령어로 생성후 사용할때는 gcc로 컴파일 하는 파일 뒤에 -L./laaa와 같이 lib는 l로 그리고 아카이브인 .a는 생략하고 써주시면 컴파일 할때 정적라이브러리로 컴파일 하고 사용할수 있게된다.)
//정적라이브러리는 주제에서 약간 벗어났지만 정적라이브러리에 대해서 궁금하신 분을 위해서 약간 써 놓았다. ()안의 것은 보지 않으셔도 무방하다. 깊게 알고 싶으신 분은
http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/C/Documents/Make_Library 로 가서셔 보시기 바란다.
그때 이 오른쪽과 같이 공유라이브러리로 가려고 하는것을 가로채서 내가 만든 라이브러리를 참조하게 되면 프로그램은 내 의도대로 움직이게 된다. 그럼 이제 우리는 공격을 위한 attack.so라는 동적라이브러리를 생성해보자.
※ 라이브러리를 만들기 위한 gcc 컴파일러의 옵션
-shared 옵션
공유 라이브러리를 우선하여 링크하도록 하는 옵션
정적라이브러리와 같이 있을 경우에 우선적으로 공유 라이브러리를 링크하도록 하는데,
사실 아무 옵션을 주지 않아도 공유 라이브러리를 우선적으로 링크한다.
-fpic -fPIC 옵션
gcc 컴파일러가 object file을 만들때, 그 안의 symbol (function, variable)들이
어떤 위치에 있더라도 동작을 하는 구조로 compile 하라는 것입니다
그렇게 된 것만이 리눅스에서 사용 가능한 모듈파일(*.so)이 될 수 있습니다.
모듈파일(*.so)은 안에는 공유 라이브러리가 포함되어있습니다.
-fpic와 -fPIC - 둘의 차이점.
"코드를 생성하기 위해 -fPIC이나 -fpic을 사용하라. 코드를 생성하기 위해
-fPIC이나 -fpic을 사용하는 것은 타겟에 따라서 다르다.
-fPIC을 사용하는것은 언제나 동작한다.
하지만, -fpic을 사용하는 것보다 큰 코드를 생성할 것이다
(PIC은 더 큰코드를 위한것이라서 더 많은 양의코드를 만든다는 것을 기억하라)
-fpic옵션은 작고 빠른 코드를 만든다. 하지만, 전역심볼이나 코드의 크기 같은 것에서
플랫폼에 독립적이다. 링커는 공유 라이브러리를 만들때 이 옵션이 맞는지 말해줄 것이다.
어느것을 써야 할지를 모를때, 나는 언제나 동작하는 -fPIC을 선택한다"
-fpic, -fPIC의 차이점은 KLDP 문서중에 인용하였습니다.
http://kldp.org/HOWTO/html/Program-Library-HOWTO/
이제 공격 라이브러리를 생성해보자. 우리는 저 함수들의 리턴값이 1209이길 바라기 때문에
#include <dlfcn.h> //동적 라이브러리 관련 헤더파일
#include <unistd.h>
#include <sys/types.h>
uid_t getuid( void ){
return 1209;
}
uid_t getgid( void ){
return 1209;
}
uid_t geteuid( void ){
return 1209;
}
uid_t getegid( void ){
return 1209;
}
로 작성한 후에
[whdudgn@/home/whdudgn/programming/chal]$ gcc -o attack.so attack.c -fPIC -shared
이와같이 컴파일 해주자. 그럼 우리는 동적 라이브러리를 생성하게 된것이다.
그리고 우리는 이제 프로그램이 함수를 참조하기 위해서 libc.so.6를 들르기 전에 우리가 생성한 attack.so로 오게 만들면 우리가 작성한 return값을 넘겨줄수있기 때문에 공격은 성공하게 될것이다.
그러기 위해서는 우리는 이 공격에서 가장 중요한 환경변수인 LD_PRELOAD 라는 환경변수를 알고 있어야 한다. 이 환경변수는 프로그램이 라이브러리를 가져오기전에 원하는 라이브러리를 먼저 등록시켜주는 환경변수로, 프로그램은 LD_PRELOAD로 지정된 공유 라이브러리를 먼저 들르게 된다. 그리고 그곳에 자기가 원하는 함수가 없을 경우에 표준 라이브러리인 libc.so.6로 향하게 된다.
그점을 이용해서 환경변수에 attack.so를 등록한후 우리가 공격할 프로그램을 작동시키면 아래와 같이 password를 뱉게 된다.
[whdudgn@/home/whdudgn/programming/chal]$ export LD_PRELOAD="/home/whdudgn/programming/chal/attack.so"
[whdudgn@/home/whdudgn/programming/chal]$ ./chal2
Password is :: acegikmoqs
참고 문헌 :
http://sosal.tistory.com/125 ==> shared library hijacking..