Brute-Forcing ASLR on Windows

By Gal Badishi | September 19, 2012

So you want to write an exploit that works on Windows machines supporting ASLR and DEP. Good for you. You found a way to exploit some vulnerability, you’ve just constructed the most amazing ROP chain using just the executable, and all that’s left for the exploit to work is to bypass ASLR. Alas, the executable was compiled with /DYNAMICBASE, searching for a non-ASLR module leaves you empty-handed, and a memory-leak vulnerability is nowhere to be found. Could it be that your plans for world domination will have to wait (once again)?

Cheer up – all may not be lost. In certain cases it might be possible to force ASLR into submission by brute-forcing the first address in the ROP chain. Of course, if you guess incorrectly, you can expect the thread/process to crash, so this method might not suit every application out there. If the application crashes and doesn’t go back up, it can’t be brute-forced, since you need to be able to repeatedly run the exploit against the vulnerable app.

A study by Alexander Sotirov and Mark Dowd shows the algorithm used to determine the random location of the executable’s base address. It also tells us that there are slightly less than 256 different base addresses to choose from, where each address is aligned on a 64K boundary.

We observe two cases suitable for brute-forcing:

  1. The application is relaunched for each failed exploit attempt. This entails calculating a new base address for the executable. Assuming all such addresses are selected randomly with uniform distribution out of the entire relevant address pool, our exploit needs not change its guess on different invocations. The executable’s base address randomly changes each time we run the exploit, so we can expect the exploit to work after about 256 tries on average.
  2. The application doesn’t crash (but perhaps a thread created specifically for us crashes). This means that the executable’s base address remains the same across different invocations of the exploit. Thus, we can simply exhaust the list of all available addresses deterministically, guaranteeing success within at most 256 tries.

Let’s concoct a contrived example to illustrate case #2. It’s important to note that, although this example is made up, a real exploit used the same techniques, due to the same initial conditions. See Alexander Sotirov’s slides on the ANI exploit for more details.

Consider the following program:

 

 

Running the program several times on a stock Windows 7 SP1 shows us Windows’ ASLR in action:

 

 

We can see that the address of the function f() changes each time we run the application. More accurately, the executable is loaded to a random address, and f() is located at offset 0×1000 from that address.

Let’s add a “vulnerable” function and “exploit” it (make sure you disable optimizations if your compiler unwittingly optimizes this function away):

 

 

Our vuln() function simply changes its return address to a user-supplied value. In our case, we ask it to go to f() instead of going back to main() and printing the second string. Let’s check it:

 

 

What happens if we don’t guess f()’s address correctly?

 

 

But what if the application doesn’t crash upon failure? Consider the following code:

 

 

The function g() calls our vulnerable function, but catches exceptions that might get generated by vuln(). Now the application doesn’t crash when we try to jump to an invalid address, and we can brute-force the address to jump to, as shown in main(). This gives us guaranteed success, with an upper bound on the number of exploitation attempts. Here’s an exemplar end result: