Questions about this topic? Sign up to ask in the talk tab.

Difference between revisions of "LD Preload"

From NetSec
Jump to: navigation, search
(Reverted edits by Hatter (talk) to last revision by Desmosedici)
Line 1: Line 1:
 
==Introduction==
 
==Introduction==
  
LD_PRELOAD in simplest terms is a way to "preload" a shared library. It's an option you pass to ld either using a config or environment variable. It will be called, and overrides LIBC:
+
LD_PRELOAD in simplest terms is a way to "preload" a shared library. It's an option you pass to ld either using a config file or environment variable. It will be called, and overrides LIBC:LD_PRELOAD=./yourso.so some_command.It has to be an absolute path, you cannot do:
LD_PRELOAD=./yourso.so some_command
+
It has to be an absolute path, you cannot do:
+
 
LD_PRELOAD=yourso.so
 
LD_PRELOAD=yourso.so
Unless its in your ld path.
+
Unless it is in your ld path.
  
LD preload is built for runtime linking. It allows things like gcc, etc to link c0de and binaries against an object, shared object, or executable file. To keep it short & sweet, "printf()", is a part of LibC or glibc. libc.so.#.#.#, basically has the printf() function built-in, it also has an export table which contains addresses.
+
LD preload is built for runtime linking. It allows things like gcc, etc to link code and binaries against an object, shared object, or executable file. To keep it short & sweet, "printf()", is a part of LibC or glibc. libc.so.#.#.#, has the printf() function built-in, it also has an export table which contains addresses.
  
Shared libraries are libraries SHARED by multiple executables, theyre linked run time, not statically when the program is built, and executed at realtime. For function names, i.e. 8if you want to use printf and 20 programs use it, a shared lib lets all the same progs access the same address to run printf().
+
Shared libraries are libraries shared between multiple executables. They are linked dynamically at runtime instead of statically during compilation. For function names, a shared library lets multiple programs access the same function address instead of loading multiple instances of the same function into memory. This is done primarily for system performance as redundant copies of a function would unnecessarily consume system resources. LD_PRELOAD allows you to tell the runtime linker to load the specified shared library "first" giving it precedence over every other library.
  
The address from the export table is stored in either a DWORD or a QWORD depending on CPU arch and ram accessibility. Shared libs exist for performance, that way you don't load the same function multiple times, shared libs are shared by multiple executable, stored once in memory. LD_PRELOAD is a feature that lets you tell your runtime linker to load your so "first".
+
LD_PRELOAD use is generally innocuous; tsocks and checkinstall use it among many other programs. However, it can easily be abused. There is a config file that usually lives in, "/etc/ld.so.preload", which will globally preload the contents. It contains the paths to shared libraries to preload. As a result, the entire userland, save statically compiled applications (rare), will use your overrided functions.The function address is stored in either a DWORD or a QWORD (depending on CPU arch and ram accessibility) and can be extracted from the export table using the function dlsym() .  
 
+
Basically it has precidence over other libs so if two printf() functions are loaded, yours is the one in the export table.  
+
 
+
tsocks uses ld_preload, checkinstall(generates packages on linux) uses ld_preload however, im sure you can see how this can be abused. There is a config file that usually lives in, "etc/ld.so.preload", which will globally preload the contents. You put the paths to preloaded shared libs in there so essentially the entire userland, save statically compiled applications (rare), will use your overrided functions.
+
  
 
There are a few other nasty things you can do, hooking ssh, hooking gpg, anywhere a user would enter sensitive information. You can preload functions like strcpy() etc, dump to files and you can retrieve the information later. It's just a matter of cracking open the target application and finding what calls are made on the sensitive data.
 
There are a few other nasty things you can do, hooking ssh, hooking gpg, anywhere a user would enter sensitive information. You can preload functions like strcpy() etc, dump to files and you can retrieve the information later. It's just a matter of cracking open the target application and finding what calls are made on the sensitive data.
  
 
==Simple Practical Example==
 
==Simple Practical Example==
If you have an app that asks for a users password then hashes it and compares it to a stored value.  
+
If you have a program that asks for a users password then hashes it and compares it to a stored value.  
Lets say it calls <syntaxhighlight lang="c">strcpy(password, user_input);</syntaxhighlight>
+
Lets say it calls: <syntaxhighlight lang="c">strcpy(password, user_input);</syntaxhighlight>
  
You hook strcpy so you'd grab the definition of strcpy: char *stpcpy(char *s1, const char *s2);.
+
You hook strcpy so you'd grab the definition of strcpy:<syntaxhighlight lang="c">char *stpcpy(char *s1, const char *s2);</syntaxhighlight>
  
Notice strcpy() is defined as: <syntaxhighlight lang="c"> char *strcpy()</syntaxhighlight> as opposed to  <syntaxhighlight lang="c">char strcpy()</syntaxhighlight> That's what allows the hook to work!
+
Notice strcpy() is defined as: <syntaxhighlight lang="c"> char *strcpy()</syntaxhighlight> as opposed to  <syntaxhighlight lang="c">char strcpy()</syntaxhighlight> Strcpy is a pointer, and because of this, we can hook and override it.
  
The definition has to be the same,  due to pointers.
 
  
Open up the man page, copy pasta it out. So then you would add some code to write the second argument to a file.
+
Open up the man page and copy the function definition then add code to write the second argument to a file. So the preloaded library now contains:
  
LD preload loads before any other shared libs, so functions can be overridden i.e. you can conflict with libc and not have issues.
+
<syntaxhighlight lang="c">char *stpcpy(char *s1, const char *s2){
 
+
FILE *fp = fopen(your_evil_filepath, 'a');
<syntaxhighlight lang="c">FILE *fp = fopen(your_evil_filepath, 'a');
+
 
fpritnf(fp, "%s\n", s2);
 
fpritnf(fp, "%s\n", s2);
fclose(fp);</syntaxhighlight>
+
fclose(fp);
 +
}</syntaxhighlight>
  
Now, there is an obvious problem with this, strcpy doesn't work anymore and all the things will break, thus ending your ruse if the app crashes. So, what you have to do is: call the original, working version of strcpy or implement your own, since strcpy is simple.  
+
Now, there is an obvious problem with this, strcpy doesn't work anymore and all the things that use it will break. So, what you have to do is either call the original, working version of strcpy or implement your own, since strcpy is simple. The former is preferable in most cases as it usually requires less code than rewriting the entire function. Unfortunately, calling the overridden function from within the preloaded library will not work. In order to call the original function, a new function that points to the original function must be declared. Fortunately, this is simple to do.  
  
I'm going to show you how to get the original address of strcpy since that works for more complicated calls you cant just rewrite in 30 seconds. If you haven't already, grab the jynxkit source. Crack open the source, take a look at ld_poison.c. You'll find everything works with pointer overrides.
+
For strcpy, the function pointer prototype is:
 +
<syntaxhighlight lang="c">char *(*strcpy)(char *dest, const char *src);</syntaxhighlight>
  
Take "readdir" for example. <syntaxhighlight lang="c">static struct dirent *(*old_readdir)(DIR *dir);</syntaxhighlight> This is a function pointer. If u know what pointers are, this is just a pointer, except it points to a function. The prototype matches that of readdir.
+
Notice that because this is a function pointer there are two *. Also notice that the prototype matches strcpy's prototype. Now the function pointer must point to the original function. To do this, use the ld library function dlsym. Which is defined as:
 
+
So if we're doing strcpy it would look like:
+
<syntaxhighlight lang="c">char *(*strcpy)(char *dest, const char *src);</syntaxhighlight>
+
Notice the two *. So now, you have to actually point your nice new pointer to the old function.
+
To do this, you'll use the ld library function dlsym. So:
+
 
<syntaxhighlight lang="c">void *dlsym(void *handle, const char *symbol);</syntaxhighlight>
 
<syntaxhighlight lang="c">void *dlsym(void *handle, const char *symbol);</syntaxhighlight>
The handle is the library youre using, the symbol is the name of the function you want to use RTLD_NEXT as your handle.  
+
The handle is the name of the library containing the original function; the symbol is the name of the function. There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find the first occurrence of the desired symbol using the default library search order. The latter will find the next occurrence of a function in the search order after the current library. This allows one to provide a wrapper around a function in another shared library.  
  
There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find the first occurrence of the desired symbol using the default library search order. The latter will find the next occurrence of a function in the search order after the current library. This allows one to provide a wrapper around a function in another shared library.
+
So to obtain a copy of strcpy that will persist after strcpy is overridden:
  
 
<syntaxhighlight lang="c">char *(*old_strcpy)(char *dest, const char *src);
 
<syntaxhighlight lang="c">char *(*old_strcpy)(char *dest, const char *src);
 
old_strcpy = dlsym(RTLD_NEXT, "strcpy");</syntaxhighlight>
 
old_strcpy = dlsym(RTLD_NEXT, "strcpy");</syntaxhighlight>
  
Now you can do:
+
Now the strcpy in the preloaded library looks like:
<syntaxhighlight lang="c">return old_strcpy(s1, s2);</syntaxhighlight>
+
<syntaxhighlight lang="c">char *stpcpy(char *s1, const char *s2){
At the end of your fake out strcpy function, and it will behave like it should and not crash.
+
FILE *fp = fopen(your_evil_filepath, 'a');
 +
fpritnf(fp, "%s\n", s2);
 +
fclose(fp);
 +
return old_strcpy(s1, s2);
 +
}</syntaxhighlight>
 +
 
 +
It will now appear to behave normally.
  
 
==Implementations==
 
==Implementations==
 
[[Jynx Rootkit]]
 
[[Jynx Rootkit]]

Revision as of 20:59, 18 October 2011

Introduction

LD_PRELOAD in simplest terms is a way to "preload" a shared library. It's an option you pass to ld either using a config file or environment variable. It will be called, and overrides LIBC:LD_PRELOAD=./yourso.so some_command.It has to be an absolute path, you cannot do: LD_PRELOAD=yourso.so Unless it is in your ld path.

LD preload is built for runtime linking. It allows things like gcc, etc to link code and binaries against an object, shared object, or executable file. To keep it short & sweet, "printf()", is a part of LibC or glibc. libc.so.#.#.#, has the printf() function built-in, it also has an export table which contains addresses.

Shared libraries are libraries shared between multiple executables. They are linked dynamically at runtime instead of statically during compilation. For function names, a shared library lets multiple programs access the same function address instead of loading multiple instances of the same function into memory. This is done primarily for system performance as redundant copies of a function would unnecessarily consume system resources. LD_PRELOAD allows you to tell the runtime linker to load the specified shared library "first" giving it precedence over every other library.

LD_PRELOAD use is generally innocuous; tsocks and checkinstall use it among many other programs. However, it can easily be abused. There is a config file that usually lives in, "/etc/ld.so.preload", which will globally preload the contents. It contains the paths to shared libraries to preload. As a result, the entire userland, save statically compiled applications (rare), will use your overrided functions.The function address is stored in either a DWORD or a QWORD (depending on CPU arch and ram accessibility) and can be extracted from the export table using the function dlsym() .

There are a few other nasty things you can do, hooking ssh, hooking gpg, anywhere a user would enter sensitive information. You can preload functions like strcpy() etc, dump to files and you can retrieve the information later. It's just a matter of cracking open the target application and finding what calls are made on the sensitive data.

Simple Practical Example

If you have a program that asks for a users password then hashes it and compares it to a stored value. Lets say it calls: <syntaxhighlight lang="c">strcpy(password, user_input);</syntaxhighlight>

You hook strcpy so you'd grab the definition of strcpy:<syntaxhighlight lang="c">char *stpcpy(char *s1, const char *s2);</syntaxhighlight>

Notice strcpy() is defined as: <syntaxhighlight lang="c"> char *strcpy()</syntaxhighlight> as opposed to <syntaxhighlight lang="c">char strcpy()</syntaxhighlight> Strcpy is a pointer, and because of this, we can hook and override it.


Open up the man page and copy the function definition then add code to write the second argument to a file. So the preloaded library now contains:

<syntaxhighlight lang="c">char *stpcpy(char *s1, const char *s2){ FILE *fp = fopen(your_evil_filepath, 'a'); fpritnf(fp, "%s\n", s2); fclose(fp); }</syntaxhighlight>

Now, there is an obvious problem with this, strcpy doesn't work anymore and all the things that use it will break. So, what you have to do is either call the original, working version of strcpy or implement your own, since strcpy is simple. The former is preferable in most cases as it usually requires less code than rewriting the entire function. Unfortunately, calling the overridden function from within the preloaded library will not work. In order to call the original function, a new function that points to the original function must be declared. Fortunately, this is simple to do.

For strcpy, the function pointer prototype is: <syntaxhighlight lang="c">char *(*strcpy)(char *dest, const char *src);</syntaxhighlight>

Notice that because this is a function pointer there are two *. Also notice that the prototype matches strcpy's prototype. Now the function pointer must point to the original function. To do this, use the ld library function dlsym. Which is defined as: <syntaxhighlight lang="c">void *dlsym(void *handle, const char *symbol);</syntaxhighlight> The handle is the name of the library containing the original function; the symbol is the name of the function. There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find the first occurrence of the desired symbol using the default library search order. The latter will find the next occurrence of a function in the search order after the current library. This allows one to provide a wrapper around a function in another shared library.

So to obtain a copy of strcpy that will persist after strcpy is overridden:

<syntaxhighlight lang="c">char *(*old_strcpy)(char *dest, const char *src); old_strcpy = dlsym(RTLD_NEXT, "strcpy");</syntaxhighlight>

Now the strcpy in the preloaded library looks like: <syntaxhighlight lang="c">char *stpcpy(char *s1, const char *s2){ FILE *fp = fopen(your_evil_filepath, 'a'); fpritnf(fp, "%s\n", s2); fclose(fp); return old_strcpy(s1, s2); }</syntaxhighlight>

It will now appear to behave normally.

Implementations

Jynx Rootkit