Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
programming your own rootkit in C via the LD_PRELOAD trick
#1
I wrote this and I have shared it on multiple forums.

I wrote this to share what I was learning, I'm not a fucking expert, criticize me all you want, and since I don't have much to share here, have this.
Ugh, luakit crashed so I'm having to rewrite this entire tutorial from scratch, I'm going to try and detail this as much as possible.
What is LD_PRELOAD?
Exactly what it sounds like, LD_PRELOAD points to a shared library and loads it before any other libraries, allowing you to overwrite predefined libraries or instructions before anything is actually executed. LD_PRELOAD is present in many modern rootkits such as Azazel and Jynx2, I'll leave the github pages for Azazel and Jynx2 at the bottom of this guide.

How does this help?
I'm hoping that you have a tiny idea of how rootkits operate and how they work and whatnot. Well using LD_PRELOAD you can overwrite libraries, so you can replace already existing libraries with your own, either to cause damage or to patch a vulnerability, whatever the fuck you want, I don't care.

How are these libraries installed?
Let's take a look at the install script I made for panther and procedurally break down each step it goes through to make this easy.
[Image: zaafri.png]
script opens
script identifies which uid is executing the script, if = 0 continue, else exit
dynamically compile with all warnings, library inclusion allowed, don't link the output and output the bin as panther.o
compile the dynamic bin as a shared library, local lib inclusion allowed, passing -soname to the linker, linking libvimcore.so, linking the dl lib, outputting as libvimcore.so, and target bin panther.o
skip these 2 lines
installing the shared library under read/write/execute (owner) + read/execute (other users) (755) into the /lib/ directory
pointing LD_PRELOAD to the newly installed library so that /lib/libvimcore.so loads before any other library
script notifies successful injection, removes compilation results and exits

My script is just a bash script replication of Azazel's Makefile, nothing else.

Let's take a look at how you overwrite a syscall.

First, you need to know what syscall you're wanting to overwrite, in this case, let's make any non-directory file starting with "__" in its name unable to be removed by rm. To do this, we need to find out what calls rm references, to do this, we'll use the tool "ltrace", similar to strace but it traces library calls and not syscalls. Syntax of ltrace is simple, "ltrace /path/to/file optional arguments", and any calls are output to stdout. Let's find out what calls rm references by using ltrace and removing an empty file.

[Image: ssbllb.png]

Fun things start to happen at the faccessat() and unlinkat() calls, if you man unlinkat, you see that it takes 3 arguments, a file descriptor, a pathname to unlink, and flags, and it is this exact call that we need to overwrite to disallow any user from removing our file. To overwrite a syscall, you need to create C source file, defined _GNU_SOURCE, include any includes mentioned on your target call's and copy the construct on the man page too. It's simple, here's an example.

[Image: ommttb.png]

Return a value of whatever the fuck you want in this, I don't care.

Okay, sure, the syscall has been overwritten now, but if you compile and execute this via a shared library + LD_PRELOAD, you won't be able to remove any files, that's not what we want. If you want to overwrite the old syscall with a new one that calls the old syscall (if that makes any sense), we can use dlsym, this was linked earlier by the -l argument in the installation script. dl* are "interfaces to the dynamic linking library". Read more about it here: http://linux.die.net/man/3/dlsym
Using dlsym() you can call the original syscall even after you overwrite it.

An example as follows.

[Image: sighdg.png]

Using typeof() automatically grabs the data type of target call and then creates it using that said data type. Keep note of me passing the arguments from the new call into the old call too. The RTLD_NEXT flag just reserves the call for future use.

Now if you were to compile this and execute it via a shared library + LD_PRELOAD, the new syscall will be overwritten and it will return the old syscall - if you want to make sure that the new syscall is indeed being called, you can use printf to display a significant message when a file is removed.

Okay, so you've overwritten a syscall and you're returning the old syscall into the new one, but this isn't very beneficial as to what we want, which is to remove the ability to remove files that start with "__". To do this, we'll use the header include "stdbool", which = standard boolean, simple stuff, and strncmp, which could be your equivalent to string.startsWith() in a higher level language like Java.

[Image: sgsiot.png]

What the boolean type isHidden() is doing is checking if the supplied char a starts with MAGIC_STRING, in this case MAGIC_STRING was defined as "__", and if a starts with "__", the boolean returns true and thus the char is hidden, else the boolean returns false and the supplied char is not that of a hidden file. Now if you compile and execute this as a shared library via LD_PRELOAD, all users, root and non-root, will be unable to remove any files starting with "__".
Azazel github: https://github.com/chokepoint/azazel
Jynx2: https://github.com/chokepoint/Jynx2

i wrote this a year ago, go away, shoo u vile scum
lillix@jabber.se
now go away
ty
Reply
#2
what colorscheme are you using?
Reply
#3
(10-14-2014, 05:57 PM)zooty Wrote:  what colorscheme are you using?

i dont remember, and i cant check rn
lillix@jabber.se
now go away
ty
Reply
 


Forum Jump:


Users browsing this thread: 2 Guest(s)

About The Bytecode Club

We're a community forum focused on Reverse Engineering, we try to target Java/Android but we also include other langauges/platforms. We pride ourselves in supporting and free and open sourced applications.

Website