

Linux Local Privilege Escalation via SUID /proc/pid/mem Write - zx2c4
http://blog.zx2c4.com/749

======
a1k0n
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.

~~~
Animus7
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.

~~~
seclorum
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.

------
iuguy
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/>

------
tptacek
Clever. Reminds me of OpenBSD's rfork bug:

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

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

------
hartror
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>

~~~
chalst
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.

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

------
adulau
[http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6...](http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=198214a7ee50375fa71a65e518341980cfd4b2f0)

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

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

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

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

~~~
zx2c4
Yulp >=2.6.39 :-)

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

------
gioele
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>

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

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

~~~
gioele
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.

------
static_cast
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>

~~~
lloeki
it affects only 2.6.39 and up.

EDIT: sorry, misread the parent.

~~~
static_cast
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.

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

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

~~~
ColdAsIce
Would you be interested in a grsecurity distro?

~~~
static_cast
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.

~~~
asomiv
Why is grsecurity not merged upstream?

~~~
static_cast
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](http://www.corsac.net/?rub=blog&post=1535)

------
lloeki
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

~~~
ahv
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.

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

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

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

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

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

~~~
rzhou
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.

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

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

~~~
burgerbrain
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.

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

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

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

~~~
zx2c4
This version works on Fedora:
[http://git.zx2c4.com/CVE-2012-0056/tree/mempodipper.c?h=fedo...](http://git.zx2c4.com/CVE-2012-0056/tree/mempodipper.c?h=fedora)

------
VMG
Arch Linux, 3.2.1

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

