Saturday, December 8, 2012

Fighting a Userland Rootkit


Rootkit is a piece of software that alters basic OS functions in purpose of hiding its’ deeds. The rootkit is not necessarily a part of a malware, but it sure always smells like one. To alter the OS behavior from inside, kernel code (ring0) is required. In the WIndows world it means three things:

  1. The developer must be well trained in art of code writing, because kernel code is considered harder to develop.
  2. The user better have a certificate to sign his code, I recommend the Realtek private certificate for the job.
  3. A good understanding of the so-called Patch Guard mechanism, that is especially made to fight rootkits of that kind.

These requirements and more, have pushed the ill-trained rootkit developers back to userland.
The userland rootkit achieves its purpose by hooking the Win32 APIs. To explain that, lets consider an example in which a userland rootkit is installed on a system. Lets say that the main executable of the rootkit is named kitty.exe and it is stored in the system32 folder. Now the user uses explorer to look at the files found under system32 folder, and somehow he doesn’t see any kitty.exe file. What happened here? No, it is not a black voodoo magic, it is simply a hook that is set on the kerenl32.dll API FindFirstFile API that filters out the kitty.exe file from the results.
So what can we do to fight this sea monster if we can’t even see it? The FindFirstFile API implemented in the kernel32.dll is a function that utilizes the lower level undocumented ntdll API called NtQueryDirectoryFile, and few others. We can write a dir command that uses the low level API to query the content of the folder. Though it is possible that the rootkit is one step ahead of us, and is actually hooking the low level API.
Lets consider what the NtQueryDirectoryFile API is doing. If one disassembles the NtQueryDirectoryFile API, he would find that it doesn’t do much besides putting some magic number in the EAX register and jumping into a specially crafted address in memory which is called the sysgate (usally 0x7ffe0300). The special address contains the single opcode of "sysenter". The magic number in EAX, is called the system service id, which is a unique number for a system function. The sysenter opcode is the portal that takes the execution from userland to kernelland. So no matter what kind of hooking is done in userland, nothing can affect the code execution that is to follow the execution of sysenter.
Writing a tool that uses the syscalls directly without going through ntdll or kernel32 is possible, but very  unrecommended, as the system services are undocumented in Windows, and it is a bit more complicated than what is done in Linux systems. Nevertheless, I’m sure someone has  already done it, so please, do share if you have any knowledge of such.
One might say that using the syscalls directly is a great overhead for the task. If so, lets examine the boundaries of the userland rootkit before we proceed, to see if we have any other way to fight it. An attempt to look for the process in the Taskmanager or Sysinternals Process Explorer could fail simply because the APIs those tools are using are found in kernel32.dll and therefore can be hooked. The files and reg keys could be hidden in the same way, and so can any other kind of system query.
Most debuggers used on Windows platform use the Win32 debug api. Debuggers like that are ollydbg, IDA and my very own NativDebugging python scripts all use the debug API. For a userland rootkit it is easy to fool such debuggers, it can hook the OpenProcess API not allowing the debugger to attach to the rootkit process, it can change the results of ReadProcessMemory to reflect incorrect memory queries or it can simply hide itself from the processes list.
Therefore all of our tools are just useless in the battle against the userland rootkit. We are left with two options:

  1. Use kernel debugger such as Windbg.
  2. Undo the hooking of the rootkit to regain the power of our arsenal.

Kernel debugging, is a big cannon to fight a small fish but it would work. I’ll explain the second option using my NativDebugging Python module and Python.
There are 4 steps to this option:

  1. Inject into a target process a new copy of ntdll.dll with a different name (ntcopy.dll in my case).
  2. Parse the exports table of the NtDll.dll
  3. Enumerate all the other loaded modules
  4. Redirect any import from ntdll.dll to CopyDll.dll.

Note that I’m not trying to fight the rootkit directly, but to clean a single process that would later let me kill the rootkit in a more fashionable manner. The rootkit might also block the debug APIs in general, what would require few changes to the NativDebugging system, to use the system services directly.
To make this blog post shorter, I’ll redirect the curious readers to the source code of the script that does all of the above: https://svn3.xp-dev.com/svn/nativDebugging/trunk/samples/PatternsSample.py
To manipulate the import / export of modules I’ve created a PE pattern that is found at:
https://svn3.xp-dev.com/svn/nativDebugging/trunk/src/Win32/PEPattern.py
more about patterns can be found in my presentation of “Looking Into The Eyes of The Bits”

More references:
My NativDebugging python module: https://svn3.xp-dev.com/svn/nativDebugging/trunk/
Installed by “python setup.py install
More about PE structure
http://code.google.com/p/corkami/wiki/PE101

Cheers,
Assaf

No comments:

Post a Comment