Skip to content

6.5.0 prerelease build#21641

Draft
adfoster-r7 wants to merge 67 commits into
rapid7:masterfrom
adfoster-r7:6.5.0-prerelease-build
Draft

6.5.0 prerelease build#21641
adfoster-r7 wants to merge 67 commits into
rapid7:masterfrom
adfoster-r7:6.5.0-prerelease-build

Conversation

@adfoster-r7

Copy link
Copy Markdown
Contributor

Ignore; Just testing CI

OJ and others added 30 commits July 1, 2026 13:21
Munged a few commits into this one. But we have basic support for
TLV-based configuration blocks instead of hard-coded block sizes.
Initial support for the MC2 stuff is in as well, but more to come.
Still don't have all the fields implemented, but this at least supports
the notion of having different URIs for GET and POST.

The approach taken, to reduce the impact on how much code has to be
changed, is to extract the UUID for the connection and use that as a
resource identifier. This UUID doesn't have any slashes in it, and hence
will not collide with any URI. This means we can use the UUID as a key
in the same hash as the resource URIs knowing that a direct lookup will
find the right session, even if by some miracle the UUID collides with a
chosen/generated URI. Any URI in the resource list will be prefixed with
a forward slash.

The listener will listen on all URIs that exist for the Meterp
configuration, including LURI setting, and the `uri` values in all three
areas that it might be specified in the C2 profile.
* Supporting "wrapping" and "unwrapping" of payloads based on the C2
  profile, which means that suffixes and prefixes are used based on what
  the configuration indicates.
* Made sure taht the debug_build flag is passed through on HTTP/S
  payloads.
* push details of the C2 profile into the meterp client so that required
  details can be easily accessed.
Stageless payloads start with an :init_connect which needs special
consideration given that it's just redirected. There's no client
instance at that point, so there's no C2 associated with it, so we have
to just manually wrap the outbound packet so that things work correctly.
Includes removal of the referrer and accept types specific TLV values,
because they can be treated like any other header, despite what the MSDN
documentation says about the HTTP APIs.

Moved packet wrapping to somewhere reusable.

Added support for binary-escaped strings in C2 profile values (eg.
"\x00").
Interim commit, contains code persists a C2 profile instance for reuse
rather than having many being parsed all the time. Also begins work
handling UUIDs outside of the URI.
The `Http::Request` class had an overload for the `body` accessor that
returned the query string parameters in the case that the body was
empty.

This is not only logically bizzarre, but functionally insane. The query
string is not part of the body. If you want the query string, go get it.

An interesting side effect of this craziness, along with the way the
body is constructed, is that if you send a POST request to the server
with a body AND a query string, MSF is kind enough to give you both
together. Crazy right? Well, this is because the class uses the `body`
accessor as an internal buffer, but that getter is overloaded.

So if the `body` is blank, and the `+=` operator is used (which, it is!)
then you end up with the query string being prepended to any actual body
content. Insane.

Also, from an API point of view, it looks just as crazy. Observe:

```
>> r = Rex::Proto::Http::Request::Post.new('/foo?lol=wtf')
=>
...
>> r.body = ''
=> ""
>> r.body
=> "lol=wtf"
```

No. This is a complete violation of logic. This commit removes this
"feature" and not only fixes the bugs that I was fighting against,
but restores some semblance of reason.
NOTE: This change does remove the trailing "/" from URIs registered..
which implies that things might not match. So more to do here.

Connection IDs are stored in the request now, so that they can be
referenced by clients if and when required.

IDs are pulled from various locations in the request.
The URI can't be blank, it needs to at least be /
Logic for finding connection UUIDs has been pushed into reverse_http so
that it's not part of the Http::Server any more. It's a little bit of a
leaky abstraction, but at least the logic is in the one place now.

Support added and tweaked for including the UUID in an HTTP header or in
a GET param.

Currently don't have support for it in the BODY as as param, not sure if
that's a requirement yet or not.

Same goes for cookies.
I hate this craziness, but I have no idea what I'll break if I don't
leave this in.
Still not fully accurate though since socks seems to be prefixed with
socks= and not socks://
The \x sequence only uses 2 hex digits, but the slice was taking 4 by
mistake. It should have been 2 instead.
Clearer checks against suffix/prefixes while also avoiding the edge-case
where suffix.length could be zero, resulting in raw_bytes[-0, length]
behaving unexpectedly.
OJ and others added 25 commits July 1, 2026 17:54
They aren't valid any more.
Stagless android was busted when meterp switched to TLV config. This
moves the flags to a TLV instead of the first byte of the config block.
Primary fix here is to make sure we put the UUID in the configuration in
the right format. It was being ignored by Windows, but not by python.
Properly supports stageless now.
PHP, java, python, and php. Adjusting windows to support.

Mettle yet to come.
Make the malleable C2 parser robust to real Cobalt Strike profiles and
honour space-separated URI lists.

Parser tolerance: detect blocks by lookahead (`name { ... }`) instead of
a fixed keyword whitelist, and allow identifier-named blocks/directives.
Unsupported blocks (dns-beacon, http-config, process-inject, post-ex,
code-signer, ...) are now parsed for structure and ignored by to_tlv
rather than aborting the whole profile. A stray top-level directive is
consumed instead of raising.

Multi-URI support: a `set uri` value may list several space-separated
candidate URIs (CS picks one at random per request). ParsedProfile#uris
now splits and flattens them so the handler registers a mount per
candidate, and add_uri emits a separate TLV_TYPE_C2_URI per candidate
(each with the query string) instead of one TLV holding the raw
space-joined string, which the client could not turn into a valid URL.
@adfoster-r7 adfoster-r7 force-pushed the 6.5.0-prerelease-build branch from e679974 to 6beb47c Compare July 3, 2026 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

5 participants