Finding Processes With Suspicious CWD Using procfs

This post demonstrates a method to find suspicious processes on Linux systems by examining their current working directories (cwd). This technique may work on other operating systems which have implemented procfs, such as FreeBSD.

Overview

Often, Linux and other Unix-like systems are compromised as a non-root user. This may be by exploiting a service such as Apache or nginx, which typically does not run as root.

Default settings on most distributions do not provide the account under which these services run with very many options for locations to write files.

This usually limits malware to being able to write to /tmp, /var/tmp, and /dev/shm. As such, searching these directories for malware, and searching for processes which are using these directories as working directories is a powerful hunting technique.

Methodology

procfs supplies a symbolic link, /proc/PID/cwd which points to each process’s current working directory.

To find malware, we will examine each process for this symbolic link pointing to /tmp, /var/tmp, or /dev/shm.

The following script demonstrates this concept:

#!/bin/sh

for process in $(find /proc/[0-9]*/ -maxdepth 0 -type d); do
    pid=$(echo $process | cut -d / -f 3)
    p_cwd=$(readlink ${process}cwd)

    if [ "$p_cwd" = "/tmp" ] || [ "$p_cwd" = "/dev/shm" ] || [ "$p_cwd" = "/var/tmp" ]; then
	echo $pid $(cat ${process}comm) $p_cwd
    fi
done

Example

To demonstrate that this technique works, we will use a small C program that sleeps for 5 minutes, which gives us time to observe its procfs entries.

We will compile this program, place it into /tmp, and run it in the background.

Finally, we will run the hunting script in the previous example, and it will correctly identify this process as having a working directory of /tmp.

daniel@wildcat ~ % cat dummy.c
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
	sleep(300); /* Sleep for 5 minutes */

	return EXIT_SUCCESS;
}
daniel@wildcat ~ % gcc -o dummy dummy.c 
daniel@wildcat ~ % mv dummy /tmp
daniel@wildcat ~ % cd /tmp
daniel@wildcat /tmp % ./dummy &               
[1] 475970
daniel@wildcat /tmp % cd
daniel@wildcat ~ % ./processes.sh
475970 dummy /tmp

As you can see, it identified “dummy” with a PID of 475970 as having a cwd of /tmp.