|
|
Subscribe / Log in / New account

Two glibc vulnerabilities

By Jake Edge
October 27, 2010

Tavis Ormandy has been busy of late, poking around in the guts of GNU libc. Out of that have come two separate local privilege escalations that exploit an obscure corner (the dynamic linker auditing API) of glibc, while the exploits themselves use—abuse—some Linux features that many probably aren't aware of. These vulnerabilities and exploits provide good examples of the way that security researchers look at code and systems—a way of looking that more developers would do well to emulate.

The runtime library auditing API is a way for developers to intercept the actions of the dynamic linker to see the steps that it is taking while searching for .so files and resolving symbols from them. When a program is executed with the LD_AUDIT environment variable pointing to one or more shared libraries, the linker will make callbacks into functions in those libraries for various events that happen in the linking process. There are various events specified in the rtld-audit man page, including searching for an object, opening an object, binding to a symbol, and so on. It seems like a useful facility, but one that is likely not in the toolbox of many Linux developers.

The simpler of the two problems that Ormandy found was that setuid programs will open whatever arbitrary library a user specifies in LD_AUDIT, as long as that library lives on the trusted library path. The more well-known LD_PRELOAD environment variable, which preloads the specified libraries before the linker searches for others, is specifically prohibited from operating on setuid programs unless the library is on the trusted path and has the setuid bit set. Exploiting ping (or some other setuid program) with LD_PRELOAD would be trivial—a user-provided library could remap any call ping made to anything the attacker wanted—so it was an obvious restriction. LD_AUDIT using non-setuid libraries was evidently not so obvious.

The problem with allowing user-provided libraries to be used for auditing setuid programs is not anywhere in the auditing API, but is instead inherent in the way the runtime linker processes libraries. When the library is opened with dlopen() to determine whether the auditing callback symbols are present, any library initialization routines must be run. So, an exploit is done by finding a vulnerable system library (it must be on the trusted path) that was not written with setuid execution in mind (and thus does not have that bit set in the filesystem).

In his description of the flaw, Ormandy gives an example of using the libpcprofile.so library, which writes an output file to the path specified by the PCPROFILE_OUTPUT environment variable. Using ping for its setuid nature, he sets LD_AUDIT to the library, points PCPROFILE_OUTPUT where he wants, and ping ends up putting a user-writable file in /etc/cron.d. The details will vary depending on the distribution, but most will be vulnerable to the flaw. There is nothing particularly special about libpcprofile.so, as Ormandy describes ways to find other vulnerable system libraries, which are likely to be numerous—those libraries weren't meant to be used by privileged programs.

The other vulnerability is more difficult to exploit, but stems from a similar laxness in LD_AUDIT handling. In the Linux executable file format, ELF, library search paths can be specified in the executable itself using DT_RPATH or DT_RUNPATH tags. Those tags can contain a $ORIGIN value, which is replaced with location of the executable in the filesystem. That way, a library used by a single executable can be located in a program-specific location rather than in the system library directories.

The ELF specification recommends that $ORIGIN be disallowed for setuid executables, but glibc ignores that recommendation. Ormandy doesn't really see a problem with that:

It is tough to form a thorough complaint about this glibc behaviour however, as any developer who believes they're smart enough to safely create suid programs should be smart enough to understand the implications of $ORIGIN and hard links on load behaviour. The glibc maintainers are some of the smartest guys in free software, and well known for having a "no hand-holding" stance on various issues, so I suspect they wanted a better argument than this for modifying the behaviour (I pointed it out a few years ago, but there was little interest).

Unfortunately, the $ORIGIN substitution code was reused in the LD_AUDIT path. There was seemingly an attempt to restrict the use of $ORIGIN in LD_AUDIT for privileged programs, but it was insufficient. $ORIGIN will be expanded if it is the only entry in LD_AUDIT. Since $ORIGIN expands to the directory that contained the program, it isn't necessarily obvious that there is anything there to exploit. But, there are known ways to exploit this kind of situation.

If the directory that contains the executable can be replaced with an exploit library object between the time $ORIGIN is expanded and when the value is used, the library will be loaded and the attacker can do what they like. It is essentially a race condition, but one that can be reliably won by the attacker. Ormandy's example basically pauses the execution of a ping that has been hardlinked into an attacker-controlled directory after the expansion of $ORIGIN has been done. He then removes the directory and its contents, and puts a library that has exploit code in its initialization function in the place of the directory.

That particular exploit mechanism is fairly modern, using relatively recent Linux kernel features, but there are others. Ormandy describes several other ways to exploit the flaw, with differing requirements (e.g. a C compiler or winning an easily winnable race) that might serve different attack strategies. While both are local privilege escalations, they very well might be used in conjunction with a web application or other flaw to turn them into a remote root vulnerability.

Both of these vulnerabilities are quite serious for systems that allow untrusted users to log in. Their impact on other systems depends on whether there are other vulnerable, network-facing programs. While it is a bit ironic that it was an audit of LD_AUDIT behavior that found these bugs, it seems clear that there isn't enough of that kind of auditing being done for Linux systems. It's always a bit worrisome to think of how many of these kinds of flaws are still lingering out there.


Index entries for this article
SecurityGlibc
SecurityVulnerabilities/Privilege escalation


to post comments

Two glibc vulnerabilities

Posted Oct 28, 2010 8:22 UTC (Thu) by mjthayer (guest, #39183) [Link] (1 responses)

> These vulnerabilities and exploits provide good examples of the way that security researchers look at code and systems—a way of looking that more developers would do well to emulate.

I have to say that I am rather naive in this respect, and don't have much idea about how to search for vulnerabilities in code (other than grepping for known dodgy functions I suppose and looking for known ways of misusing them). What about an article on that subject? Or does anyone knowledgeable feel like doing a guest article on the subject?

References for program security

Posted Oct 28, 2010 23:30 UTC (Thu) by vonbrand (subscriber, #4458) [Link]

Look at David A. Wheeler's Secure Programming HOWTO for starters.

suid-binary vulnerabilities

Posted Oct 28, 2010 12:08 UTC (Thu) by epa (subscriber, #39769) [Link] (29 responses)

How many of these exploits would be prevented if the kernel always blanked out the environment before running a suid binary? (Unless the execing process is already running as the specified user.)

Or, alternatively, every environment variable X could be renamed .suid.X so the process can get at them if it really wants, but code written without thinking about suid conditions wouldn't be tripped up.

It wouldn't be POSIX, but so what?

suid-binary vulnerabilities

Posted Oct 28, 2010 12:35 UTC (Thu) by mjthayer (guest, #39183) [Link] (28 responses)

> How many of these exploits would be prevented if the kernel always blanked out the environment before running a suid binary?

It might not have helped here, but many exploits would surely be prevented by using some sort of suid loader, something like sudo, to load binaries which have to run setuid root, rather than marking the binaries themselves. That way the loader could take care of most the little things you have to think of when playing with suid and the binary would have less things (though still enough) it could get wrong.

suid-binary vulnerabilities

Posted Oct 28, 2010 12:58 UTC (Thu) by cesarb (subscriber, #6266) [Link] (27 responses)

Or even better, do not use setuid at all.

For instance, imagine if ping, instead of being setuid, called into dbus to load a helper daemon, and that helper daemon did all the actions which need root (in this example, sending pings). The part controlled by the user would limit itself to the user interface (showing one line per packet, doing the average calculations, and so on).

This way, the daemon is always run in a clean environment (the one dbus uses to launch daemons).

In the ping example, this also has the advantage that it makes it harder to avoid the restrictions on the minimum ping interval by running several instances of ping in parallel (no matter how many you run, they will talk to a single daemon, which can limit the ping rate per user or even globally).

suid-binary vulnerabilities

Posted Oct 28, 2010 13:31 UTC (Thu) by mjthayer (guest, #39183) [Link]

> Or even better, do not use setuid at all.
>
> For instance, imagine if ping, instead of being setuid, called into dbus to load a helper daemon, and that helper daemon did all the actions which need root (in this example, sending pings).

Actually a clean environment was the main point of what I suggested above - i.e. using a setuid loader which can clean the environment (not just the environment as in setenv of course) before it loads your privileged binary. My worry with using dbus for this is that it requires a sizeable piece of infrastructure to be present and running properly in order to start your binary, which is fine for desktop use, but may not be appropriate for all situations.

suid-binary vulnerabilities

Posted Oct 28, 2010 14:22 UTC (Thu) by marcH (subscriber, #57642) [Link] (13 responses)

> Or even better, do not use setuid at all.

Indeed setuid looks very much like a kludge

> For instance, imagine if ping, instead of being setuid, called into dbus to load a helper daemon,...

This looks like PolicyKit. Another setuid killer is setcap:

chmod u-s /bin/ping
setcap 'CAP_NET_RAW+ep' /bin/ping

Et voilà, all these LD_AUDIT holes are plugged without even upgrading glibc. Correct? If yes why isn't ping installed like this by default?

suid-binary vulnerabilities

Posted Oct 28, 2010 14:58 UTC (Thu) by RobSeace (subscriber, #4435) [Link] (12 responses)

Well, the hole becomes less serious when exploited, but I wouldn't say it was
"plugged", really... Now, successfully exploitation just gets you the ability
to create raw sockets instead of full root... But, raw sockets aren't really
something you want to give to an untrusted user to play with, either...

But, yeah, it'd still be better than giving them root, so yeah, why isn't this
done if it's possible?

suid-binary vulnerabilities

Posted Oct 28, 2010 15:21 UTC (Thu) by ccurtis (guest, #49713) [Link] (8 responses)

Looks like Fedora 15 is going to try it.

http://fedoraproject.org/wiki/Features/RemoveSETUID

suid-binary vulnerabilities

Posted Oct 28, 2010 19:44 UTC (Thu) by dlang (guest, #313) [Link] (1 responses)

they are not fundamentally changing anything, they are just moving from a single suid bit to a array of individual capibilities. This still lets a user execute a program that will have more privilages than the user with whatever environment the user defines.

suid-binary vulnerabilities

Posted Oct 29, 2010 11:00 UTC (Fri) by marcH (subscriber, #57642) [Link]

Still looks like a major improvement to me.

suid-binary vulnerabilities

Posted Oct 28, 2010 22:04 UTC (Thu) by kees (subscriber, #27264) [Link] (5 responses)

This just slightly reduces the attack surface, but doesn't fundamentally solve the problem (vulnerabilities like this in the loader are extremely dangerous). There will still be things with CAP_SETUID. Here's Tavis's $ORIGIN attack, unchanged except modified to target /bin/su instead of /bin/ping, and with the proposed change made to /bin/su (drop setuid, gain CAP_SETUID):

[kees@fedora-13-i686 ~]$ ls -la /bin/su
-rwxr-xr-x. 1 root root 29292 Feb 12 2010 /bin/su
[kees@fedora-13-i686 ~]$ getcap /bin/su
/bin/su = cap_setuid+ep
[kees@fedora-13-i686 ~]$ ./glibc-ld_audit-origin.sh
[root@fedora-13-i686 ~]# id
uid=0(root) gid=500(kees) groups=0(root),500(kees) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

suid-binary vulnerabilities

Posted Oct 29, 2010 10:27 UTC (Fri) by marcH (subscriber, #57642) [Link] (4 responses)

Please be fair and compare CAP_NET_RAW with CAP_SETUID..

suid-binary vulnerabilities

Posted Oct 29, 2010 11:49 UTC (Fri) by kees (subscriber, #27264) [Link] (3 responses)

Why? If this is about whole-system security, there will still be binaries with CAP_SETUID (su, sudo, newrole, seunshare, etc). It absolutely reduces the attack surface in general, but linker vulnerabilities will remain a serious problem. Removing the setuid bit is a great idea for reducing the impact of bugs in the setuid program itself, though.

suid-binary vulnerabilities

Posted Oct 29, 2010 11:52 UTC (Fri) by rahulsundaram (subscriber, #21946) [Link]

"Removing the setuid bit is a great idea for reducing the impact of bugs in the setuid program itself, though"

Precisely, the goal.

suid-binary vulnerabilities

Posted Oct 29, 2010 13:41 UTC (Fri) by marcH (subscriber, #57642) [Link] (1 responses)

> Why? If this is about whole-system security, there will still be binaries with CAP_SETUID (su, sudo, newrole, seunshare, etc).

"Let's not bother making the windows more secure, because the front door sucks anyway".

Actually, let's bother. Because it's progress:
- progress towards the entire perimeter being finally secured.
- some malware knows only about windows. Being hacked once a month is progress compared to twice.

> It absolutely reduces the attack surface in general,...

Agreed!

suid-binary vulnerabilities

Posted Oct 29, 2010 15:14 UTC (Fri) by kees (subscriber, #27264) [Link]

Right, I don't meant to say it shouldn't be done. Getting rid of the setuid bit is a great goal. I was just trying to point out that it does not solve problems like those recently found in glibc. It _does_, of course, kill a whole separate set of problems, and I love that. :) I just don't want people to think dropping setuid bits is a magic bullet for solving all local privilege escalations.

suid-binary vulnerabilities

Posted Oct 28, 2010 17:05 UTC (Thu) by jreiser (subscriber, #11027) [Link] (2 responses)

why isn't this done if it's possible? There are [have been] bigger fish to fry.

suid-binary vulnerabilities

Posted Oct 28, 2010 18:53 UTC (Thu) by RobSeace (subscriber, #4435) [Link] (1 responses)

Oh, and I just realized that if a system were to do this for all setuid binaries,
then it would be very important for the linker to treat these "enhanced capability"
programs just as it would setuid/setgid programs... Ie: don't allow $LD_PRELOAD
and such... Otherwise, of course, it would be trivial for anyone to gain their
enhanced capabilities... Which, while not as bad as gaining root, is still not
something you want to make trivially easy to do...

suid-binary vulnerabilities

Posted Oct 28, 2010 19:55 UTC (Thu) by spender (guest, #23067) [Link]

This is exactly what the AT_SECURE auxv entry already does.

-Brad

suid-binary vulnerabilities

Posted Oct 28, 2010 21:24 UTC (Thu) by jengelh (guest, #33263) [Link]

>For instance, imagine if ping, instead of being setuid, called into dbus to load a helper daemon

Or just enhance the kernel to enable socket(AF_INET, SOCK_RAW, IPPROTO_ICMP) for unprivileged users, and scrutinize packets sent/received through the socket.

suid-binary vulnerabilities

Posted Oct 29, 2010 7:47 UTC (Fri) by viro (subscriber, #7872) [Link] (10 responses)

Great improvement, that - now ping depends on dbus. Which is to say, that Fine Piece Of Software suddenly becomes mandatory on boxen that used to avoid it just fine.

suid-binary vulnerabilities

Posted Oct 29, 2010 10:59 UTC (Fri) by marcH (subscriber, #57642) [Link] (9 responses)

> Great improvement, that - now ping depends on dbus.

Actually, ping using D-Bus would be such a change that you would rather have new-secure-dbus-user-ping on one hand and good-old-insecure-root-ping on the other hand. Embedded and other single user systems can just run everything as root and use the old one.

If you are serious about security you really need a good IPC on multi-user systems... what would you use instead of D-BUS?

suid-binary vulnerabilities

Posted Oct 31, 2010 15:18 UTC (Sun) by nlucas (guest, #33793) [Link] (8 responses)

I don't think ping is a good example for this argument.

It's too simple, so you could also solve this problem by making a static build of ping that can not load any shared library.

In my "non-security guy" perspective that would be enough for most environments.

suid-binary vulnerabilities

Posted Nov 1, 2010 11:17 UTC (Mon) by mjthayer (guest, #39183) [Link] (7 responses)

> I don't think ping is a good example for this argument.
>
> It's too simple, so you could also solve this problem by making a static build of ping that can not load any shared library.

I thought that with ELF there was no such thing as a pure statically linked binary.

suid-binary vulnerabilities

Posted Nov 1, 2010 23:34 UTC (Mon) by nix (subscriber, #2304) [Link] (6 responses)

There certainly is! PT_INTERP isn't mandatory: if it's not set, the executable has no interpreter, thus cannot use shared libraries and must be self-contained.

(Also, if pure statically-linked binaries cannot exist, what do you think /lib/ld-linux.so.2 is? If ELF interpreters don't count because they are technically shared objects and relocate themselves, /sbin/sln surely does. No relocation, no PT_INTERP: static as static comes, and on pretty much every system.)

suid-binary vulnerabilities

Posted Nov 1, 2010 23:52 UTC (Mon) by anselm (subscriber, #2796) [Link] (1 responses)

/sbin/sln surely does. No relocation, no PT_INTERP: static as static comes, and on pretty much every system

I'm on Debian sid, and I don't have an executable called »sln« – not in /sbin, not anywhere. Am I missing something?

suid-binary vulnerabilities

Posted Nov 2, 2010 0:14 UTC (Tue) by nix (subscriber, #2304) [Link]

It's part of glibc, and is bloody useful e.g. to put your dynamic linker symlink back in place if something blows it away. Since this is its primary purpose, perhaps some distros don't ship it... but if you don't have that I hope you have sash or busybox or something else statically linked to recover your system in case of disaster. (OK, a boot CD/USB key could do the same job but is somewhat inelegant.)

suid-binary vulnerabilities

Posted Nov 2, 2010 0:17 UTC (Tue) by dlang (guest, #313) [Link] (3 responses)

one thing on recent systems is that the name resolution libraries are loaded dynamically, even if you compile a 'static' binary. If those libraries are not available as separate files, in the expected location, name resolution fails.

suid-binary vulnerabilities

Posted Nov 3, 2010 14:58 UTC (Wed) by nix (subscriber, #2304) [Link] (2 responses)

In this case, 'recent' is 'more recent than glibc 2.2'. I haven't seen a glibc 2.2 system for many years. Essentially all current Linux systems work this way. (Usernames as well as hostnames: everything that uses NSS.)

suid-binary vulnerabilities

Posted Nov 3, 2010 23:29 UTC (Wed) by cesarb (subscriber, #6266) [Link] (1 responses)

Does it still happen if you use nscd? Or does it simply open the socket to nscd and lets it load the NSS stuff?

suid-binary vulnerabilities

Posted Nov 4, 2010 0:18 UTC (Thu) by foom (subscriber, #14868) [Link]

Depends on the function. Not all NSS functionality goes through nscd even when it's enabled. I forget the details of which do and which don't, though.

Two glibc vulnerabilities

Posted Oct 29, 2010 14:50 UTC (Fri) by jzbiciak (guest, #5246) [Link]

If the directory that contains the executable can be replaced with an exploit library object between the time $ORIGIN is expanded and when the value is used, the library will be loaded and the attacker can do what they like. It is essentially a race condition, but one that can be reliably won by the attacker. Ormandy's example basically pauses the execution of a ping that has been hardlinked into an attacker-controlled directory after the expansion of $ORIGIN has been done. He then removes the directory and its contents, and puts a library that has exploit code in its initialization function in the place of the directory.

Ok, maybe my coffee hasn't kicked in yet. Why is it necessary to remove / replace the attacker-controlled directory in this case?

For example, suppose /var/tmp and /bin are (perhaps unwisely) on the same partition:

$ mkdir /var/tmp/evil
$ ln /bin/ping /var/tmp/evil/ping
$ cp $HOME/libevil.so /var/tmp/evil/.
$ export LD_AUDIT='$ORIGIN/libevil.so'
$ /var/tmp/evil/ping
# whoami
root

Or is it that the linker only expands LD_AUDIT if it is precisely '$ORIGIN', and so you have to replace the directory itself with a library file?

Also, am I correct in thinking that if you keep your untrusted-user-writeable directories on different partitions from your setuid executables, you'll thwart this attack?


Copyright © 2010, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds