Rootkits are set of programs that allows someone to manipulate behavior of the host operating system without revealing his or her presence. They are often used by attackers to maintain access on victim machines without setting off any alarm. They come at play in post exploitation phase of an attack. That being said, not all rootkits are malicious. Dtrace , a framework for performance analysis and debugging in real time included in FreeBSD can be considered a “good rootkit” because of the way its designed to work.
Rootkits can be classified into
i) Kernel mode - Rootkits which can alter kernel’s internal data structures and manipulate it’s behavior.
ii) Usermode rootkit - Rootkits which modify operating system functions at user-level like modifying programs , libraries or maybe registry hives in windows.
Today, we’re gonna take a look at Linux userland rootkits (Maybe windows next time?). There are many ways to load linux rootkit but popular technique involve
This method involves replacing system utilities like ls, top,ps, etc with their malicious counterpart. These malicious version of utilities can hide files, processes and sockets but this method sucks. Why? Because it raises too much suspicion. Host based intrusion detection systems will instantly alert admin about change in cryptographic checksum of binaries. Almost all modern HIDS do checksum of system files at regular interval. So, detection is inevitable by this method if proper security measures are in place. This also sucks because we’ll need to write malicious version for every system utilities we need hook for (hook means modified functions) , which involves too much work. What if we can just mess up C library functions somehow ? We won’t need to write malicious version of every utility just malicious version of C library functions and every utility would be affected by it.
Before going any further we need to clear a few things about shared libraries.
Shared libraries are very much like a program that never gets started. They have code and data sections (functions and variables) just like every executable; but no where to start . They usually have .so extension in Linux or .dll in windows. They just provide a library of functions for programmers to call. When we compile our program that uses a dynamic library, object files are left with references to the library functions. Shared libraries provide flexibility to the development environment as the library code can be changed, modified and recompiled without having to re-compile the applications that use this library.
The programs ld.so and ld-linux.so find and load the shared libraries needed by a program, prepare the program to run, and then run it unless program was statically linked. Dynamic linker tries to load shared library (*.so) from :
- directories listed in the LD_LIBRARY_PATH environment variable.
- directories listed in the executable’s rpath.
- directories on the system search path, which consists of the entries in /etc/ld.so.conf plus /lib and /usr/lib
We can see what dynamic libraries are loaded by a program ls using ldd utility
This is where environment variable called LD_PRELOAD comes into play. If we set LD_PRELOAD to the path of a shared object, that file will be loaded before any other library (including the C runtime, libc.so). We can even implement our own libc functions using this.
To show how this can be misused , let’s try to hide something called rootkit.txt from ls. Let’s start by taking a look into ls source code. This utility is part of GNU coreutils on linux and part busybox on Android. To hide this file we must understand how ls reads file names. We can read directory in linux programmatically with readdir() as defined in standard C library or getdents() which is a syscall. A quick grep into ls.c source code tells us that there’s no use of getdents() while readdir() is used to list out files. Code looks something like
Now we just need to hook readdir() to hide rootkit.txt. Here’s the code to do it.
Code basically checks if file name is “rootkit.txt”, if not then return result of whatever readdir() returns else return nothing.
Notice that we used dlsym() to get original readdir() (called o_readdir() ) from libc. Now, compile and link it
and now create a dummy file to test
and try to ls now
stat shows that file is still there, just “hidden”. We can even subvert stat to hide our file from it too. Not a big deal, now that we know how to get around hooking functions. We can also extend this idea to hide sockets and processes.
But there’s still a problem. We need to set environment variable every time to load our malicious shared library. Even echoing out $LD_PRELOAD in shell will reveal we’re preloading some shared library and we also need to set it for every user on system. Quite a work? Isn’t?
we can just add shared library path in /etc/ld.so.conf file which acts as “global” preload by
and hook other functions in c library to prevent this file from getting detected. This method too will create lot of suspicions if proper intrusion detection mechanism are in place but still better than former technique. Maybe next time we’ll try be more stealthy using kernel mode rootkits.
####How to detected if you are victim?
Check /proc/$BASHID/maps for suspicious looking loaded shared libraries.
Check output of ldd to see what shared objects a program uses.
Check $LD_PRELOAD environment variable.
Check what’s inside /etc/ld.so.conf
We can compare address of functions from libc using dlsym() and address directly from program to check if it was hooked using
Use some Intrusion detection system , I personally recommend [OSSEC HIDS]
Rootkit detection with OSSEC