Hacker News new | past | comments | ask | show | jobs | submit login
Linux Local Privilege Escalation via SUID /proc/pid/mem Write (zx2c4.com)
199 points by zx2c4 on Jan 23, 2012 | hide | past | favorite | 55 comments



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.


Makes sense.

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.


ptrace() lets you read a word at a time per syscall (slowwww), presumably /proc/pid/mem allows mmap'ing full regions in a single syscall.


I'm still clueless how su gets the same pid as his exploit...

Maybe i will get it when it's not 4am


PID does not change on execve().


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.

[1] - http://www.attackingthecore.com/


Clever. Reminds me of OpenBSD's rfork bug:

http://www.openbsd.org/advisories/rfork.txt


I hadn't read about this oldie (97) before. Awesome! Good read.


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.

[1] XKCD comic on the subject http://xkcd.com/292/

[2] On my infrequent visits to C++ land, I generally live in Python land where goto doesn't exist.

[3] http://kerneltrap.org/node/553/2131


Goto is common for error out code in C. The code that uses that mentioned in the blog code is from the linux kernel, not any exploit code.

But please, for the love of god, can we not make this discussion thread an extended conversation on the merits or horrors of goto? Pretty please?


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.


This. Also python's execeptions can be treated as limited goto's: http://docs.python.org/faq/design.html#why-is-there-no-goto


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.



meta: I don't see why it is necessary to downvote newbie questions on HN


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.


Yulp >=2.6.39 :-)


Strangely, it doesn't work inside of screen. I'd bet that it's because of the parent ID?


http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6...

The commit message (10 months ago) when the vulnerability has been introduced is quite interesting:

"With recent changes there is no longer a security hazard with writing to /proc/pid/mem. Remove the #ifdef."

I'm wondering how long it will take in a proprietary software without any good commit messages to discover such bug...


What makes you think that proprietary software doesn't have good commit messages?


I mean commit messages accessible to a large potential audience that could review them.


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.

[1] https://news.ycombinator.com/item?id=3472618 [2] https://news.ycombinator.com/item?id=3473068


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.


"because distros do not compile `su` with PIE"

Really? Every single distro I've tested has su compiled with PIE.


From the article:

> It turns out that su on the vast majority of distros is not compiled with PIE, disabling ASLR for the .text section of the binary!

I tested on Ubuntu 10.04 LTS, likely to be installed on many servers, and 11.04. On these two versions `su` has not been compiled with PIE.

Edit: just to make things clear: in these Ubuntu versions su is compiled without PIE, but are not vulnerable because they have kernels < 2.6.39.


No workie here on ArchLinux:

    $ 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


Also works on Arch with minor modifications.

Had to change 'exit@plt' to '<exit@plt' where it searches for the relevant function and change the program run from /bin/su to /bin/mount.


Thanks for the improvement. I've made the < modification in this commit: http://git.zx2c4.com/CVE-2012-0056/commit/?id=49174152d57ffc...

And made a separate branch for /bin/mount: http://git.zx2c4.com/CVE-2012-0056/commit/?h=mount


Have you checked if Arch's su uses a relocatable text segment?


Is a grsecurity[1] patched kernel safe? I only have a machine with a 2.6.32-grsec kernel, so I can't test the exploit.

In the features list they state:

    /proc/pid filedescriptor/memory protection
But I'm unaware how they implemented that.

1: http://grsecurity.net/features.php


it affects only 2.6.39 and up.

EDIT: sorry, misread the parent.


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.


Just tried it on 3.0.4 with grsecurity enabled, and it didn't work, so there.


Thanks. I guess It's finally time for me to move every machine to a grsecurity kernel.


Would you be interested in a grsecurity distro?


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.


Why is grsecurity not merged upstream?


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].

1: http://en.wikipedia.org/wiki/Grsecurity

2: http://en.wikipedia.org/wiki/PaX

3: http://www.corsac.net/?rub=blog&post=1535


Egos and the childish behaviour of half the kernel developers involved in Linux, this includes Spender and co at grsec


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.


If you read the article it says that only versions >=2.6.39 are vulnerable.


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.


It doesn't stop the exploit, as it is still possible to use ptrace to essentially dump the binary, even though it's not readable.


Or if you know the distro, it is trivial to get the package containing the su executable and locate the address.


I compiled it myself, so that is not an option.


Presumably they only need to guess the flags you used then. There is really not all that much entropy there.

And I suspect doing so is fairly uncommon in production environments anyway.


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.
I waited 2-3 minutes.

<edit> Argh, I need 2.6.39, sorry.


Just tried this out on my ubuntu laptop (3.0.0-15-generic) and it worked like a charm. Scary.


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.



Arch Linux, 3.2.1

    ....
    [+] Executing su with shellcode.
    [1]    18663 segmentation fault  ./a.out




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: