Well, that's mind-bending. I couldn't figure out how/where the actual exploitation of su happened since it seemed to require some careful synchronization, until it hit me: His exploit essentially starts up a process which, when it writes to stderr, is writing to its own memory. Then, it lseeks to the correct position which would cause su, when writing its error message, to overwrite its own exit() with shellcode. And then it just exec()s su with these preconditions, and it works. Dang.
But this strategy for me begs an important question: why the <bleep> is it even possible to write directly to memory over an fd? I get that "everything is a file", sure, but you've already got ptrace for insane use cases that require trashing another other process's memory (i.e. a dev toolchain).
I've been twiddling bits in C(++) almost every day for over a decade and I've never once felt the need for such a feature, except when theorizing about exploits. Alas.
I have used the ability to write to /proc/pid/mem to TEST safety-critical software intended for deployment on Linux systems. The apps I wrote were designed to ensure that altered binaries were not being run on the target system - in order to test the effectiveness of this "TEXT Segment check", I wrote many test cases to overwrite memory using this mechanism. Its useful in that context.
This type of clever problem with confused trust relationships is remarkably common in OS Kernels. If you'd like to know more about finding, exploiting and fixing kernel bugs then I'd highly recommend Attacking the Core[1] as an introductory book.
The use of goto[1] in this code surprises me. Isn't use of it still generally shunned?
Appearing in the Linux kernel makes me reconsider my avoidance of it[2]. A quick google throws up a discussion with Linus in 2003[3] which can be summarised as: don't blame the tool, blame the programmer and as a tool goto has a place.
Goto is actually relatively common in C code. As in this case, it's usually used for error handling in much the same manner as exceptions might be. The document "/usr/src/linux/Documentation/CodingStyle" actually recommends such usage.
The reason why Linus Torvalds and others prefer to handle errors using goto is that it avoids the main branch of the code receiving an additional indentation, as it would if it were wrapped in a conditional. Not to do so would make the code less readable.
Note that these gotos are always local jumps (that is, they are in the same code block), at the same indentation level, and they are forward jumps (so the control flow is much the same as for a conditional). They are easy to understand and audit, and not similar to the goto examples that Dijkstra criticised for making code hard to reason about.
I addition what others have said, I'd emphasise that goto for error handling is fine in C, but in C++ it is usually unnecessary because C++ has exceptions and RAII.
Gotos are common in C for error handling because the other alternative of doing an if/then/else with all the error handling is so verbose. It's used to work around the lack of exceptions in C.
You still should not use gotos for regular control flow. And jumping backwards with goto is bad practice in most cases.
Neat. I updated one of my VMs running debian Sid from 2.6.32 kernel to 3.2.0-1-686-pae just to see if it would work. It works on the 3.2 kernel. Kind of scary. I checked to see if I had access to any 'real world' servers running and exploitable kernel and none of them seemed to.
The presented vulnerabilities is so easy to exploit mostly because distros do not compile `su` with PIE and lose the ability to hide memory addresses with ASLR. Just few days ago there has been a discussion about how PIC/PIE is only a performance loss and should not be present in modern Unix systems.
How long would it take to brute-force ASLR for this exploit, given that execution is under control of the exploiter, and can thus be tried many times in quick succession? I recall that some OSes only have a small number of different ASLR permutations, but I don't know the details, and especially not the details of Linux's implementation.
On 32-bit systems, it's feasible to brute force, as there are only about 12 bits of entropy. There are a lot more random bits on 64-bit systems, so I don't consider it brute force to be that practical there.
$ uname -a
Linux sekhmet 3.2.1-1-ARCH #1 SMP PREEMPT Fri Jan 13 06:50:31 CET 2012 x86_64 Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz GenuineIntel GNU/Linux
$ ./mempodipper
===============================
= Mempodipper =
= by zx2c4 =
= Jan 21, 2012 =
===============================
[+] Waiting for transferred fd in parent.
[+] Executing child from child fork.
[+] Opening parent mem /proc/3196/mem in child.
[+] Sending fd 3 to parent.
[+] Received fd at 5.
[+] Assigning fd 5 to stderr.
[+] Reading su for exit@plt.
[+] Resolved exit@plt to 0x401a60.
[+] Calculating su padding.
[+] Seeking to offset 0x401a57.
[+] Executing su with shellcode.
zsh: segmentation fault ./mempodipper
I know. I want to know if someone with a kernel >=2.6.39 and applied grsecurity patch can successfully use this exploit or if grsecurity protects from this exploit.
I'd love to see up to date stable grsecurity kernel repositories for the major distributions (ubuntu, debian, rhel/centos) that provide patched versions of the distribution kernel. You can configure most of grsecurity via the sysctl interface. At the moment it is always a bit of hassle to patch & compile a kernel from hand even with the great debian/ubuntu kernel-package.
I don't think I'll use an extra distribution. But something like a hardened LAMP/LAPP stack for shared hosting out of the box in a distribution would be great (I think in terms of easy chrooting of users and php, secure permissions, etc.pp) However, I guess everyone has different needs and there is no one size that fits for all.
I don't know. I'm just on the end-user side. Just a guess from my (pretty limited) understanding of the issue: The grsecurity[1] patch includes PaX[2] that can break a lot of software. e.g. Java and X11 and there are sometimes other unwanted side effects as well. And I've found a blog post stating that the author does not want to maintain a upstream patch[3].
There are a lot of local root exploits in Linux the past few years. But ever since Linux dropped the stable-unstable version numbering scheme, different distributions ship a wide variety of kernels + their own patches instead of "the latest kernel" because the latest may not necessarily be stable. How do I find out which local root exploits my distribution's kernel is vulnerable against, and how do I find out how quickly they get fixed? I'm on Debian 6 right now.
On my system, all SUID binaries are executable yet not readable, e.g.
$ ls -l /bin/su
-rws--x--x 1 root root 52144 Mar 5 2011 /bin/su
Doesn't this effectively stop the exploit? It still works when I insert the <exit@plt> function address, but I don't think it's possible to trace this without root rights, which kind of defeats the purpose.
Tried this on 2.6.31.1 but it hung while waiting for the fd from the child.
[+] Executing child from child fork.
[+] Opening parent mem /proc/11045/mem in child.
[+] Sending fd 3 to parent.
[+] Waiting for transferred fd in parent.
Very good read.. this post is actually "Hacker News", Seems my Fedora hosts are not susceptible as they compiled with -pie and the kernels at work haven't been updated in sometime.