Saturday, December 12, 2009

Beating Linux ASLR for Local Exploits

Stack address randomization definitely helps to prevent exploitation of stack based exploits, since it becomes almost impossible to correctly guess the address to return to for executing your code, and brute force guessing this can be time consuming and/or alert a NIDS.

I did, however, notice something interesting about it today while analyzing some of GCC's optimization techniques.

I've never analyzed ASLR itself. I've just noticed the stack base address being random on every execution of a program. This can be turned off by running your command with "setarch" or using the "personality" function. This is only really useful for debugging purposes as far as I can see. The point I'm trying to make is that this is in now way an in depth analysis of ASLR and that I could be wrong in some of my assumptions.

I'm not sure if ASLR reseeds it's generator. If it does it seems like a globally shared seed is used. At least in a process tree with some chosen parent or something. I'm basing these guesses on the fact that spawning a new process using the execv() system call, very quickly after the parent process started, the stack bases will be the same.

Take this example:
#include <stdio.h>

int main(int argc, char **argv)
{
int i;
char *arg[] = {argv[0], argv[0], 0};
if (argc < 2)
{
printf("parent addr i: %p\n", &i);
//sleep(1);
execv(argv[0], arg);
}
else
{
printf("child addr i: %p\n", &i);
}
}

With this, the address of "i" is printed, then a child process is spawned which will also print the address of the same variable in it's stack.

Running this you'll notice that the addresses are very close or match most of the time. I figure if it spawns quickly enough after the first process, the seed for the new process is the same and thus they'll generate the same random number for the stack base. Try uncommenting the "sleep(1)" call and running it again. The stack doesn't match anymore (if it does it's a very rare/lucky case).

I also noticed that even when the bases don't match, they are sometimes very close to each other, usually exactly 0x10 bytes. This might have been a coincidence, but in these cases the child process was always 0x10 bytes lower than the parent process. (UPDATE: The 0x10 bytes was unique to this case. Further I've found that the generator used for ASLR uses the PID of the process (added to the jiffies) to generate the random number, which explains why they end up "close")

These 2 facts can be used in local exploits. If you can quickly enough setup the exploit and spawn the vulnerable program, you should be able to reliably guess the return address using the information from the parent process, and with a NOP slide of at least 0x10 bytes, both facts can be accounted for.

Proof of Concept

I created a proof of concept, which consists of a program with a buffer overflow vulnerability, and an exploit that makes use of this execv() weakness to circumvent ASLR protection. Download Proof of Concept.

Read the README file, and run demo.sh. Further, an example of what you might expect is in the SAMPLE file, included in the tarball. When extracted all files will be found in the "aslr-poc" directory.

No comments: