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.
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:
- 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.
- 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.
- 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
Debug configurations that everyone is used to. It now also contains two new configurations called
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.
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.
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
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:
- Starting the webcam using
- Capturing a frame from the webcam using
- Stopping the webcam using
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
bind_tcp payloads now exit cleanly, but the
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.
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
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.
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:
- 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.
- 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:
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:
The reasons I decided to do this are:
- It provides a definitive semantic separation of intent between something that’s inline and something that is not.
- I wanted the prototype to be reflective of the difference in meaning to the caller.
- 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:
1 2 3 4 5 6 7 8 9 10 11 12 13
1 2 3 4 5
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
migrate, so those were both changed:
1 2 3 4 5 6 7 8 9
At this point Meterpreter shutdowns were more deterministic … at least for
If you’re interested in taking a look at the guts of the rework you can see it in this pull request.
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:
- A process is created which contains the shell, in this case
- Handles to
stdoutare acquired and stored alongside the channel.
- A thread is created and the work for that channel is tied to the thread.
- 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:
- Extracting references to channel handles (which in our case would be the read handle for
stdoutand the thread’s
sigtermhandle for when an external entity wanted to signal the closing of the channel).
- 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.
- 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
- Rinse and repeat until close.
- 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.
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.
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
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.
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
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:
(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.
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:
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
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:
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
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.
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Big ups to Egypt for the idea, for implementing the PHP version, and for landing the PR into master.
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.
1 2 3 4 5
1 2 3 4 5
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_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.
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.
extapi, do the same as you would with any other extension:
And with that, let’s take a look at the features.
1 2 3 4 5 6
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
-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:
1 2 3 4 5 6 7 8 9 10
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
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:
1 2 3 4 5
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?
Now, on to more serious stuff.
1 2 3 4 5 6 7
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:
1 2 3 4 5 6 7 8 9
No parameters are required to make this function tick. Here’s some sample 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 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
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:
1 2 3 4 5 6 7
The name of the service is required to drill deeper, and this is what it looks like when it’s run:
1 2 3 4 5 6 7 8 9 10
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!
This is my favourite and the main motivation for starting the
extapi extension in the first place.
1 2 3 4 5 6 7
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
But first, let’s look at the bit of magic that let’s us pull clipboard data from the victim.
1 2 3 4 5 6 7 8 9 10 11
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:
1 2 3 4 5 6
Pretty simple stuff. It does get more complicated when we want to deal with something like one or more files:
1 2 3 4 5 6 7 8 9 10 11 12
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:
1 2 3 4 5 6 7 8 9 10 11 12 13
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:
1 2 3 4 5 6 7 8 9 10 11 12
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?
1 2 3 4
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:
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.
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
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.
1 2 3 4 5 6 7
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 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.
1 2 3 4 5 6 7 8
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:
1 2 3 4 5 6 7 8 9 10 11 12 13
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
You can see that I’m searching for any objects in the
windows.old domain that have a name starting with
a and I’m asking for the fields
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:
1 2 3 4 5 6 7 8 9 10 11
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:
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
If you like this idea, then watch carefully because this should be landing in a Metasploit installation near you very soon.
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.
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.
mimikatzto v2.0 (already in progress!).
- Trying to improve Meterpreter’s stealth and protect it from being investigated.
- Finalising the streaming work so that
clipboard_monitorcan 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.
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.