ETDYN guide The PaX Team solar Alexander Gabert John Davis Zack Gilburd This guide contains documentation and examples on how to create dynamic ELF executables. These guidelines are required to achieve full Address Space Layout Randomization. 1.1 2003-08-05 Introduction

One of the features of PaX is Address Space Layout Randomization (ASLR) that allows the kernel to randomize the addresses of various areas in the task's address space. While most of ASLR requires no changes in userland, randomizing the main executable's base address presents a challenge as traditionally such ELF executables of the ET_EXEC kind do not contain enough relocation information. Nevertheless, PaX provides two ways to solve this problem: RANDEXEC and RANDMMAP.

RANDEXEC works by mapping the ET_EXEC ELF file in a special way in memory and requires no changes in userland (except for actually enabling it on a given file, as this feature is disabled by default). The drawback of this approach is that it is slow (the kernel compilation benchmark sees a 3 times slow down for example) and prone to false positive detections of so-called return-to-libc style attacks (which renders it unusable on such executables).

Therefore this method mainly exists to prove a point and is not intended for production use.

RANDMMAP on the other hand works on ELF files of the ET_DYN kind which is normally used for dynamically linkable libraries. This approach has none of the drawbacks that plague RANDEXEC because such ET_DYN ELF files have enough relocation information and the dynamic linker has no problem with relocating them (and there is no performance penalty at runtime), nor is there a chance for false positive attack detections as none is done in the first place. This means that protecting against the return-to-libc style attack (in case the information about the randomization can leak to the attacker) requires other approaches, which is not discussed here.

It should be clear by now that the preferable way of randomizing the main executable's base address is via RANDMMAP and not RANDEXEC. This in turn means that we need a way to produce ET_DYN ELF executables instead of the ET_EXEC kind. The following parts describe the process in detail and hopefully provide enough information so that modifying existing packages to produce ET_DYN ELF targets will not be a problem. Software authors and/or package maintainers are encouraged to provide such make targets themselves in the future.

How to produce ET_DYN ELF executables

The following discussion assumes that the GNU toolchain (such as gcc and ld) is used to produce the target file, other languages and tools should follow the same principles however. The process has two main steps, first compilation then linking, both of which need to be modified for producing an ET_DYN ELF executable.

Compilation has to be modified in order to produce position independent code (PIC) which in turn allows the linker to not emit so-called text relocations in the final ET_DYN ELF file. Although this step is not strictly required (it does not affect the relocatability of the result), it is useful as this allows for another security related hardening: PaX normally makes it impossible to make an executable file mapping writable, however for text relocations it has to make an exception. If there are no such ET_DYN ELF files on a system, this exception can be removed and then the only way to introduce new executable code into a task's address space will be via file creation and mapping (which can be prevented by other methods such as ACL systems). Producing PIC is very easy, one just has to add the -fPIC switch to the compiler command line. This will not get rid of all text relocations however as there are other sources of (position dependent) code contributing to the final ET_DYN ELF file that will lead us to the next step.

Linking the main executable is governed by a special script called the 'specs' file ('gcc -v' tells us what is used by default). Studying it in detail is beyond our scope, but let's note the fact that there are more object files linked into the result than one has specified on the linker command line. These extra objects are necessary for implementing such features as calling constructors/destructors or the low-level entry point of the code (the main() C function is not the actual entry point of an ELF executable).

Linking an ET_DYN ELF file is initiated by specifying the -shared switch on the gcc command line which in turn will affect what extra object files go into the result. Since our actual goal is to produce the main executable (vs. a shared library), we have to make sure that we link in all extra objects normally expected by an ET_EXEC target and not necessarily those specified by the specs file for libraries. Luckily there is only one extra object we have to take care of: crt1.o (we will ignore profiling and not care about gcrt1.o). It is no coincidence that crt1.o is not linked into shared libraries as this object contains (among others) the low-level entry point and startup code that invokes the C library startup code which in turn calls main().

Initiating the building of ET_DYN executables on Gentoo does not require us to put -shared in our CFLAGS or LDFLAGS

Making crt1.o position independent is easy, we just have to make use of the GOT (in keeping with the tradition of the glibc naming convention for the position independent version of the extra object files, we will call it crt1S.o). There is one last issue to take care of: a dynamically linked executable requires a special program header that specifies the dynamic linker to be mapped into memory by the kernel on task creation. As we can see it from the specs file, having the -shared switch on the linker command line will omit the dynamic linker specification and would produce an unusable ET_DYN ELF file. The solution is simple, we follow the approach of glibc which is also an executable ET_DYN ELF file: the dynamic linker is specified in an object file that contains the full path to the dynamic linker in a specific ELF section that ld will recognize and convert into the corresponding program header.

The above method is demonstrated on a simple 'hello world' program that is included with this document. The source code of the main executable is in a.c, our PIC crt1 is in crt1S.S (it has to be written in assembly, the code is directly derived from glibc 2.2) and finally interp.c defines the dynamic linker (technically it could be put into crt1S.S as well to reduce the number of extra files to a minimum). The makefile is very simple as well, it compiles each source file into an object file and then links them together. One important thing to note is the order of the object files on the linker command line: crt1S.o must come first (that is, before any object file of the application) and interp.o should follow it directly as this will result in the interpreter program header getting emitted before the PT_LOAD headers (which is the normal program header ordering in ET_EXEC files, although it is not strictly necessary). Since crt1S.o and interp.o are constant (they do not depend on the application code) they can be compiled once and put into the same directory where the other systemwide crt* files are.

ET_DYN ELF executables (The Gentoo Way)

On Gentoo this is accomplished by merging hardened-gcc:

# emerge hardened-gcc

hardened-gcc is an umbrella package for non-mainstream gcc modifications The hardened-gcc packages was initially created by Alexander Gabert for this special purpose we are serving here: rolling out the etdyn specs file and interp.o together with the position independent crt1S.o. But this package is not limited to that purpose. It was designed to be the be used for any future changes to gentoo-hardened systems regarding the improvement of gcc compiling binaries that are more secure than the default product coming out when the vanilla gcc is used. And it can be used for ebuild packages to "trigger" some alternative action once they "realize" that they are getting built on a system equipped with a modified gcc for enforcing gentoo hardened protection measures. Straight this means that when a package is found to be breaking when used with the hardened-gcc changes, the particular ebuild of that failing package can and will be modified by our gentoo-hardened developers to put some "check" logic into it when the hardened-gcc is found on the target system.

As an example lets try the rebuilding our chpax binary as an ET_DYN shared executable. We can use the file(1) command to determine if we in fact we are building our executables as ET_EXEC or ET_DYN.

The first example here we have chpax built as an ET_DYN and the second one is chpax built as an ET_EXEC.

# file /sbin/chpax
/sbin/chpax: ELF 32-bit LSB shared object, Intel 80386, version 1 \
(GNU/Linux), stripped
/sbin/chpax: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for \
GNU/Linux 2.0.0, dynamically linked (uses shared libs), stripped
Credits
Works Cited
  • PaX Homepage
  • PaX Documentation
  • Collective Work. PaX - Gentoo Wiki.