Year after year it seems that Santa is intent on sending me coal, but little does he know that this year I already have access to one of his Linux machines and I'm going to make sure that I at least deserve to get my fair share of black rocks. I decided to dig into the world of Linux rootkits and long-term footholds with evasion techniques; this is an area where lots of previous research has been done, but in traditional bootstrapper fashion I decided to start writing a backdoor from scratch with the long-term goal of making a rootkit that would be suitable for penetration tests instead of just heavy evasiveness and taking advantage of multiple rootkit methods.
Oftentimes as a penetration tester I gain root-level access to a system through a complex set of steps, but then find myself in a position where it is difficult to secure a long-term foothold with easy access back into the system, or without tripping up additional monitoring solutions. I wanted to create a rootkit that gave me unrestricted access over a secure channel, cleaned itself up after an engagement, evaded common detection techniques (while also allowing for retainment of detailed logs to send to clients post-engagement), and eventually offered tight Metasploit integration.
The first step was to do research and create a small proof-of-concept that would help me understand the scope of the space and the future project. That's how the trip down the chimney started.
A general rule of thumb is that if something runs with elevated privileges, someone has found a way to abuse it and use it for escalating privileges. When looking into prior open-source implementations, however, I quickly noticed that there were two kinds of rootkits that were most common:
- Kernel module rootkits: These are straightforward. They insert kernel modules into a system, giving fairly unrestricted access and allowing for very high levels of evasion.
- ld.so userland hooking rootkits: Most Linux dynamic linkers load shared objects from a defined path, but also first load shared objects from files defined in
/etc/ld.so.preload. This class of rootkits injects a shared object (.so) file into all dynamically linked processes and allows for the rootkit developers to hook into any system calls.
While both of these are extremely interesting, I quickly discovered the Jynx2 and Azazel projects to be the most fully-featured, weaponized, and easiest to understand for a Christmas weekend research project.
The Night Before
After taking the knowledge learned from the latter projects it was time to start tinkering by just writing code. Knowing that Go supported C export functions as well as building ELF shared objects, I chose that as my toolkit to attempt the project and started designing the application. For this simple example I kept it easy and did a bind shell over a secure channel using the Noise protocol, and specifically the NoiseSocket extension, to handle authentication and encryption on the bind shell. Compile time arguments were used to embed the mutually authenticated keys; the configuration is hardcoded into the shared object to keep it even more simple and minimize parsing. Additionally simply checking the system time and comparing it to the hardcoded time for the length of an engagement allows for naive but simple hooks for the end of an engagement—for example, removing the shared objects and the configuration entries.
Earning My Coal
Many hours of debugging and fighting linking later, everything started to take shape. A working example with debugging enabled and injection into the
ld.so.preload showed promising results.
Disabling debugging made things even more promising, as there is no more output to the user.
For the more curious, a copy of this proof-of-concept, the laundry list of caveats, and a list of references I used for research can be found in this repository (eggnog not included).
Many things that I experimented with did not make it into this example, such as; accept() hooks with lookahead or Jynx2 like source port checks, evasion techniques, logging, dynamic configuration, reverse shells with meterpreter support, executable shared object to allow for self-installation, actual privilege escalation, Go plugin support, tests, so many more tests, and more. Many of the requirements for a proper and stable penetration testing rootkit were not met, but it was a fun project that proved to be both challenging and promising. It definitely inspires me to continue down this line of research. Saint Nick might be able to get away with giving me coal this year, but I’d bet he won’t next year! Stay tuned for more HaXmas miracles.
Get the full complement of HaXmas hijinks here.