LWN.net Logo

A rootkit dissected

By Jake Edge
November 21, 2012

A recently discovered Linux rootkit has a number of interesting attributes that make it worth a look. While it demonstrates the power that a rootkit has (to perform its "job" as well as hide itself from detection) this particular rootkit also has some fairly serious bugs—some that are almost comical. What isn't known, at least yet, is how the system where it was discovered became infected; there is no exploit used by the rootkit to propagate itself.

The rootkit was reported to the full-disclosure mailing list on November 13 by "stack trace". Customers had noticed that they were being redirected to malicious sites by means of an <iframe> in the HTTP responses from Stack Trace's site. Stack Trace eventually found that the Nginx web server on the system was not delivering the <iframe> and tracked it to a loadable kernel module, which was attached to the posting. Since then, both CrowdStrike and Kaspersky Lab's Threatpost have analyzed the module's behavior.

The first step for a rootkit is to get itself running in the kernel. That can be accomplished by means of a loadable kernel module. In this case, the module is targeted at the most recent 64-bit kernel version used by Debian 6.0 ("Squeeze"):

    /lib/modules/2.6.32-5-amd64/kernel/sound/module_init.ko 
The presence of that file would indicate infection, though a look at the process list is required to determine if the rootkit is actually loaded. Once loaded, the module has a number of different tasks to perform that are described below. The CrowdStrike post has even more detail for those interested.

The rootkit targets HTTP traffic, so that it can inject an <iframe> containing an attack: either a malicious URL or some kind of JavaScript-based attack. In order to do that in a relatively undetectable way, it must impose itself into the kernel's TCP send path. It does so by hooking tcp_sendmsg().

Of course, that function and other symbols that the rootkit wants to access are not exported symbols that would be directly accessible to a kernel module. So the rootkit uses /proc/kallsyms to get the addresses it needs. Amusingly, there is code to fall back to looking for the proper System.map to parse for the addresses, but it is never used due to a bug. Even though the kernel version is hardcoded in several places in the rootkit, the System.map helper function actually uses uname -r to get the version. The inability to fall back to checking System.map, along with this version-getting oddity make it seem like multiple people—with little or no inter-communication—worked on the code. Other odd bugs in the rootkit only add to that feeling.

For example, when hooking various functions, the rootkit carefully saves away the five bytes it needs to overwrite with a jmp instruction, but then proceeds to write 19 bytes at the start of the function. That obliterates 14 bytes of code, which eliminates any possibility of unhooking the function. Beyond that, it can't call the unhooked version of the function either, so the rootkit contains private copies of all the functions it hooks.

Beyond hooking tcp_sendmsg(), the rootkit also attempts to hide its presence. There is code to hide the files that it installs, as well as its threads. The file hiding works well enough by hooking vfs_readdir() and using a list of directories and files that should not be returned. Fortunately (or unfortunately, depending on one's perspective), the thread hiding doesn't work at all. It uses the same file-hiding code, but doesn't look in /proc nor convert the names into PIDs, so ps and other tools show the threads. In the original report, Stack Trace noted two threads named get_http_inj_fr and write_startup_c; those names are fairly descriptive given the behavior being seen. The presence of one or both of those names in the process list would mean that the system has the rootkit loaded.

The rootkit does successfully remove itself from the list of loaded modules. It directly iterates down the kernel's module list and deletes the entry for itself. That way lsmod will not list the module, but it also means that it cannot be unloaded, obviating the "careful" preparations in the hooked functions for that eventuality.

As with other malware (botnets in particular), the rootkit has a "command and control" client. That client contacts a particular server (at a hosting service in Germany) for information about what to inject in the web pages. There is some simple, weak encryption used on the link for both authentication and obfuscation of the message.

Beyond just missing a way to propagate to other systems, the rootkit is also rather likely to fail to persist after a reboot. It has code to continuously monitor and alter /etc/rc.local to add an insmod for the rootkit module. It also hooks vfs_read() to look for the exact insmod line and adjusts the buffer to hide that line from anyone looking at the file. But it just appends the command to rc.local, which means that on a default installation of Debian Squeeze it ends up just after an exit 0 line.

Like much of the rest of the rootkit, the HTTP injection handling shows an odd mix of reasonably sensible choices along with some bugs. It looks at the first buffer to be sent to the remote side, verifies that its source port is 80 and that it is not being sent to the loopback address. It also compares the destination IP address with a list of 1708 search engine IP addresses, and does no further processing if it is on the list.

One of the bugs that allowed Stack Trace to diagnose the problem is the handling of status codes. Instead of looking for the 200 HTTP success code, the rootkit looks for three strings on a blacklist that correspond to HTTP failures. That list is not exhaustive, so Stack Trace was able to see the injection in a 400 HTTP error response. Beyond that, the rootkit cleverly handles chunked Transfer-Encodings and gzip Content-Encodings, though the latter does an in-kernel decompress-inject-compress cycle that could lead to noticeable server performance problems.

None of the abilities of the rootkit are particularly novel, though it is interesting to see them laid bare like this. As should be obvious, a rootkit can do an awful lot in a Linux system, and has plenty of ways to hide its tracks. While this rootkit only hid some of its tracks, some of that may have happened after the initial development. The CrowdStrike conclusion is instructive here: "Rather, it seems that this is contract work of an intermediate programmer with no extensive kernel experience, later customized beyond repair by the buyer."

The question of how the rootkit was installed to begin with is still open. Given the overall code quality, CrowdStrike is skeptical that some "custom privilege escalation exploit" was used. That implies that some known but unpatched vulnerability (perhaps in a web application) or some kind of credential leak (e.g. the root password or an SSH key) was the culprit. Until and unless some mass exploit is used to propagate an upgraded version of the rootkit, it is really only of academic interest—except, of course, to anyone whose system is already infected.


(Log in to post comments)

SecureBoot can't come soon enough

Posted Nov 22, 2012 8:46 UTC (Thu) by paulj (subscriber, #341) [Link]

So that we can be protected from our kernel code being maliciously replaced in such ways...

SecureBoot can't come soon enough

Posted Nov 22, 2012 11:42 UTC (Thu) by cesarb (subscriber, #6266) [Link]

SecureBoot by itself would not help, since the bootloader and kernel are not being modified. It is loading a module, so disabling module loading is what would help.

When SecureBoot is used, the kernel will disable loading modules, but you do not need SecureBoot to do so yourself.

SecureBoot can't come soon enough

Posted Nov 22, 2012 15:52 UTC (Thu) by raven667 (subscriber, #5198) [Link]

> When SecureBoot is used, the kernel will disable loading modules, but you do not need SecureBoot to do so yourself.

Although without checking of the bootloader you could create a grub module which would inject code or silently enable module loading as the kernel booted so that a rootkit could persist. And even with secureboot and disabling of unsigned module loading you can still inject code into the kernel using any kernel vulnerability accessible from userspace and use that to load a module or enable module loading.

It's good that most of these rootkits are clearly made by amateurs, what would a linux rootkit look like if it had the professional resources of cyber-weapons like Stuxnet or Flame?

SecureBoot can't come soon enough

Posted Nov 22, 2012 16:40 UTC (Thu) by nix (subscriber, #2304) [Link]

It would look invisible. :)

(Perhaps you're infected already! Look behind you!)

SecureBoot can't come soon enough

Posted Nov 22, 2012 16:54 UTC (Thu) by dps (subscriber, #5725) [Link]

My preferred fix would be module signing. If the required key was either not present, because I compiled the kernel elsewhere, or securely shredded then loading the module would fail and there would be a nasty message in the kernel log.

Real experts could just replace the kernel too but script kiddies can't. I can't afford to implement write protected /, /usr, kernel image, etc. If udev or systemd can't cope then I would use something simpler which can.

My personal firewall machine (original pentium with 2 * 10/100 ethernet) uses a kernel does not support modules, period. I don't need SecureBoot to stop you loading modules on that box :-)

SecureBoot can't come soon enough

Posted Nov 22, 2012 19:32 UTC (Thu) by Seegras (subscriber, #20463) [Link]

> ... uses a kernel does not support modules, period. I don't need
> SecureBoot to stop you loading modules on that box :-)

Aye. I've got no module-loading capability on my firewalls and servers either. For years.

SecureBoot can't come soon enough

Posted Nov 23, 2012 0:02 UTC (Fri) by mjg59 (subscriber, #23239) [Link]

There's plenty of ways for root to modify your running kernel without loading modules. Restrictions on module loading are necessary for improved security, but not sufficient.

SecureBoot can't come soon enough

Posted Nov 22, 2012 21:23 UTC (Thu) by paulj (subscriber, #341) [Link]

Disabling module loading doesn't stop modules being loaded though...

A rootkit dissected

Posted Nov 22, 2012 14:04 UTC (Thu) by epa (subscriber, #39769) [Link]

It would save everyone a lot of trouble if the kernel had built-in functions for hiding files and processes and other rootkitty things. It wouldn't make anyone less secure: if you can get as far as loading a kernel module to call these functions, then the machine is already compromised. But it would save everyone some time writing and debugging these things and help to emphasize the important point: that what matters is not the existence of a rootkit, but the vulnerabilities that allow you to get root and modify the kernel in the first place.

A rootkit dissected

Posted Nov 22, 2012 15:11 UTC (Thu) by dtalen (subscriber, #86448) [Link]

Because kernel developers should spend their time making life easier for rootkit developers. Right...

Sarcasm aside, there aren't many legitimate uses for APIs that do "rootkitty things." You're right that the existence of these doesn't necessarily make the kernel less secure, but it would make a statement that kernel developers even care about doing something for malware developers, which is a bad message to send.

A rootkit dissected

Posted Nov 22, 2012 15:46 UTC (Thu) by spender (subscriber, #23067) [Link]

Linux already has it -- it's called LSM.

-Brad

A rootkit dissected

Posted Nov 22, 2012 22:57 UTC (Thu) by BenHutchings (subscriber, #37955) [Link]

That doesn't help, as LSMs can't be loadable modules.

A rootkit dissected

Posted Nov 22, 2012 23:33 UTC (Thu) by PaXTeam (subscriber, #24616) [Link]

is the stable 3.2 series maintainer seriously saying that general kernel modules can't (ab)use the LSM interfaces? for real? ;)

A rootkit dissected

Posted Nov 23, 2012 1:18 UTC (Fri) by BenHutchings (subscriber, #37955) [Link]

Kernel modules can use, abuse or bypass any interface, exported or not. But run-time installable LSMs would be so much more convenient to the rookit author.

A rootkit dissected

Posted Nov 23, 2012 1:25 UTC (Fri) by PaXTeam (subscriber, #24616) [Link]

and what exactly prevents a normal module from posing as an LSM? nothing? ;)

A rootkit dissected

Posted Nov 24, 2012 0:12 UTC (Sat) by dpquigl (subscriber, #52852) [Link]

You're right absolutely nothing and with this proposed patch by the TOMOYO developer[1] It will become even easier.

[1]http://www.spinics.net/linux/fedora/linux-security-module...

Copyright © 2012, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds