Just Another Geek

I am blogging about Information Security since 2003

02 Nov 2011

ld-linux.so ELF hooker

TL;DR
Stéphane and myself are releasing ag new tool injecting code at runtime, just between the ELF loader and target binary. It is an alternative to LD_PRELOAD, just a little bit more intrusive but 100% reliable :)\

Sources were released on Github




When a binary is execve(), the kernel extracts from the ELF headers the interpreter to be launched, usually  /lib/ld-linux.so.2The kernel creates a new process and prepares the environment (arguments and auxiliary data). The target ELF entry point is set in auxiliary vector of type “ENTRY”.


Then the kernel opens the requested interpreter, maps the memory regions and start its execution at ld’s ELF entry point. Then the loader analyzes the target ELF file, performs its loader work and sets EIP to target ELF entry point (extracted from auxv). At this point, main()’s program is eventually executed.


Our goal was to permit the execution of code for arbitrary dynamically linked binary without patching each of them. So our interest moved on the loader, the common point between most executables. Thus, we decided to patch a normal ld in order to inject code. My awesome colleague, Stéphane Duverger (the ramooflax author!) and myself wrote ld-shatnerIts task is to patch ld-linux.so file accordingly:
\

  1. After ELF header, we shift “ELF program header” a few pages away
  2. In this new section, we inject a “loader routine” (hooked.s) and embedded code to be executed at runtime
  3. After having been saved in our section, ld’s ELF entry point is overwritten to jump directly on our routine. This routine extracts from auxiliary vectors the target ELF entry point and overwrites it with a pointer to our embedded code (func() in the payload).
  4. Original ld’s entry point is called and ld works as usual
  5. Eventually, it calls entry point set in auxiliary vector (which was replaced by a pointer to our payload)
  6. Embedded code runs
  7. It returns to our routine which finally jumps on original target entry point

Some pictures before/after ld-shatner voodoo:


ld-shatner voodo


Screenshot

$ make clean all
$ cp /lib/ld-linux.so.2 /bin/ls .
$ ./ld-shatner ld-linux.so.2 obj.elf
$ sudo cp ld-hook.so /lib/
$ ./interpatch ls
$ ./ls
ld-hook <---------------------- output of obj.elf
[...]


(Ok, we cheat for the moment because we have to patch ls binary but we will not have to do that eventually)\

So what?

My ultimate goal for ld-shatner is to use this method for starting applications in my sandbox project, seccomp-nurse. For the moment, I rely on LD_PRELOAD feature but this approach is… hackish and I have to work around some bugs because of this special context…