Userspace Linux Security Monitoring: PART 1

A couple months ago I read a post by @swagitda_ on the various approaches to monitoring a Linux system. I was already familiar with AuditD and its shortcomings. I was not familiar with LD_PRELOAD but the simplicity of the solution appealed to me. Running in userspace felt like a great idea. I started searching on google for projects that use LD_PRELOAD for monitoring with no luck. I found lots of code snippets to hook certain functions and how LD_PRELOAD can be used for privilege escalation under certain circumstances.

My C skills are rusty and I was itching to build a proof of concept solution to build a PoC monitoring solution that’s persistent and cannot be overridden by the user. In this post I will walk you through my experience making a rudimentary solution and hopefully generate interest for the great community to help start an open source project around it. Part 1 goes over what is LD_PRELOAD, why does it work and how to get started. Please note this is not a complete solution and definitely requires a lot more work. I am looking for help from the community to build a kick ass opensource Linux security solution. You can fork the code here

Figure 1: Sample Event log

The Linux Dynamic loader “ld-linux” is responsible for loading shared libraries in order of their position to resolve symbols used by the program during execution. LD_PRELOAD is an environment variable containing path(s) to shared librarie(s), or shared object(s), that the loader will load before any other shared library. This allows overriding / hooking functions to inject our logging code. Example below show a modified string compare function to log to screen which strings were compared during a program execution.

int strcmp(const char *s1, const char *s2) {
	static int (*func_strcmp) (const char *, const char *) = NULL;
	int retval = 0;
	if (! func_strcmp)
		func_strcmp = (int (*) (const char*, const char*)) dlsym (RTLD_NEXT, "strcmp");

	printf("Comparing: strcmp( \"%s\" , \"%s\" )\n", s1, s2);
	retval = func_strcmp (s1, s2);
	return retval;
}

The first magic happens here “dlsym (RTLDNEXT, "strcmp")”. “RTLDNEXT” finds the entry point of the original (strcmp) function. This allows us to snoop on the parameters passed to the function before passing it along to the original function. We need to add “#define GNUSOURCE“ to our source to make use of this. The second trick is the compiler. we call gcc with these parameters “-fPIC -nostartfiles -shared” parameter to ensure that our function is position independent, to prevent linking of standard _init, and to export a shared object. In my case the source file is called “monitor.c” and I used “gcc -fPIC -shared -nostartfiles -m64 -O3 monitor.c -o monitor_x64.so -ldl“ to compile the 64bit object and “gcc -fPIC -shared -nostartfiles -m32 -O3 monitor.c -o monitor_x86.so -ldl“ to compile the 32bit object. Once the object is compiled you are ready to hook applications by setting the LD_PRELOAD environment variable to your object. Alternatively you can call it inline.

Figure 2: Executing programs with LD_PRELOAD

Now if you are wondering well an attacker can easily reset the environment variable you are absolutely correct. However you can create a file called “/etc/ld.so.preload” with the path to your monitor object. Then all dynamically linked binaries are forced to load that object Only a root user can modify ld.so.preload. This is actually really effective as there is no way to bypass this if ld.so.preload is set. The screenshot below show a user logged into a remote device over SSH executing curl to get the wan IP of the system. The logs below show the output from the hooked functions. If you are interested this is the log generated by a test Kali Linux system during reboot.

Figure 3: Shared Object is hooked using ld.so.preload system wide.

I will go over some of the challenges / issues I ran into, road map for this project and how to send these logs to an Elastic stack for Security monitoring. I believe there is a strong need for a solution like this and there is a huge potential in this approach. I would love to hear your feedback and any help i can get from my Linux / InfoSec community peers. Stay tuned for PART 2

Source: https://github.com/ByteSecLabs/ULSM