I still remember 5 years ago when I decided to do my first PowerShell class at Derbycon and some of my buddies told me I was nuts for teaching what they called a "Toy Language" I have used Windows PowerShell almost daily for work since 2007, started with my previous job setting up and securing Exchange 2007 servers, once PowerCLI from VMware come out it became my go to environment for automating and hardening ESX and ESXi environments. Once we figured how to run encoded commands it became a must for post-exploitation since it gave me access to ADSI, COM, Win32 API, .NET API and all sorts of third party .NET library I could get my hands on. Some kind of PowerShell ability has been present in most major comercial products one way or another and now Metasploit is taking it a step further thanks to the great work of OJ Reeves also known as @TheColonial by adding a Metrerpeter extension for unmanaged Windows PowerShell Runspace. This extension is based on the work from Lee Christensen and his UnmanagedPowerShell project.
Loading the Extension and using Help
Just like with any other Metrepreter extension we start by loading it using the load command.
Once the extension is loaded successfully we look at the new commands that where added by running the help command. This will show that we have 3 new commands available for us in the interface.
The first command is the powershell_execute, this command executes a given string inside a the unmanaged runspace in memory and returns the string output of it. If we look at the help message of it we see there is an additional optional option for the command, the -s option allows you to specify a ID or Name to a separate pipeline inside the Runspace, this allows you to keep variable separate from each other in case you want to run one pice of code separate from the other.
Next command we look at the help information is powershell_import this command basically dot sources a PS1 file in to the current pipeline (session) or loads a .NET assembly. Things we need to keep in mind:
- Script files must end with the .ps1 extension.
- .NET assemblies have to be compiled for .Net Framework 3.5, the architecture of the Meterpreter session you are running under and they must end in .dll.
- Any assembly you load will affect all pipelines in the runspace.
The next command powershell_shell allows us to have an interactive shell with the current pipeline or any other pipeline we create or have created including any scripts or assemblies we have imported in to it.
This interactive shell is not a conhost shell so do not expect tab completion or keyboard shortcuts to work. Remember inside of Windows when you use powershell.exe it is in reality a conhost wrapper over the System.Management.Automation library and it is what handles all of the keyboard interactions.
Basic Extension Usage
We start by executing a command using the powershell_execute command. You can run the command by simply proving a string with the command or series of commands you want to execute and optional the ID or Name we want to give the session. Here is the execution of commands inside the default pipeline:
Here we are adding session names to show how the variables differ from one to the other:
The interactive shell is just like any other shell we have worked inside of Metrepeter or any other payload where we can write any command and get is output but backspace, tab, arrow keys and others do not work like in a proper console application:
Lets look at the import of a script. Here is a simple script we will import with the powershell_import command.
When we import this script the function we created is now available for us to execute and use in our pipeline:
Lets look now at loading a .NET assembly and using it. We will load JHSoftware DNS Client assembly in to the runspace:
Once loaded we build a script that uses the assembly to do a DNS query:
Since the code is outside of a function the code will execute just like any other PowerShell script and we will see the results of our calls to the APIs exposed by the assembly:
Meterpreter PowerShell Bindings
In addition to the commands and the APIs added for them OJ also exposed Meterpreter functionality as .NET namespaces with methods so as to aid with the automation and functionality of the environment, they can be found in the GitHub repo. The bindings exposed are:
- Incognito - allows for creations of users, adding users to groups and basic token manipulation.
- Kiwi - Executes a scrape of the passwords in memory using mimikatz.
- User - check if system, get user name under which the process in running and get the user SID.
- Sys - Allows for listing of processes and getting system information
- Transport - Add and list new transports for the current session.
Each of the bindings can be access from inside the runspace created by Meterpreter under the namespace of MSF.Powershell.Meterpreter. Each one of the bindings depend that the extension for which they provide access to their commands be loaded first. We can instantiate each inside a interactive PowerShell session and look at their static methods using the Get-Member cmdlet and looking at only the static methods exposed:
Indicators of Compromise
Just like with most post-epxploitation tools there will be indicators of compromise you can use to detect and track the use if the techniques. The following should prove useful for the pentester/red-team to know their footprint and what to inform as IOC in their report for actions taken and for the IR/-blue-team to look for in their hunting sessions.
The initial version of the extension is creating an unmanaged runspace with version 2.0 of the engine, When we look in to the PowerShell eventlog in all versions of PowerShell and Windows we will see a Event with an ID of 400 where the HostName for the engine will be MSFConsole.
When we look further at the process we will notice that the PowerShell .NET modules and System.Automation libraries are loaded for the module.
It is very easy to find processes that have the PowerShell assemblies loaded with PowerShell itself or with Sysinternal Tools. One note to take in to consideration is to use both x86 and x64 instances of PowerShell when looking so as to not encounter a false negative so to SysWOW64.
In the following example our session is running under the pageant.exe process and it migrates in to the OneDrice.exe proces.
This action is easily caught by Sysinternal Sysmon with the CreateRemotheThread rule enabled as it can be seen bellow.
When we search again for the presence of the System.Automation assemblies on the running processes we can see the previous and new process under which meterpreter is running with the extension loaded.
To get more information so as to build a timetable of actions the use of Sysmon with a Image Load rule shows the time and process that the assembly in loaded and can be used to track as the attacker moved from one process to the other. In this case we can see he started with notepad and then migrated and closed the notepad.exe process, this would have been missed by just looking at running processes but Sysmon logged the action.
On more modern versions of Windows (2012 R2, 8.1 and 10) running Windows PowerShell 4.0 and above we can enable logging that would catch loading of scripts and keep a detail list of actions, even passing code before execution to the Windows Defender process to examine catching things like Invoke-Mimikatz among others. But since this extension instantiates the engine 2.0 of the engine this mitigations have no effect. Now one thing to remember is that Engine 2.0 is optional feature not installed by default on modern versions of Windows so this would cause the extension to fail when it attempts to take an action. It will still load with no error. Bello an example of w Windows 10 system without the feature enabled as it is a default install:
No IOCs in the event log even with Sysmon are created in this case for the engine initialization failure.
I wrote a post module for the enumeration of logging settings, general PowerShell environment configuration and if the 2.0 engine is enabled or not. The module is called enum_ps_env when ran it will let you know if the featured is installed or not on the modern version of the OS that did not come with PowerShell v2.0 as default:
The function to detect it is a simple one checking the registry for the information and it saves a note if enabled or not for those hosts running the right version of Windows.
This blog post only skimmed the surface of what can be done and how to use the extension, hopefully in the near future I will write more on it. As with anything Metasploit all of this is subject to change as OJ works on improving the extension. If interested in creating your own Sysmon custom rules I invite you to play with the PowerShell module I wrote for generating them call Posh-Sysmon. Hope you found the blog post useful and more to come in the near future.