LWN.net Logo

Security

Fun with NULL pointers, part 1

By Jonathan Corbet
July 20, 2009
By now, most readers will be familiar with the local kernel exploit recently posted by Brad Spengler. This vulnerability, which affects the 2.6.30 kernel (and a test version of the RHEL5 "2.6.18" kernel), is interesting in a number of ways. This article will look in detail at how the exploit works and the surprising chain of failures which made it possible.

The TUN/TAP driver provides a virtual network device which performs packet tunneling; it's useful in a number of situations, including virtualization, virtual private networks, and more. In normal usage of the TUN driver, a program will open /dev/net/tun, then make an ioctl() call to set up the network endpoints. Herbert Xu recently noticed a problem where a lack of packet accounting could let a hostile application pin down large amounts of kernel memory and generally degrade system performance. His solution was a patch which adds a "pseudo-socket" to the device which can be used by the kernel's accounting mechanisms. Problem solved, but, as it turns out, at the cost of adding a more severe problem.

The TUN device supports the poll() system call. The beginning of the function implementing this functionality (in 2.6.30) looks like this:

    static unsigned int tun_chr_poll(struct file *file, poll_table * wait)
    {
	struct tun_file *tfile = file->private_data;
	struct tun_struct *tun = __tun_get(tfile);
	struct sock *sk = tun->sk;
	unsigned int mask = 0;

	if (!tun)
	    return POLLERR;

The line of code which has been underlined above was added by Herbert's patch; that is where things begin to go wrong. Well-written kernel code takes care to avoid dereferencing pointers which might be NULL; in fact, this code checks the tun pointer for just that condition. And that's a good thing; it turns out that, if the configuring ioctl() call has been made, tun will indeed be NULL. If all goes according to plan, tun_chr_poll() will return an error status in this case.

But Herbert's patch added a line which dereferences the pointer prior to the check. That, of course, is a bug. In the normal course of operations, the implications of this bug would be somewhat limited: it should cause a kernel oops if tun is NULL. That oops will kill the process which made the bad system call in the first place and put a scary traceback into the system log, but not much more than that should happen. It should be, at worst, a denial of service problem.

There is one little problem with that reasoning, though: NULL (zero) can actually be a valid pointer address. By default, the very bottom of the virtual address space (the "zero page," along with a few pages above it) is set to disallow all access as a way of catching null-pointer bugs (like the one described above) in both user and kernel space. But it is possible, using the mmap() system call, to put real memory at the bottom of the virtual address space. There are some valid use cases for this functionality, including running legacy binaries. Even so, most contemporary systems disable page-zero mappings through the use of the mmap_min_addr sysctl knob.

Security module checks are supposed to be additive to the checks which are already made by the kernel, but it didn't work that way this time. This knob should prevent a user-space program from mapping the zero page, and, thus, should ensure that null pointer dereferences cause a kernel oops. But, for unknown reasons, the mmap() code in the 2.6.30 kernel explicitly declines to enforce mmap_min_addr if the security module mechanism has been configured into the kernel. That job, instead, is left to the specific security module being used. Security module checks are supposed to be additive to the checks which are already made by the kernel, but it didn't work that way this time; with regard to page zero, security modules can grant access which would otherwise be denied. To complete the failure, Red Hat's default SELinux policy allows mapping the zero page. So, in this case, running SELinux actually decreased the security of the system.

Not that life is a whole lot better without SELinux. In the absence of SELinux, the exploit will run up against the mmap_min_addr limit, which would seem like enough to bring things to a halt. That particular difficulty can be circumvented, though, through the use of the personality() system call. Enabling the SVR4 personality causes a read-only page to be mapped at address zero when a program is invoked with exec(), but only if the process in question has the CAP_SYS_RAWIO capability. So one more trick is required: the top-level exploit code will set the SVR4 personality, then use exec() to run the pulseaudio server with a special plugin module. Pulseaudio is installed setuid root, so it will get the zero page mapped at invocation time. By the time the plugin code is called, pulseaudio has dropped its privileges, but, by then, the zero page will be available to the exploit code, which can make the page writeable and place its own data there.

As a result of all this, it is possible for a user-space process to map the zero page and prevent tun_chr_poll() from causing a kernel oops. But, one would think, that would not get an attacker very far, since that function checks tun against NULL as the very next thing it does. This is where the next interesting step in the chain of failures happens: the GCC compiler will, by default, optimize the NULL test out. The reasoning is that, since the pointer has already been dereferenced (and has not been changed), it cannot be NULL. So there is no point in checking it. Once again, this logic makes sense most of the time, but not in situations where NULL might actually be a valid pointer.

So, an attacker is able to get into the body of tun_chr_poll() with a NULL tun pointer. One then needs to figure out how to get control of the kernel using this situation. The next step takes advantage of this code from a little further into tun_chr_poll():

	if (sock_writeable(sk) ||
	    (!test_and_set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags) &&
	     sock_writeable(sk)))
		mask |= POLLOUT | POLLWRNORM;

The value of sk, remember, came from the dereferencing of tun, so it's under the attacker's control. SOCK_ASYNC_NOSPACE is zero, so the test_and_set_bit() call can be used to unconditionally set the least significant bit of any word in memory. As kernel memory corruptions go, this is a small one, but it turns out to be enough. In Brad's exploit, sk->sk_socket->flags points into the TUN driver's file_operations structure; in particular, it points to the mmap() function. The TUN driver does not support mmap(), so that pointer is normally NULL; after the poll() call, that pointer is now one instead.

The final step in the exploit is to call mmap() on a file descriptor for the open TUN device. Since the internal mmap() operation is no longer NULL (it has been set to one), the kernel will jump to it. That address also lives within the zero page mapped by the exploit, so it is under the attacker's control. The exploit will have populated that address with another jump to its own code. So, when the kernel calls (what it thinks is) the TUN driver's mmap() function, the result is arbitrary code being run in kernel mode; at that point the exploit has total control.

In well-designed systems, catastrophic failures are rarely the result of a single failure. That is certainly the case here. Several things went wrong to make this exploit possible: security modules were able to grant access to low memory mappings contrary to system policy, the SELinux policy allowed those mappings, pulseaudio can be exploited to make a specific privileged operation available to exploit code, a NULL pointer was dereferenced before being checked, the check was optimized out by the compiler, and the code used the NULL pointer in a way which allowed the attacker to take over the system. It is a long chain of failures, each of which was necessary to make this exploit possible.

This particular vulnerability has been closed, but there will almost certainly be others like it. See the second article in this series for a look at how the kernel developers are responding to this exploit.

Comments (80 posted)

Brief items

JITter Bug (Linux Journal)

Linux Journal looks into a security problem with Mozilla's just-in-time compiler. "Two weeks ago, Mozilla was celebrating the triumphant release of the much-delayed Firefox 3.5. The browser brings its users a pantheon of new features, with perhaps the most celebrated being the TraceMonkey JavaScript engine, said to provide speed enhancements twice as fast as Firefox 3.0 and up to ten times that of Firefox 2.0. One element of the acclaimed performance booster is giving its developers something of a headache this week, however. The first zero-day exploit for Firefox 3.5 was revealed publicly on Monday, in the form of a vulnerability in the browser's Just-in-time compiler."

Comments (10 posted)

New vulnerabilities

compat-wxGTK26: arbitrary code execution

Package(s):compat-wxGTK26 CVE #(s):CVE-2009-2369
Created:July 20, 2009 Updated:September 3, 2010
Description: An integer overflow in the wxImage::Create() function (through version 2.8.10) allows for a denial of service attack and possible arbitrary code execution.
Alerts:
Gentoo 201009-01 2010-09-02
Debian DSA-1890-1 2009-09-19
Mandriva MDVSA-2009:204 2009-08-16
Fedora FEDORA-2009-7780 2009-07-19
Fedora FEDORA-2009-7794 2009-07-19
Fedora FEDORA-2009-7780 2009-07-19
Fedora FEDORA-2009-7755 2009-07-19
Fedora FEDORA-2009-7763 2009-07-19

Comments (none posted)

fckeditor: missing input sanitizing

Package(s):fckeditor CVE #(s):CVE-2009-2265
Created:July 17, 2009 Updated:July 22, 2009
Description: From the Debian advisory: Vinny Guido discovered that multiple input sanitizing vulnerabilities in Fckeditor, a rich text web editor component, may lead to the execution of arbitrary code.
Alerts:
Fedora FEDORA-2009-7794 2009-07-19
Fedora FEDORA-2009-7761 2009-07-19
Debian DSA-1836-1 2009-07-16

Comments (none posted)

firefox and related: multiple vulnerabilities

Package(s):firefox CVE #(s):CVE-2009-2462 CVE-2009-2463 CVE-2009-2464 CVE-2009-2465 CVE-2009-2466 CVE-2009-2467 CVE-2009-2469 CVE-2009-2471 CVE-2009-2472
Created:July 22, 2009 Updated:June 14, 2010
Description: The firefox 3.0.12 release fixes several significant security issues. Related packages (seamonkey, xulrunner, ...) are being released with fixes for the same problems.
Alerts:
Mandriva MDVSA-2010:071 2010-04-23
Fedora FEDORA-2010-7100 2010-04-21
SuSE SUSE-SR:2010:013 2010-06-14
Debian DSA-2025-1 2010-03-31
CentOS CESA-2010:0153 2010-03-26
Ubuntu USN-915-1 2010-03-18
CentOS CESA-2010:0154 2010-03-17
Red Hat RHSA-2010:0153-02 2010-03-17
Red Hat RHSA-2010:0154-02 2010-03-17
SuSE SUSE-SA:2009:042 2009-08-06
Mandriva MDVSA-2009:185 2009-07-30
Mandriva MDVSA-2009:182 2009-07-30
Slackware SSA:2009-209-01 2009-07-28
Fedora FEDORA-2009-8033 2009-07-27
CentOS CESA-2009:1162 2009-07-28
SuSE SUSE-SA:2009:039 2009-07-27
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Fedora FEDORA-2009-7961 2009-07-23
Debian DSA-1840-1 2009-07-23
CentOS CESA-2009:1163 2009-07-23
Ubuntu USN-798-1 2009-07-22
Red Hat RHSA-2009:1163-01 2009-07-21
Red Hat RHSA-2009:1162-01 2009-07-21
Debian DSA-1931-1 2009-11-08

Comments (none posted)

mediawiki: cross-site scripting

Package(s):mediawiki CVE #(s):
Created:July 20, 2009 Updated:July 22, 2009
Description: The mediawiki 1.15.1 and 1.14.1 releases contain fixes for a cross-site scripting vulnerability introduced in 1.15.0 and 1.14.0.
Alerts:
Fedora FEDORA-2009-7750 2009-07-19
Fedora FEDORA-2009-7785 2009-07-19

Comments (none posted)

nagios: mysterious vulnerability

Package(s):nagios CVE #(s):CVE-2008-6373
Created:July 20, 2009 Updated:July 22, 2009
Description: From the Gentoo advisory: An unspecified vulnerability in Nagios related to CGI programs, "adaptive external commands," and "writing newlines and submitting service comments" has been reported.
Alerts:
Gentoo 200907-15 2009-07-19

Comments (none posted)

perl-IO-Socket-SSL: site spoofing

Package(s):perl-IO-Socket-SSL CVE #(s):
Created:July 20, 2009 Updated:July 22, 2009
Description: The perl-IO-Socket-SSL library only checks the prefix of hostnames when performing certificate matching, making site-spoofing attacks possible.
Alerts:
Fedora FEDORA-2009-7435 2009-07-11
Fedora FEDORA-2009-7544 2009-07-11

Comments (none posted)

pulseaudio: privilege escalation

Package(s):pulseaudio CVE #(s):CVE-2009-1894
Created:July 16, 2009 Updated:July 28, 2009
Description: PulseAudio has a local privilege escalation vulnerability. From the Gentoo alert:

Tavis Ormandy and Julien Tinnes of the Google Security Team discovered that the pulseaudio binary is installed setuid root, and does not drop privileges before re-executing itself. The vulnerability has independently been reported to oCERT by Yorick Koster. A local user who has write access to any directory on the file system containing /usr/bin can exploit this vulnerability using a race condition to execute arbitrary code with root privileges.

Alerts:
Mandriva MDVSA-2009:171 2009-07-28
Debian DSA-1838-1 2009-07-18
Mandriva MDVSA-2009:152 2009-07-17
Ubuntu USN-804-1 2009-07-16
Gentoo 200907-13 2009-07-16

Comments (none posted)

ruby: certificate spoofing

Package(s):ruby CVE #(s):CVE-2009-0642
Created:July 20, 2009 Updated:December 8, 2009
Description: The ruby library does not properly validate X.509 certificates, enabling an attacker to use expired or invalid certificates.
Alerts:
Mandriva MDVSA-2009:325 2009-12-07
Debian DSA-1860-1 2009-08-12
Mandriva MDVSA-2009:193 2009-08-05
Ubuntu USN-805-1 2009-07-20

Comments (none posted)

wordpress: file inclusion and information disclosure

Package(s):wordpress CVE #(s):CVE-2009-2334 CVE-2009-2335 CVE-2009-2336
Created:July 20, 2009 Updated:January 28, 2010
Description: The wordpress system suffers from vulnerabilities which can allow an attacker to include (and execute) arbitrary local files and to enumerate valid user names. The 2.8.1 release contains the fixes.
Alerts:
Fedora FEDORA-2009-12547 2009-12-03
Debian DSA-1871-2 2009-08-27
Debian DSA-1871-1 2009-08-23
Fedora FEDORA-2009-8529 2009-08-15
Fedora FEDORA-2009-8538 2009-08-15
Fedora FEDORA-2009-7701 2009-07-19
Fedora FEDORA-2009-7729 2009-07-19

Comments (none posted)

Page editor: Jake Edge
Next page: Kernel development>>

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