|
|
Subscribe / Log in / New account

Making attacks a little harder

By Jonathan Corbet
November 17, 2010
Regardless of whether one believes that the security of the Linux kernel is as good as it should be, it is hard to disagree with the idea that it could be made more secure. For some years, it has seemed like much of the security-related work on the kernel has been directed toward the creation of new access control mechanisms. But access control is only so helpful if the kernel itself is vulnerable, allowing any access control system to be bypassed. Recently we have begun to see more work aimed at making small improvements to the security of the kernel itself; this article will survey some of that work.

One key to hardening a system against attackers is to make it harder for them to obtain information which could be used to compromise the kernel. So it is not surprising to see an increase in patches which lock down access to information. It turns out, though, that there is not universal agreement on the value of restricting any kind of information about the running system.

Marcus Meissner started things off with a simple patch removing world-read access from /proc/kallsyms. It is difficult to subvert the kernel without knowledge of how the kernel's memory is laid out, so, Marcus thought, there is no point in providing that information to anybody who asks. The problem with this change, as Ingo Molnar pointed out, is that there are many sources of that information. For example, the System.map file shipped by most distributors also has the locations of all symbols built into the kernel.

Now, one can certainly read-protect System.map as well, but that may not be particularly helpful. Most systems out there are running distributor-supplied kernels, and the packages for those kernels are widely available. So an attacker does not need to read /proc/kallsyms or System.map if the target system is running a stock kernel; they need only dig up a package file containing the needed information. For this reason, Ingo suggested that a complete solution would require restricting access to the running kernel version as well. Removing all of the globally-readable kernel version information from a system would be hard, but, if it could be done, attackers would no longer have easy access to the locations of functions and data structures within the kernel.

Suffice to say that this idea was not received with universal acclaim. Critics claim that there are plenty of ways to determine which kernel version is running; hiding version information would just make life harder for legitimate applications (which may need that information to know which features are available) without appreciably slowing attackers. Ingo talked some about instrumenting the kernel to detect an attacker's attempts to determine the running kernel version, thus giving an early alarm, but this idea did not seem to gain a great deal of traction. So, chances are, kernel versions will not be hidden in any near-future release (the /proc/kallsyms patch has been merged for 2.6.37, though).

Dan Rosenberg has a similar concern: when the kernel exposes pointer values to user space, it gives information to potential attackers. These values can be found in a number of places, including the system log and numerous places in /proc. Keeping pointer values out of the system log seems like a hopeless task, but it is possible to better restrict access to that log. To that end, Dan has posted a patch adding a new sysctl knob controlling access to the syslog() system call. Later versions of the patch include a configuration option for the default setting of this knob; with that, distributors can make the system log off limits for unprivileged users starting at boot.

Kernel addresses also show up in other places, though; for example, /proc/net/tcp contains the address of the sock structure associated with each open TCP connection. Dan worries about exposing the address of these structures, especially since many of them contain function pointers; if an attacker is somehow able to change the contents of kernel memory, this kind of address might facilitate the task of taking over the system. To raise the bar a bit, Dan posted a series of patches which replaces the pointer value with an integer value (often zero) if the process reading the associated /proc file is not suitably privileged.

Unlike the syslog patch, which has made it into the mainline, the /proc modification ran into some stiff opposition. It was described as "security theater," and developers worried that it would break applications which are legitimately using the pointer values. There were suggestions that, perhaps, pointer values could be hashed, or that a more general solution could be had by modifying the behavior of "%p" in format strings. We might see the "%p" patch at some point, but Dan has given up on the /proc patches for now, saying "It's clear that there's too much resistance to this effort for it to ever succeed, so I'm ceasing attempts to get this patch series through."

Making it difficult to find structures containing function pointers may make life harder for an attacker, but it still seems better to block the modification of those structures whenever possible, regardless of who knows their location. To that end, Kees Cook has announced his intent to try to lock down more of the kernel:

The proposal is simple: as much of the kernel should be read-only as possible, most especially function pointers and other execution control points, which are the easiest target to exploit when an arbitrary kernel memory write becomes available to an attacker.

Getting various structures marked const is an obvious starting point; "constification" patches have been produced by many developers over the years, but many structures still can be modified at run time. Beyond that, though, Kees would like to have working read-only and no-execute memory in loadable modules, "set once" pointers for things like the security module operations vector, and more; many of the changes he would like to see merged can currently be found in the grsecurity tree. It could be a long process, but Kees says that it would be a security win for everybody and that he would appreciate cooperation from subsystem maintainers.

Not all kernel vulnerabilities are in the core code; many, instead, are found in loadable modules. An attacker wishing to exploit a vulnerability in a module must first ensure that the module is loaded. Module loading is a privileged operation, but there are a number of ways in which an unprivileged user can cause the kernel to load a module anyway; the kernel normally goes out of its way to autoload modules on demand so that things "just work." It seems clear that a kernel which never allows users to trigger the loading of modules is less likely to be affected by any vulnerability which is found in a loadable module.

Dan has posted another patch (again, based on work done in the grsecurity tree) which makes the demand loading of modules harder. It replaces the existing modules_disable sysctl knob with a more flexible version; if it is set to one, only root can trigger the loading of modules. Setting it to two disables module loading entirely until the next boot. The changing of the existing ABI was not well received, so a future version of the patch will keep the existing switch and its semantics. Beyond that, doubts have been expressed regarding whether administrators will enable this option, since demand loading is a convenient feature.

Hardening the kernel to make the exploiting of vulnerabilities more difficult seems like a good thing, but it would also be nice if we could find those vulnerabilities before anybody even tries to exploit them. One technique which can help in this regard is "fuzzing," the process of passing random values into system calls and looking for unexpected behavior. Some attackers certainly have good fuzzing tools, but the development community seems to be rather less well equipped. So it is good to see some recent work by Dave Jones aimed at the creation of a more intelligent fuzzer. It turns out that, by making system call parameters a bit less fuzzy, the tool is more likely to get past the trivial checks and turn up real problems; the improved fuzzer has already turned up one real bug.

The value of all this work may not be clear to everybody, and it probably will not all make it into the mainline kernel. But it does seem that we are seeing the beginning of a more focused effort to improve the security of the kernel and to make it harder to exploit the inevitable bugs. A more secure kernel may make it harder to gain true ownership of our gadgets in the future, but it still is generally a good thing.


to post comments

Pointer values

Posted Nov 18, 2010 13:12 UTC (Thu) by epa (subscriber, #39769) [Link] (4 responses)

Generate a 64-bit random value at boot time, then XOR each pointer value with it and hash the result with SHA or similar. These pointers can be recorded in the log file safely; so an unprivileged user can see when two pointers are equal but no other information. The root user can ask the kernel for the secret random value and so decode the log file.

Pointer values

Posted Nov 18, 2010 15:50 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link] (2 responses)

And code to decrypt this XOR number will be published in 20 seconds after kernel release.

Pointer values

Posted Nov 18, 2010 18:40 UTC (Thu) by andfarm (guest, #61973) [Link] (1 responses)

Make it a truncated SHA1 hash, then? Same idea.

Pointer values

Posted Nov 18, 2010 19:47 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link]

Make an IOCTL and stop inventing lame interfaces?

Pointer values

Posted Nov 22, 2010 10:35 UTC (Mon) by epa (subscriber, #39769) [Link]

On second thoughts I realize that even if you knew the secret value you wouldn't be able to reverse the hash function to get the original pointers back. So if you wanted to hide the pointer values you'd need to encrypt them, with a much longer secret key than a mere 64 bits.

0400 /proc/kallsyms not in 2.6.37

Posted Nov 20, 2010 0:56 UTC (Sat) by spender (guest, #23067) [Link] (1 responses)

FYI, the patch has been reverted:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-...

-Brad

0400 /proc/kallsyms not in 2.6.37

Posted Dec 8, 2010 12:27 UTC (Wed) by kevinm (guest, #69913) [Link]

Anyone who wants the effect can simply add:

chmod 400 /proc/kallsyms

to their /etc/rc.local or similar.


Copyright © 2010, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds