By Jonathan Corbet
August 18, 2009
On July 16, Brad Spengler
disclosed an easily-exploitable
kernel vulnerability based on getting the kernel to dereference a null
pointer. This security hole affected a version of the kernel which had not
been widely distributed, so it was a problem for relatively few users, but
it highlighted a class of problems which was sure to be seen again. Given
that, and given our community's perception of itself as being responsive to
security problems, it should be safe to assume that steps were taken to
prevent null pointer vulnerabilities from opening up systems in the
future. The reality is that some things have truly improved, but that some
important vulnerabilities remain.
C programmers normally expect that an attempt to dereference a null (zero)
pointer
will result in a hardware exception which, in turn, causes the program to
crash. This happens not because there is anything special about a pointer
containing zero, but because the trick of not mapping valid memory at the
bottom of the virtual address space has been known and used for decades.
If no valid memory is mapped near address zero, the hardware will trap
attempts to access memory in that range; that includes attempts to
dereference null pointers. It is a useful setup which minimizes the damage
caused by misuse of null pointers.
The only problem is that, in the kernel environment, there
is no guarantee that no valid memory is mapped at the bottom of the address
space. The default is to not map anything there, but applications can
request, via the mmap() system call, that the lowest addresses be
made valid. So the null pointer address can be made to point to real
memory, and this can
happen entirely under the control of user space. User-space addresses
remain valid when running in the kernel, so, if the kernel can be made
to dereference a null pointer, it will be accessing user-controlled
memory. Should the kernel try to jump to a null pointer, it will be
running user-controlled code directly. Needless to say, this sequence of
events would not be good for the security of the system.
After the July disclosure, it was reasonably evident that more null-pointer
vulnerabilities had to exist in the kernel. Such bugs are easy to create;
all that is required is a missing initialization. A new function pointer
added to a structure will be silently set to null by the compiler in every
declaration which does not include an explicit initialization for that
pointer. Kernel programmers may be diligent about checking for null
pointers, but they are human and will miss an occasional check. At times,
these checks have been actively discouraged on the reasoning that
dereferencing the pointer would, by virtue of oopsing the kernel, provide
the same information as the check. For all of these reasons, one must
conclude that there will be other situations in which the kernel can be
tricked into dereferencing null pointers.
Given that, it would behoove us all to build our systems in ways which are
resistant to null-pointer attacks. The /proc/sys/vm/mmap_min_addr
parameter was meant to be the first line of defense here; it specifies the
lowest address which can be mapped by unprivileged user-space code.
Unfortunately, this protection proved porous. Systems with SELinux
running, as it turns out, allowed "unconfined" users to map low memory
regardless of the mmap_min_addr setting. For many other systems,
it was possible to exploit a problem with pulseaudio to run code with the
SVR4 personality, which resulted in a mapped zero page. All told, these
problems enabled an attacker to bypass the low-memory limits and exploit
null-pointer vulnerabilities.
On August 13, another null
pointer vulnerability turned up; this one resulted from the combination
of a missing function pointer initialization and a failure to check the
pointer before jumping to it. It was an easily exploited hole;
demonstration code was duly posted and there have been reports that attack code is already attempting
to use this vulnerability.
The kernel itself was patched quickly, even if the commit
which closed this vulnerability was less than forthcoming about the
problem:
Make sock_sendpage() use kernel_sendpage(). kernel_sendpage() does
the proper default case handling for when the socket doesn't have a
native sendpage implementation.
Linus did mention the problem in the 2.6.31-rc6 announcement, though:
There's the NULL pointer fix that was already talked up on
Slashdot, but quite frankly, assuming we got all the "you can't map
things at zero" issues fixed from the last scare, that one
hopefully wasn't quite as bad as it could have been.
So, do "we" really have all of those issues fixed? We do not, though some
important progress has been made in that direction. Take Fedora as an
example: the SELinux policy
problem which unconditionally allowed "unconfined" users to map low memory
has been fixed; as a result, Fedora systems with SELinux running in
the enforcing mode are not vulnerable. But the underlying means by which
security modules bypass the mmap_min_addr check has not been
fixed. So unpatched Fedora
systems with SELinux in permissive mode are vulnerable, even though
systems with SELinux disabled entirely are not.
Updates for Fedora were released on August 15, two days
after the disclosure of the vulnerability. Two days may seem slow for a
problem of this nature, but, as
it happens, only one distributor - Debian - got an update out more
quickly.
Red Hat has not, as of this writing, issued an update for this
vulnerability. That is unfortunate because most RHEL systems are
vulnerable as the result of a policy choice made by Red Hat. RHEL systems, by
default, allow "unconfined" users to map low addresses addresses.
Red Hat's Dan Walsh explains: "We
are not planning on changing the default in RHEL5, to maintain backwards
compatibility." So, because compatibility trumps security, RHEL
systems (and those running distributions based on RHEL) remain vulnerable
to a trivial local root problem with exploit code easily available and in use. Not
good.
As of this writing, no other distributors have fixed this problem (though
Mandriva's update showed up just before publication). Given
that this vulnerability affects every kernel released since 2001, every
distribution will have shipped vulnerable kernels. Even those which do not
enable SELinux and which have taken steps to mitigate the other zero-page
mapping problems should really be moving quickly to close this hole.
Leaving the barn door open may not be a wise course of action, even if one
trusts the fence which has been built around the barn.
One also should not forget all of those older systems, including embedded
systems like DSL routers, which will be exposed to this vulnerability.
This hole could be a boon to people trying to liberate the devices they
own, but it could also be an easy way to take control of important systems
which have long since been forgotten about. 2.4 kernels, too, are affected
by this problem; it is easy to imagine that the bulk of these older
systems will never be fixed.
One month ago we got an undeniable warning that this kind of vulnerability
was coming. The security of many of our systems has undoubtedly improved
over the course of that month. Even so, the latest null pointer
vulnerability would appear to have taken some distributors by surprise;
important holes have not been closed and updates have, in some cases, been
slow in coming. We can - and should - do better than this.
(
Log in to post comments)