A simple way to hack C/C++ application

Vishal Chovatiya - Sep 30 '19 - - Dev Community

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)());
}
Enter fullscreen mode Exit fullscreen mode
  • 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
}
Enter fullscreen mode Exit fullscreen mode

main.c

#include <stdio.h>
#include <stdlib.h>

int main()
{
  malloc(1);
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

Creating a shared library

$ gcc -o malloc.so -shared -fPIC malloc.c -D_GNU_SOURCE
Enter fullscreen mode Exit fullscreen mode

Linking & executing the main application

$ gcc -o main main.c ./malloc.so -ldl
$ ./main
Our Malloc
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

How it works

  • When you compile main.c with gcc -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)
Enter fullscreen mode Exit fullscreen mode
  • 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

[Click here to read more. . .]

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .