Problem solve Get help with specific problems with your technologies, process and projects.

Linux virtual address randomization and impacting buffer overflows

PaX team developers Ed Tittel and Justin Korelc explain how to stop buffer flow exploitation leaks and bolster security with Linux virtual address randomization.

Buffer overflows have been a bane for nearly every operating system and for all programming languages not explicitly designated as "type-safe" (such as C). Everything from desktops to workstations, servers and embedded hardware can (and often do) contain potentially exploitable conditions because buffer overflows are allowed to occur, sometimes with far-reaching effects.

When exploiting a buffer overflow, an attacker's primary objective is to direct the target machine to execute arbitrary code, usually a sequence of position-independent object code known as a payload.

In a nutshell, buffer overflow exploitation requires two parts: an exploitable condition known as the injection vector and executable object code known as the payload. The injection vector is quite often a buffer whose bounds are not checked, or an operation that is somehow coerced into deliberately overstepping the bounds of its given buffer. Once the buffer limits have been exceeded, a carefully-crafted overflow attack delicately attempts to redirect the target system into acting on attacker-supplied data and instructions. This data must normally be in a predictable place, accessible through some absolute address.

Although address-space randomization is not a new technique in the Unix world (as in PaX [1] and OpenBSD), it's a newcomer in Linux kernel functionality, starting with the 2.6.x releases. Ostensibly, virtual address space randomization attempts to mitigate widespread feasibility of a given exploit vector by making the very properties the exploit relies on non-deterministic. For example, stack-based exploits rely heavily on known, hard-coded stack addresses to trick the processor into executing attacker-supplied object code.

Within heap-based overflows, function calls, like mmap(), return unpredictable addresses so that this type of exploitation is effectively crippled.

[email protected]]$ /bin/cat /proc/1676/maps
08048000-08049000 r-xp 00000000 03:01 484716 /home/jkor/env
08049000-0804a000 rw-p 00000000 03:01 484716 /home/jkor/env
b7e02000-b7f0b000 r-xp 00000000 03:01 229995 /lib/
b7f0b000-b7f0c000 ---p 00109000 03:01 229995 /lib/
b7f0c000-b7f0d000 r--p 00109000 03:01 229995 /lib/
b7f0d000-b7f10000 rw-p 0010a000 03:01 229995 /lib/
b7f10000-b7f13000 rw-p b7f10000 00:00 0
b7f1f000-b7f34000 r-xp 00000000 03:01 230174 /lib/
b7f34000-b7f35000 r--p 00014000 03:01 230174 /lib/
b7f35000-b7f36000 rw-p 00015000 03:01 230174 /lib/
bfd1f000-bfd34000 rw-p bfd1f000 00:00 0 [stack]
ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]

[[email protected]]$ /bin/cat /proc/1641/maps
08048000-08049000 r-xp 00000000 03:01 484716 /home/jkor/env
08049000-0804a000 rw-p 00000000 03:01 484716 /home/jkor/env
b7df6000-b7eff000 r-xp 00000000 03:01 229995 /lib/
b7eff000-b7f00000 ---p 00109000 03:01 229995 /lib/
b7f00000-b7f01000 r--p 00109000 03:01 229995 /lib/
b7f01000-b7f04000 rw-p 0010a000 03:01 229995 /lib/
b7f04000-b7f07000 rw-p b7f04000 00:00 0
b7f13000-b7f28000 r-xp 00000000 03:01 230174 /lib/
b7f28000-b7f29000 r--p 00014000 03:01 230174 /lib/
b7f29000-b7f2a000 rw-p 00015000 03:01 230174 /lib/
bfc0e000-bfc28000 rw-p bfc0e000 00:00 0 [stack]
ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]

Bold text illustrates the randomization of stack-based addresses, complicating straight-forward exploitation of such bugs.

While the randomization of core system component locations limits garden-variety address exploitations, it still isn't foolproof. Given the limitations of a 32-bit system, finding a usable virtual address space that meets the constraint of being properly aligned can be a chore. Currently, usable address spaces of this type are quite limited (they will increase in number over time) so that some exploitation methods will continue to work.

Several documented workarounds to successful exploitation under systems with stack randomization include: walking the stack for a previously-stored pointer as a potential return address for the payload, clobbering the EAX (return value for a stack-based function) register and causing misrepresentation of hard-coded values (integer values as hexadecimal string of microprocessor code)[2]. Until these workarounds are themselves no longer feasible, buffer overflows that use address-based attack methods will probably remain widespread and chronic.

About the authors: Ed Tittel is a full-time freelance writer and trainer based in Austin, TX, who specializes in markup languages, information security, and IT certifications. Justin Korelc is a long-time Linux hacker who works with Ed and concentrates on hardware and software security topics. Together, the two have recently authored a book on Home Theater PCs and the Tom's Hardware 2005 Holiday Buyer's Guide.

For more information:

Visit the PaX Team home page

Read Smack the Stack: Advanced Buffer Overflow Methods.

Dig Deeper on Linux servers