This post will be talking about LD_PRELOAD environment variables and what they can be used for. This is usually used in C programming as well as CTF style challenges. It will be a bit easier to understand the examples if you have prior C knowledge and basic Linux knowledge.
To talk about LD_PRELOAD, one needs to be familiar with static and dynamic linking of compiler
Static linking
This method of the compilation will produce a fully standalone executable or a library file containing all the function definitions into just one file.
Dynamic linking
This method of compilation, however, will not be fully standalone, instead, it will rely on the existence of the statically linked libraries on the system. Normally they will search for directory paths such as /lib64
or /lib
.
When you have spent a sufficiently long time on a Linux system, you should have seen error messages while installing packages or running programs that look something like the following, complaining about the shared objects not being found.
Error: openssl.so.1 not found
Error: libcrypto.so.1 not found
This kind of error is common on Linux, and it can usually be solved by installing the necessary libraries.
GCC will be using dynamic linking by default. It means the program and the library will be loaded to memory during execution time and linked. If you want to compile it as static executable, you have to add -static
to the compiler options when compiling.
Let’s explore some dynamically linked binaries.
ldd
command can be used to show the libraries that the binary is linked. In this case, it shows the dependencies of ls
binary.
You can see the shared objects of the library ls
requires, for instance, wlibc.so.6
, and the path of the .so
file on the system. There is one interesting thing about the picture, why does ls
needs libpthread
? I can’t think of any reason why ls
would need multi threading.
In CTFs, you might see the following types of c program source code. This is simplified to show the LD_PRELOAD misuse. Let’s go ahead and compile the code.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char passwd[] = "p455w0rd";
if (argc < 2) {
printf("Usage: %s <password>\n", argv[0]);
return 0;
}
if (strcmp(passwd, argv[1]) == 0) {
printf("You pwned the process, congratz\n");
printf("Flag is {LD_PRELOAD_rulz}\n");
return 1;
}
printf("Wrong password!\n");
printf("Access Denied\n");
return 0;
}
Notice that many c programs need libc, as it contains the functions like printf
.
This is a very simple program, that if you execute with a correct password, it will print the flag, else it will print wrong password.
The function we need to mess around is the strcmp()
function. If this function returns 0, then it will successfully print the flag. So, let’s bypass this by misusing LD_PRELOAD
.
First of all, save the following code as hijack.c
. It is a very simple function with the same function name and parameters as the standard strcmp()
function, but it will do nothing but return 0. This will be overwriting the strcmp()
later on.
int strcmp(const char *s1, const char *s2) {
return 0;
}
Compile the code as shared object
gcc -fPIC -c hijack.c -o hijack.o
gcc -shared -o hijack.so hijack.o
Now it is the exciting part, the exploitation.
What this does is that it will define the LD_PRELOAD
variable, and execute the program with xxx
as input. In normal cases, this will not print the flag, because the input is not the correct password.
But due to the LD_PRELOAD
, when the program asks for strcmp()
function, it loads that function from hijack.so
file, which of course returns 0. Thus PWNED.
This trick is useful in doing CTF challenges and it can lead to local privilege escalation. It is also useful in trying to do dynamic analysis of malware, etc, because you can easily write a function wrapper to tap into the function calls of the programs.
Reference urls
Reverse Engineering with LD_PRELOAD
c - Exploiting SUID files with LD_PRELOAD and IFS - Stack Overflow
The magic of LD_PRELOAD for Userland Rootkits