How To Exploit NULL Pointers
An anonymous reader writes "Ever wondered what was so bad about NULL pointer exceptions? An MIT Linux kernel programmer explains how to turn any NULL pointer into a root exploit on Linux. (There was also a previous installment about virtual memory and how to make NULL pointers benign.)"
TFA is an extremely well-written, easy-to-follow tutorial. I "played along at home" (well, at work, actually) as the author recommended and exploited a system on the first try. Great stuff!
Hang on, one of our SysAdmins wants to talk to me about something.
BRB
What a bad attempt at trolling. You could make various funny posts involving Windows and security, and this is what you came up with? Up your game, mister.
"Ever wondered what was so bad about NULL pointer exceptions?..."
Nothing. Because if they're an exception, they've been safely caught by the platform's exception handling mechanism. This article isn't about exceptions, it's about dereferencing your actual raw NUL pointers themselves in languages that either don't have the exception mechanism or where it simply hasn't been used.
Cheers,
Ian
This is very OS dependent.
For example, on AIX on POWER, page 0 in both real and virtual addressing modes is readable by all and writable by none. So a read from a NULL pointer produces junk data (actually interrupt machine code) and a write is fatal.
Terrorist, bomb, al Qaeda, nuclear, yellowcake, kill, assassinate. Carnivore is dead... long live Echelon.
I was intrigued by the ./ posting, which claimed that the tutorial would show how to exploit any NULL pointer dereference. The actual article, however, requires a CALL to the NULL pointer. While some NULL pointer bugs are function pointers, many are not. Kernel code that merely reads or writes data to a NULL pointer will not be exploitable as shown.
To be honest, I'm not sure why I bothered writing this comment. If the editors themselves don't care about the accuracy of the stories, why should I?
For further context, see my whitepaper on how to turn any kdawson-posted Slashdot story into a NULL issue.
Well, I upped my game. Now up yours.
One of the many exploits that we've used to own the Wii (in fact, the very first runtime IOS exploit that we used, which I found and implemented) was a NULL pointer dereference bug, and it wasn't even a function pointer.
I wrote a detailed blog post about it recently. The short version is that they doubly dereference a near-NULL address and write to it, and NULL happens to be real physical memory that we control (call it 'insecure', if you wil). The double dereference lets us direct the write anywhere, including the stack, and it's game over. That's the "usermode" exploit. Privilege escalation into the kernel is trivial because they have some huge kernel holes. The fact that they map the 'insecure' memory as executable (!) in every application makes it even easier.
I thought the BigMem kernel patches a few years back put the kernel in it's own VM, with minimal copying into userspace VM space, or am i missing something?
I don't know what that patch did (BigMem implies something like using 2MB pages, but what's in a name?), but I do know that the author is right about address space switches being expensive and not something you'd want to do on every system call, or any system call that is expected to return control to the same process for that matter.
In practice I don't ever see CR3 writes ( CR3 points to the root of the page table, so writing it is how you switch address spaces) in system calls. Though I am not sure exactly what kernel rev or patch level the benchmark traces are taken from. Still, sounds like it's probably right to me.
The enemies of Democracy are
Well, if you read the article, you'll find out that you have to
* circumvent the protection against mmap to address 0 (in the article, that one was just done as root)
* get the kernel to call a function through a function NULL pointer (that's what was done through the special kernel module)
Since the exploit doesn't make much sense if you already are root, for this exploit you have to
* find an existing bug in the kernel which allows you to circumvent the mmap protection.
* find another existing bug in the kernel which causes the kernel to do a function call through a NULL function pointer.
So you need two independent bugs in the kernel to make an actual exploit from this demonstration code.
Having said that, I think it would certainly be a nice option to be able to trade performance for security by telling the system to put the kernel into its own memory space.
The Tao of math: The numbers you can count are not the real numbers.
If you have a bug in kernel code that causes NULL pointer dereference, it can be used for various nastiness (in this case, privilege escalation).
This is why kernel shouldn't do it, and this is why it was an actual kernel bug that was exploited by so-called NULL pointer exploits. This is why those bugs were fixed.
Apparently some readers have an impression that what was posted is an actual exploit that works on a current kernel by dereferencing NULL pointer in userspace. In reality it relies on a buggy module being introduced, so kernel NULL dereference can be triggered by the user.
Contrary to the popular belief, there indeed is no God.
Yeah, shouldn't switch be easily take care of by a base register?
Well it is. On x86 systems, the intuitively named Control Register 3 is a pointer to the base of the page tables. From a software point of view, switching address spaces is as easy as writing CR3.
From a hardware point of view, that act has additional implications. You have to flush the TLBs, which sucks royal if it happens on every system call. If you have linearly tagged caches (or any other linearly tagged structure) then you'll have to flush those too. There are ways to partially mitigate these effects, but since you can't rely on them being there it's best to just avoid CR3 writes as much as possible -- which means there's less reason to implement the necessary widgets.
The enemies of Democracy are
Considering that null function pointer bugs are a dime a dozen on any system, finding one of those is easy. TFA also points out that the mmap protection code in Linux has been historically weak, although there don't seem to be any open bugs at the moment.
So the article could have been better titled as "Why null function pointer bugs are serious business", but "How to exploit null [function] pointers" is still pretty accurate.
Sorry, but if anything that simple can cause root access, then that’s a general error of the architecture and kernel.
By default you need root access (or an exploitable bug) to map page zero into your address space, and you need to specifically configure the kernel to allow it, and then you need an exploitable kernel bug to make use of it.
I wouldn't exactly call that 'simple'.
Why should a change of the page table be needed?
It's not needed if you map your kernel into the application's page tables. ;)
All you need are separate segments for kernel and user mode.
1) Segmentation is essentially non-existent* in 64-bit mode.
2) Segmentation sucks. Always has, always will. That's why even in 32-bit mode most segments are made with base 0 and max limit, and processors are optimized for this case.
3) Okay, so you switch your CS and DS segments when you go into kernel mode (well actually you do anyway, but they're non-base-zero in this case). That's great, but you still need to map your linear address (linear = virtual address + segment base) to a physical address. So you either need to write to CR3 to use the kernel's page table, or you need to map your kernel's memory into the user's page table.
* Ask VmWare about the non-essentially existent remnants of segmentation.
The enemies of Democracy are
This is not "how to exploit NULL pointers" ... this is "how to exploit a kernel NULL function pointer".
Well, duh.
In other news, security researches find exploit for systems with blank root password.
68k is alive and well in the embedded market with ColdFire and DragonBall processors.
I strongly disagree.
With the entire industry. It's okay. You're not the only one to have maintained the belief that segments are not useless crap. ;)
Segmented addressing got a bad name from the days of real mode
That wasn't "segmentation" in the academic sense, and it's the academic sense of segmentation, what is actually implemented in 32-bit protected mode, that has the well-deserved bad reputation among the engineers implementing and coding for it. Since the default in 32-bit mode was to effectively eliminate segmentation, it only made sense to just get rid of it.
That's not really hard to handle, especially since the code/data part is automatically handled by the processor.
In the sense that you don't have to specify that your code accesses use the code segment and data accesses use the data segment by default. However you still have to explicitly change code and data descriptors when changing between OS and user land and those operations are also not performance-neutral. They are rather slow in fact. Not as bad as a CR3 switch, but bad enough you don't want to do two (per segment, so four) just so the kernel can return the value in it's time_t structure.
And that's before adding on the performance penalty of having to do an extra addition for every access when not using zero-base segments. You realize how many man-years of performance widgets you've undone by doing that? :)
Kernel code would have to explicitly distinguish between user mode access and kernel mode access (which IMHO is good).
In theory, but the exact case in question (mmap) is one where you want the both kernel and application to have the fastest access to the mmap()ed region possible. Which, in case you were wondering, means you can't just map two linear addresses to the same physical page, one for the user and one for the kernel, because that results in TLB thrashing. And when you take that and then add all the other cases where the kernel needs to regularly access something that might be mapped in user land, and suddenly you've just recreated the need for far pointers, and passing around far pointers results in the same possibility for badly formed pointers as before. Remember, if making sure all your pointers were valid was easy we wouldn't have this problem in the first place.
There's a reason why even when IA32 provided these segmentation facilities that nobody used them. And it's not prejudice from the 16-bit days, as if that could explain why nobody else has implemented segmentation.
Good riddance to bad rubbish I say. Modern ISAs ftw, even if they're still CISCy. :)
The enemies of Democracy are
FYI: AMD-V or Intel Nehalem or later
Nehalem processor TLB address mapping entries include a Virtual Processor Identifier (VPID), while AMD-V supports tagged TLBs.
-- Terry
I was intrigued by the ./ posting, which claimed that the tutorial would show how to exploit any NULL pointer dereference. The actual article, however, requires a CALL to the NULL pointer. While some NULL pointer bugs are function pointers, many are not. Kernel code that merely reads or writes data to a NULL pointer will not be exploitable as shown.
But sometimes, they can still be exploited. Let's hypothesize a UNIX clone whose kernel has this code in its implementation of the chroot() system call, something that only root should be able to call:
if (get_current_process()->m_uid != 0)
{
return EPERM;
}
Now let's suppose that there is a bug in the kernel that you can exploit to cause get_current_process() to return a null process pointer. Using mmap(), you can allocate the zero page. The get_current_process()->m_uid expression now reads memory that you control. Of course, you're going to put 0 at that location.
With chroot() available to a non-root program, it will only be a matter of a few tricks with setuid programs before you get root access. Once you have root access, you can elevate to full kernel mode by loading a kernel extension.
"Screw Sun, cross-platform will never work. Let's move on and steal the Java language." - Visual J++ Product Manager
changing address spaces, which is a costly operation in any architecture.
Not necessarily. What if Intel had real segments, pointing each to a separate address space rather than just being windows into a same global address space.
In a perfect world, each segment would have its own CR3 (page table root), and it would not only be more secure, but also more performant (no flushing of kernel's TLB cache when switching from one process to the other), and would have allowed better "big memory" support under 32 bit systems.
...as far as I can tell is simply to ban null pointers in the kernel.
I've done alot of development in my time and I've never needed to point at null.
I'll grant you that when the term is used alone. "Did you get NULL again?" Still, when paired with pointer, the capitalization seems redundant, sort of like writing PIN number, cold temperature, and the like.