| |
Subscribe / Log in / New account

Prelink and address space randomization

This article brought to you by LWN subscribers

Subscribers to LWN.net made this article — and everything that surrounds it — possible. If you appreciate our content, please buy a subscription and make the next set of articles possible.

July 5, 2006

This article was contributed by John Richard Moser

Prelink (PDF) is a popular tool used to decrease program load time, shortening system boot time and making applications start faster. Developed by Jakob Jelinek at Red Hat, prelink relocates libraries on disk to save dynamic linking time.

When the dynamic linker loads a dynamically linked ELF binary, it has to also load and link all of the libraries before executing the program's entry point, _main(). This process involves relocating libraries—changing all addresses referenced in the library to reflect the actual addresses in memory. Relocating libraries involves iterating through each address in the library and replacing it with the real address as determined by the library's location in the process's virtual address space. Most relocations happen in the symbol table and PLT; but in rare cases there are also .text relocations which require fixed-position executable code to be patched in a slightly slower process.

The relocation process will slow down an application's launch. In order to speed up the process, prelink relocates the libraries ahead of time. This is done by scanning every executable to be prelinked, generating a graph of libraries that will be loaded at the same time as other libraries, and then calculating target addresses for each library at such that it will never be loaded at the same address as other libraries. These offsets are then stored in the shared object files themselves, and the symbol tables and segment addresses are all adjusted to reflect addresses based on the chosen base address.

Once prelink has done its job, the dynamic linker no longer has to concern itself with relocation. Libraries are loaded at the address specified in the library header and the symbol table is already correct. If anything forces the library to be loaded at a different address, then the library is relocated appropriately as usual; otherwise we can say goodbye to the load-time overhead of relocating libraries.

Kernel facilities supplying address space layout randomization for libraries cannot be used in conjunction with prelink; to do so would require relocating the libraries, defeating the purpose of prelinking. Address space randomization is a core feature of secure systems such as OpenBSD, Adamantix, Hardened Gentoo, Fedora Core, and Red Hat Enterprise Linux. It has appeared as part of PaX as well as part of Ingo Molnar's Exec Shield, and has been accepted into the mainline kernel as of 2.6.12 after submission by Arjan van de Ven.

The simple purpose of address space randomization is to make it more difficult to perform certain classes of attacks by changing where in memory important segments for the attack are loaded. If an attacker wants to execute injected shell code or manipulate the program to execute out of order, he obviously has to know where that code is. By shuffling memory segments around, these attacks become quite difficult; the chances of successful attack are mathematically described in the PaX documentation and Wikipedia.

In an attempt to restore some of the benefits of address space randomization, prelink is capable of randomly selecting the addresses used for prelinking. This makes it more difficult to perform certain attacks on a system, because the addresses used are unique to that system. This approach is, however, less effective than per-process randomization because the addresses stay constant until prelink is run again.

There is another implication that has to be examined with prelink. To understand this implication, let us first review a feature of prelink by examining the load address of the C standard library in two processes: a user-owned 'cat' and a root-owned 'bash'. The C standard library is interesting because, in practice, virtually all return-to-libc attacks utilize it exclusively.

 user@icebox:~$ cat /proc/self/maps | grep libc | grep r-xp
 4df2e000-4e053000 r-xp 00000000 08:07 81197      /lib/tls/i686/cmov/libc-2.3.6.so
 user@icebox:~$ sudo -s
 root@icebox:/home/user# cat /proc/$$/maps | grep libc | grep r-xp
 4df2e000-4e053000 r-xp 00000000 08:07 81197      /lib/tls/i686/cmov/libc-2.3.6.so

Closely examining these quickly verifies that the address of glibc's executable code is the same between these two processes; this is consistent with the behavior of prelink. Because the library itself is relocated ahead of time, there is a preference for the dynamic linker to load it at that address. Examination of libc itself yields the below.

 user@icebox:~$ readelf -S /lib/tls/i686/cmov/libc-2.3.6.so | head -n 6
 There are 64 section headers, starting at offset 0x12d114:
 
 Section Headers:
   [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
   [ 0]                   NULL            00000000 000000 000000 00      0   0  0
   [ 1] .note.ABI-tag     NOTE            4df2e154 000154 000020 00   A  0   0  4

Computing 4df2e154 - 154, the address and offset taken from any given non-NULL segment, yields 4df2e000, the base address of libc. This makes sense; prelink rewrites the segment and symbol addresses for the library based on a specific load address, and the dynamic linker loads the library at that address to avoid relocating it. Further, any program that links with libc has to be able to read libc, and will thus be able to derive the same information.

All of this means that any program on the system using any prelinked library will be able to leak information about higher privileged tasks using the same library. This allows any attacker able to gain any form of local access—or more directly any ability to read libc—to gain information about the address space layout of higher privileged processes, including the load address of libc. As we know, this information is extremely valuable to an attacker wanting to exploit a privileged process without brute forcing library load addresses.

This vulnerability only applies to attackers with local access; but this is not an unreasonable requirement. Many web hosting companies give local shell access or allow PHP; either of these can be used to remotely fetch a copy of libc. Due to the nature of the dynamic linker and sane security design, the dynamic linker is exactly as privileged as the process it is starting; therefor, even the most stringent mandatory access policies on systems such as SELinux, grsecurity, or AppArmor cannot prevent this attack.

Besides avoiding prelinking, there is one other way to prevent this information leak from being exploited. All processes linked to a prelinked library need access to the library file and load that library at the same address; the point of exposure is the use of the same copy of the library. In order to prevent information leaking, then, you must have separate copy of each library common between any two programs you don't want to leak information about each other. This can be done with Xen, chroot jails, UML, or simply isolated machines, as long as the directory hierarchies are individually prelinked with prelink randomization. Each system will have a different set of addresses from every other system in this scheme. This of course requires more hardware, more disk space, more management, more memory, and more work.

The direct implications of this information leak depend on your exact security concerns. A web hosting company, for example, may not want to run prelink on its servers, given the risk of effectively losing the benefit of address space randomization. A home desktop, on the other hand, may only have to worry about a trojan using the information leak to stage an attack on a system service such as cups or dbus—and should probably worry about /proc/PID/maps first. While these are both essentially the concern of an attacker with local access, the likelihood of attack and the value of potential damages are different.

The prelink tool gives a useful decrease in program load time, and can help users reach their desktop and the programs they need to run more quickly. It does however have some unfortunate repercussions that must be examined, especially in security-sensitive environments relying on address space randomization.


Index entries for this article
GuestArticlesMoser, John Richard


(Log in to post comments)

Prelink and address space randomization

Posted Jul 6, 2006 21:08 UTC (Thu) by oak (guest, #2786) [Link] (1 responses)

Prelink also saves memory because relocating symbols dirties the memory
pages on which the symbols are (makes that memory private for the
process). For servers running many instances of the same binaries this
could be significant. However, they could to prelink only most used
binaries...

Prelink and address space randomization

Posted Jul 7, 2006 0:49 UTC (Fri) by nix (subscriber, #2304) [Link]

prelinking only the most used binaries won't necessarily help; that will necessarily prelink libc and ld.so as well, and while these will benefit from ASLR when a non-prelinked app is run, if the attacker can wander around the libc's ELF structures it can determine what its preferred load address is in any case, even though it wasn't actually loaded there.

Personally, I prelink non-network-exposed systems only (and make my network-exposed systems stripped-down UML instances).

Prelink and address space randomization

Posted Jul 7, 2006 1:00 UTC (Fri) by brian (subscriber, #6517) [Link] (4 responses)

Doesn't run-time linking use the same in-memory copy of libc for every executable anyway?

Prelink and address space randomization

Posted Jul 7, 2006 4:10 UTC (Fri) by jamesh (guest, #1159) [Link] (3 responses)

Most of the library text is shared, yes. But if the library is loaded at different addresses, then the functions and variables will have different memory addresses. So pointers to the functions and variables will need to be fixed up to point at the correct place. This is the relocation process and results in portions of the library text being modified for the app and hence not being shared.

As the article explained, the prelink process tries to do this relocation ahead of time so different apps can use the same library text unmodified (and hence share it completely), but this means that the load addresses of libraries are not randomised between different processes.

Prelink and address space randomization

Posted Jul 7, 2006 9:39 UTC (Fri) by nix (subscriber, #2304) [Link]

Of course, the kernel could still randomize the load addresses anyway; but if it did that, the benefits of prelink would be entirely negated.

(prelink has sped up a lot recently, did you notice? You no longer have to wait eight thousand years for C++ stuff with lots of symbols to be prelinked...)

Prelink and address space randomization

Posted Jul 7, 2006 16:19 UTC (Fri) by jzbiciak (guest, #5246) [Link]

BTW, it's the fact that the same shared copy of the library can be loaded at different addresses for different processes that requires dynamic libraries to use Position Independent Code (PIC).

Now, what might be interesting is a selectively applied address randomization. For instance, tell the kernel that for some list of UIDs, apply address randomization, and for the rest, use a default address map.

So, for instace, an average desktop with eleventy billion programs running the desktop environment, etc., all running as a non-privileged user--those can all benefit from prelink. Any system services running in the background with elevated privileges or a network-facing component, such as sshd, CUPS, a webserver, whatever... those should all run with address randomization.

And, if some program seems to be problematic wrt. to buffer overflows? Perhaps allow marking it to be randomized as well regardless of UID. (Perhaps extended fs attributes are a good way to denote this?)

Prelink and address space randomization

Posted Aug 20, 2006 7:52 UTC (Sun) by bluefoxicy (guest, #25366) [Link]

"portions of the library text" what? That's TEXTRELs. There is a specific set of relocation sections that tell where to make relocations; they're ALL done in the Global Offset Table, which the library text references. TEXTRELs happen only as a result of bugs; rewriting library text is slow. :|

Good try, but read up on how PIC works.

Prelink and address space randomization

Posted Jul 7, 2006 13:34 UTC (Fri) by jakub@redhat.com (guest, #31780) [Link] (6 responses)

Security sensitive binaries should be linked as Position Independent
Executables (-pie). Those are not prelinkable and the kernel and dynamic
linker by default ignores the prelink chosen load addresses of libraries.
So, if e.g. all network facing and suid programs are PIEs and the rest
is not, you can prelink the whole system. PIEs will have full address
space randomization, while the rest of programs will have shared libraries
(and the binary) always at the same addresses (until next forceful
reprelinking, which is usually every fortnight or so).

Prelink and address space randomization

Posted Jul 7, 2006 18:58 UTC (Fri) by bluefoxicy (guest, #25366) [Link] (5 responses)

Unfortunately some of the most security sensitive binaries aren't typically PIE'd, even on RedHat systems (at least not last I looked; I was told explicitly "network-facing daemons"). These can inclcude:

- Konqueror plug-ins (they run as separate executable)
- Web browsers (Firefox)
- Music players (playing MP3s you just downloaded from creative commons? STREAMING radio?!)
- Video players (same problem as music players)
- Gaim (Ugly away message a la CAN-2005-2103?)
- IRC programs (Xchat is stupid to run as root? Ever do a local root exploit? No we don't all have SELinux...)
- E-mail clients (buggy handling of JavaScript or ugly e-mail headers?)

If you want to nit pick, you could even say something about less obvious binaries:

- Office suites (malicious Word documents that overflow the decoder)
- File browsers (rabidly displaying thumbnails of images-- remember one day when SIX PNG EXPLOITS showed up at the same time and what FOUR of them were RCE?)
- Image editors (Here open this .psd... no I don't know how or why you would trick the user into it but it could happen)
- compression programs (zip, tar, gzip, bzip2, and p7zip; anything using libz or libbz2; etc. IIRC libz <=1.1.3 had that lovely double-free())
- Games (globulation2, Nexuiz, or Armagetron... think about it, you'll figure it out. Look up Stronghold 2 in the CVE database for a non-hypothetical)

And if you really want to go all out:

- Shells (for those days when they just don't want to deal with certain escape sequences or invalid UTF8)
- Terminal emulators (these HAVE had exploits from bad escape sequence handling)
- Script interpreters (for those days when you break the parser or a built-in function implemented in a C library)

I can't think of anything more ridiculous to exploit than a shell, sorry. 'ls' and 'cat' would be cute but I can't think of any technically feasible screw-up that would leave those exploitable. Note most of these are theoretical; I don't have real shell/script interpreter exploits in mind.

Back to the original topic, let's not forget that prelink is also available on Debian/Ubuntu and Gentoo. On Debian/Ubuntu we don't yet have pie; on Gentoo we have pie everywhere*** or nowhere. Building such binaries as PIE is a good idea and fairly non-invasive (the overhead is about 1%* in about 5-8%** of the code ever executed, so 0.05%-0.08%), but it's not wide-spread and it defeats the purpose of prelink in those cases.

* There is a 5% performance increase gained by -fomit-frame-pointer on x86, but NOT in PIC. I didn't say 6% because Ubuntu doesn't seem to use -fomit-frame-pointer anyway. On x86-64 the overhead is like 0.02% max.

** About 4-7% of the code executed on the system is in Xorg's main executable; every other program seems to execute under 1% in the main executable, most under 0.2%. This information courteous of me mass-profiling my whole damn system with oprofile because Theo de Raadt insists that PIE is "very expensive."

*** Everywhere except where it breaks things, which is a lot of places. Turn off hand-optimized assembly in everything (during ./configure or with USE flags) if you want to attempt this. It AND ProPolice used to break Xorg; I don't know if Xorg can handle it yet, I haven't used Hardened Gentoo since Ubuntu hit the map.

Prelink and address space randomization

Posted Jul 13, 2006 21:03 UTC (Thu) by roelofs (guest, #2599) [Link] (4 responses)

- File browsers (rabidly displaying thumbnails of images-- remember one day when SIX PNG EXPLOITS showed up at the same time and what FOUR of them were RCE?)

Was there ever a proof of concept for any of those (or even for the zlib double-free)? I don't recall one, and I have a certain personal interest in such things. But I could have missed it easily...

And FWIW, it was six by CERT's count (of which three were considered potentially remotely exploitable and the others were "impact unknown"), but MITRE (or whoever) considered them as three cases (CVE candidates 2004-0597 through 0599). All are linked from the bottom of the libpng page.

Greg

Prelink and address space randomization

Posted Jul 14, 2006 2:13 UTC (Fri) by bluefoxicy (guest, #25366) [Link] (3 responses)

Three then. I think one is enough to say "we should not load random images in file browsers and expect them to never try to hax0rz us with their l33t skr1ptz1ez!!!1113"

Search milw0rm and security focus if you're looking for exploit code. There might be some there. Also I hear packetstorm keeps loads of example code around but I never personally looked; I may be a security nut but I am more into understanding why these things work than memorizing every exploit in every program ever written.

Prelink and address space randomization

Posted Jul 21, 2006 15:47 UTC (Fri) by roelofs (guest, #2599) [Link] (2 responses)

Three then. I think one is enough to say "we should not load random images in file browsers and expect them to never try to hax0rz us with their l33t skr1ptz1ez!!!1113"

Oh, absolutely--I'm not defending the existence of the security problems, just questioning the sensationalism and risk-inflation that seems to be inherent in security companies' announcements these days.

Consider the most recent issue (listed under "png" rather than "libpng" in this week's Security page). Yes, it's a buffer overflow, but does that automatically mean remote code execution? It's an overflow by two bytes, and those two bytes are "n" and "k" no matter how mangled you make the image. Now what? Is there any realistic way to exploit that for anything beyond denial of service? Every RCE I've ever seen is highly dependent on the attacker's ability to write dozens of bytes to the stack, bytes that contain the code to be executed. That's not the case here.

And while it's still conceivable that random chance could cause these two bytes to combine with two others to overwrite a return address and send the code off to somewhere in the compressed or uncompressed image buffer for execution of badness, I suspect that possibility is so remote as to be nonexistent in any practical sense. (Also, further analysis might well be able to demonstrate that it's physically impossible--e.g., because the other two bytes are constrained by upstream libpng stack usage or because the "nk" become the high bytes and refer to an impossible address in either 32-bit or 64-bit Linux systems or whatever. But now I'm speculating...)

Anyway, it's definitely bad that libpng and other image-processing libraries continue to have these bugs. Not terribly surprising, mind you, given their complexity, but bad nonetheless. I keep hoping we're simply plugging longstanding holes and thus making the net result safer at every step, but I don't know if that's true, either.

Greg

Prelink and address space randomization

Posted Jul 22, 2006 15:35 UTC (Sat) by roelofs (guest, #2599) [Link]

I hate replying to myself, but:

It's an overflow by two bytes, and those two bytes are "n" and "k" no matter how mangled you make the image.

Doh, it's a string: the two bytes are always "k" and a NULL, not "n" and "k". But the same line of argument holds.

Greg

Prelink and address space randomization

Posted Aug 20, 2006 7:58 UTC (Sun) by bluefoxicy (guest, #25366) [Link]

Apache did this recently, didn't it? "We have an overflow, but you always overflow by like 3 bytes and they're always these bytes, and so it's not exploitable" then like 4 days later "OK somebody owned one of the devs' personal boxes we were wrong there was a really cool trick we missed."

Point is, we shouldn't say "We found an RCE here" if we don't see a way; but we should still say "there may be implications we have not discovered with this." It may be overflow by "k\0" today but tomorrow someone might make it overflow something else in there. We should neither assert that we know this will happen, nor that we know this will not happen.

Prelink and address space randomization

Posted Jul 15, 2006 11:31 UTC (Sat) by mfr (guest, #39093) [Link]

IMO The best approach would be to have ASLR on and prelink off to be secure by default and have
prelink as a choice via /proc or similar for desktop systems or whoever wants to use it.

Prelink and address space randomization

Posted Aug 7, 2006 9:04 UTC (Mon) by nix (subscriber, #2304) [Link]

I may be missing something. If this is turned on, the kernel randomizes the vDSO address as early as possible: i.e., before ld.so is entered. Therefore, there's no way we can tell prelink is in use: and indeed it looks like the vDSO gets randomized no matter whether prelink is used or not.

So what keeps the vDSO from being mapped slap bang in the middle of space allocated to a prelinked library, breaking prelinking for that application? Nothing, as far as I can tell.

I wodner just how much this increases memory usage and how many unnecessary relocations this causes on a completely prelinked system...


Copyright © 2006, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds