This is a cross-post from my blog
While I was working as a core C library developer with my previous employer. I came across this RTLD_NEXT flag in dynamic linking which has the amazing capability and can be easily exploited or used for the unethical purpose(Here I intend to educate the developer to don’t be victims). In this article, I will show you a simple way to hack C/C++ application using RTLD_NEXT with an easy example.
Intro to RTLD_NEXT
- Library linking & symbol resolution i.e. extracting address(precisely offset here in dynamic linking case) of function is specified at compile time.
- For example, there are four shared libraries linked & loaded dynamically in order as A.so, B.so, C.so & D.so with the main application. And funcXYZ() is called from the main application which is defined in both the library C.so & D.so with the same prototype.
- Then funcXYZ() from C.so will be called first as it’s ahead of D.so in linking order.
- But what if you want to call funcXYZ() from D.so ? You can achieve this by RTLD_NEXT flag defined in . What you have to do is define your funcXYZ() as below in C.so:
void funcXYZ()
{
void (*fptr)(void) = NULL;
if ((fptr = (void (*)(void))dlsym(RTLD_NEXT, "funcXYZ")) == NULL)
{
(void)printf("dlsym: %s\n", dlerror());
exit(1);
}
return ((*fptr)());
}
- Now, whenever funcXYZ() called from main application it will come to C.so which simply search for the same symbol from next loaded libraries i.e. D.so .
- dlsym() search for symbol provided in argument from the memory and a returns function pointer to the same. ## Let’s hack C/C++ application using RTLD_NEXT
malloc.c
#include <stdio.h>
#include <dlfcn.h>
void *malloc(size_t size)
{
static void *(*fptr)(size_t) = NULL;
/* look up of malloc, only the first time we are here */
if (fptr == NULL)
{
fptr = (void *(*)(size_t))dlsym(RTLD_NEXT, "malloc");
if (fptr == NULL)
{
printf("dlsym: %s\n", dlerror());
return NULL;
}
}
printf("Our Malloc\n");
return (*fptr)(size); // Calling original malloc
}
main.c
#include <stdio.h>
#include <stdlib.h>
int main()
{
malloc(1);
return 0;
}
Creating a shared library
$ gcc -o malloc.so -shared -fPIC malloc.c -D_GNU_SOURCE
Linking & executing the main application
$ gcc -o main main.c ./malloc.so -ldl
$ ./main
Our Malloc
Note: You can also use LD_PRELOAD as below, which loads the specified library first. No need to mention ./malloc.so explicitly in the compilation.
$ LD_PRELOAD=`pwd`/malloc.so ./main
How it works
- When you compile
main.c
withgcc -o main main.c ./malloc.so -ldl
, you specify malloc.so explicitly on first order. We can verify this by ldd command
$ ldd main
linux-vdso.so.1 => (0x00007fff37bf4000)
malloc.so (0x00007fc5df598000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fc5df37d000)
libc.so.6 => /lib64/libc.so.6 (0x00007fc5defbb000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc5df79b000)
- So when you call malloc it will refer the first occurrence of the symbol from the loaded library sequence which is in our malloc.so library.
- We now extract original malloc from next loaded shared library which is /lib64/libc.so.6. Vulnerability & precautions you should consider