Hiding processes on Unix/Linux

freebsdprocprocessshared library

I found an interesting article about how to hide specific processes on linux from process monitoring tools like ps, top, lsof, …

Person states that there are several possible ways to hide processes:

  1. Using a proper framework: there are a bunch of very good frameworks, like SELinux and Grsecurity that do, among other
    things, exactly this. In a production system, I would absolutely
    consider these, although today I want to get my hands dirty and have
    fun creating something from scratch.
  2. Modify top/ps/... binaries: I could grab the source code of each of these tools, implement my own "hiding linux processes"
    logic, recompile, and replace the binaries. Very inefficient and time
    consuming.
  3. Modify libc: I could modify the readdir() function inside libc and input the code to exclude the access to some /proc files.
    But recompiling libc is a burden, not to mention the libc code
    tends to be very hard to understand.
  4. Modify the system calls in the kernel: This is the most advanced, and it would work by intercepting and modifying the getdents()
    system call directly in the kernel with a custom module. It’s
    definitely tempting, but I won’t follow this route today because I’m
    already very familiar with how the system call interception works in
    sysdig, so I want to do something new.

I decided to go for an intermediate solution, one that is interesting
and simple enough to implement in an hour or so: it’s a variant of
"modifying libc" based on a tricky feature offered by the Linux dynamic linker (the component that takes care of loading the various
libraries needed by a program at runtime), called preloading.

With preloading, Linux is kind enough to give us the option to load a
custom shared library before the other normal system libraries are
loaded. This means that, if the custom library exports a function with
the same signature of one found in a system library, we are literally
able to override it with the custom code in our library, and all the
processes will automatically pick our custom one!

This sounds like a solution to my problem, because I could write a
very simple custom library that overrides libc's readdir(), and write
the logic to hide the process! The logic would be fairly
straightforward too: every time I see that the /proc/PID directory
(where PID is the PID of the process having the name "evil_script") is
being read, I just block that access in a clean way, thus hiding the
entire directory! I went ahead and implemented these thoughts in code.
You can get the sources at
https://github.com/gianlucaborello/libprocesshider/blob/master/processhider.c
(it’s actually less than 100 lines of code including comments, so go
read it!). Once the code is written, let’s compile it as a shared
library, and install it in the system path.

Source code: processhider.c

So the steps are:

  1. make => gcc -Wall -fPIC -shared -o libprocesshider.so processhider.c -ldl
  2. mv libprocesshider.so /usr/local/lib/ (as root)
  3. echo /usr/local/lib/libprocesshider.so >> /etc/ld.so.preload

Now back to my question/problem: Here it is described for Linux systems. I tested it on my Ubuntu 18.04 (64 bit) machine and everything worked fine – process was now hidden from ps. Additionally I tested it on my another machine where FreeBSD 11.0 (64 bit) is installed.

First I had to remove following part of the code:

DECLARE_READDIR(dirent64, readdir64);

because I got an error (dirent64 wasn't defined in dirent.h – I just used locate dirent.h and compared the code to some internet sources):

processhider.c: In function 'readdir64':
processhider.c:87:37: error: dereferencing pointer to incomplete type 'struct dirent64'
                 get_process_name(dir->d_name, process_name) &&          \
                                     ^
processhider.c:97:1: note: in expansion of macro 'DECLARE_READDIR'
 DECLARE_READDIR(dirent64, readdir64);
 ^
*** Error code 1

After removing DECLARE_READDIR(dirent64, readdir64); I got another error complaining about -ldl flag:

/usr/local/bin/gcc5 -Wall -fPIC -shared -o libprocesshider.so processhider.c -ldl
/usr/local/bin/ld: cannot find -ldl
collect2: error: ld returned 1 exit status
*** Error code 1

Stop.

I found a solution where one could replace -ldl with -L/usr/local/lib where my libdl.so file is located (meaning of "collect2: error: ld returned 1 exit status" error).

Then I was able to compile the code. I placed the library into /usr/local/lib/ and added it to /etc/ld.so.preload.

However when I called my script evil_script.py (Code was different (no more UDP packets spam), but I still have while true loop and time.sleep(60) so the process should be there) it still appeared in the process list (ps auxww). Maybe /etc/ld.so.preload is not working? Can be there some problem with ld.so shared library? Is there any way how I can test what goes wrong at which point?

Best Answer

Method 2 and 3 (modifying ps, top, libraries), is prone to walk around breach. E.g. an attacker can just use there own ps.

A better way would be to block these tools from accessing info on processes of other users. Then running the processes to be hidden as a different user.

For the 1st part, edit /etc/fstab, to include

#protect /proc
proc /proc proc defaults,nosuid,nodev,noexec,relatime,hidepid=2,gid=admin 0 0
Related Question