Support encoding/decoding of data from C2 profile

This commit is contained in:
OJ Reeves
2025-07-30 15:02:08 +10:00
parent ba5e097b6f
commit 2c4eaff583
3 changed files with 59 additions and 29 deletions
+43 -15
View File
@@ -182,7 +182,19 @@ module Msf::Payload::MalleableC2
prefix = prepends.map {|p| p.args[0]}.join('')
appends = self.http_get&.server&.output&.append || []
suffix = appends.map {|p| p.args[0]}.join('')
prefix + raw_bytes + suffix
# do any encoding necessary
if raw_bytes.length > 0
if self.http_get&.server&.output&.has_directive('base64')
raw_bytes = Rex::Text.encode_base64(raw_bytes)
elsif self.http_get&.server&.output&.has_directive('base64url')
raw_bytes = Rex::Text.encode_base64url(raw_bytes)
end
end
result = prefix + raw_bytes + suffix
result
end
def unwrap_inbound_post(raw_bytes)
@@ -197,6 +209,15 @@ module Msf::Payload::MalleableC2
unless suffix.empty? || (raw_bytes[-suffix.length, raw_bytes.length] <=> suffix) != 0
raw_bytes = raw_bytes[0, raw_bytes.length - suffix.length]
end
# do any decoding necessary
if raw_bytes.length > 0
if self.http_post&.client&.output&.has_directive('base64')
raw_bytes = Rex::Text.decode_base64(raw_bytes)
elsif self.http_post&.client&.output&.has_directive('base64url')
raw_bytes = Rex::Text.decode_base64url(raw_bytes)
end
end
raw_bytes
end
@@ -213,15 +234,19 @@ module Msf::Payload::MalleableC2
self.add_http_tlv(get_uri, client, get_tlv)
prepends = self.http_get&.server&.output&.prepend || []
prefix = prepends.map {|p| p.args[0]}.join('')
get_tlv.add_tlv(MET::TLV_TYPE_C2_SKIP_COUNT, prefix.length) unless prefix.length == 0
prefix_len = prepends.map {|p| p.args[0].length}.sum
get_tlv.add_tlv(MET::TLV_TYPE_C2_PREFIX_SKIP, prefix_len) unless prefix_len == 0
appends = self.http_get&.server&.output&.append || []
suffix_len = appends.map {|s| s.args[0].length}.sum
get_tlv.add_tlv(MET::TLV_TYPE_C2_SUFFIX_SKIP, suffix_len) unless suffix_len == 0
client.get_section('metadata') {|meta|
enc_flags = 0
enc_flags |= MET::C2_ENCODING_FLAG_B64 if meta.has_directive('base64')
enc_flags |= MET::C2_ENCODING_FLAG_B64URL if meta.has_directive('base64url')
enc_flags = MET::C2_ENCODING_NONE
enc_flags = MET::C2_ENCODING_B64URL if meta.has_directive('base64url')
enc_flags = MET::C2_ENCODING_B64 if meta.has_directive('base64')
get_tlv.add_tlv(MET::TLV_TYPE_C2_ENC, enc_flags) if enc_flags != 0
get_tlv.add_tlv(MET::TLV_TYPE_C2_ENC, enc_flags) if enc_flags != MET::C2_ENCODING_NONE
get_tlv.add_tlv(MET::TLV_TYPE_C2_UUID_GET, meta.get_directive('parameter')[0].args[0]) if meta.has_directive('parameter')
get_tlv.add_tlv(MET::TLV_TYPE_C2_UUID_HEADER, meta.get_directive('header')[0].args[0]) if meta.has_directive('header')
# assume uri-append for POST otherwise.
@@ -237,16 +262,20 @@ module Msf::Payload::MalleableC2
http_post.get_section('client') {|client|
self.add_http_tlv(post_uri, client, post_tlv)
prepends = self.http_get&.server&.output&.prepend || []
prefix = prepends.map {|p| p.args[0]}.join('')
post_tlv.add_tlv(MET::TLV_TYPE_C2_SKIP_COUNT, prefix.length) unless prefix.length == 0
prepends = self.http_post&.server&.output&.prepend || []
prefix_len = prepends.map {|p| p.args[0].length}.sum
post_tlv.add_tlv(MET::TLV_TYPE_C2_PREFIX_SKIP, prefix_len) unless prefix_len == 0
appends = self.http_post&.server&.output&.append || []
suffix_len = appends.map {|s| s.args[0].length}.sum
post_tlv.add_tlv(MET::TLV_TYPE_C2_SUFFIX_SKIP, suffix_len) unless suffix_len == 0
client.get_section('output') {|client_output|
enc_flags = 0
enc_flags |= MET::C2_ENCODING_FLAG_B64 if client_output.has_directive('base64')
enc_flags |= MET::C2_ENCODING_FLAG_B64URL if client_output.has_directive('base64url')
enc_flags = MET::C2_ENCODING_NONE
enc_flags = MET::C2_ENCODING_B64URL if client_output.has_directive('base64url')
enc_flags = MET::C2_ENCODING_B64 if client_output.has_directive('base64')
post_tlv.add_tlv(MET::TLV_TYPE_C2_ENC, enc_flags) if enc_flags != 0
post_tlv.add_tlv(MET::TLV_TYPE_C2_ENC, enc_flags) if enc_flags != MET::C2_ENCODING_NONE
prepend_data = client_output.get_directive('prepend').map{|d|d.args[0]}.join("")
post_tlv.add_tlv(MET::TLV_TYPE_C2_PREFIX, prepend_data) unless prepend_data.empty?
@@ -258,7 +287,6 @@ module Msf::Payload::MalleableC2
post_tlv.add_tlv(MET::TLV_TYPE_C2_UUID_GET, client_id.get_directive('parameter')[0].args[0]) if client_id.has_directive('parameter')
post_tlv.add_tlv(MET::TLV_TYPE_C2_UUID_HEADER, client_id.get_directive('header')[0].args[0]) if client_id.has_directive('header')
# assume uri-append for POST otherwise given that we always put the TLV payload in the body?
# TODO: add support for adding a form rather than just a payload body?
}
}
+6 -6
View File
@@ -117,20 +117,20 @@ class Client
#
# Wrap the given packet data with any prefixes and suffixes that are stored in
# the associated C2 profile server configuration (if it exists)
# the associated C2 profile server configuration (if it exists) and handle
# encoding of data
#
def wrap_packet(raw_bytes)
raw_bytes = self.c2_profile.wrap_outbound_get(raw_bytes) if self.c2_profile
raw_bytes
self.c2_profile.wrap_outbound_get(raw_bytes) if self.c2_profile
end
#
# Unwrap the given packet data from any prefixes and suffixes that are stored in
# the associated C2 profile client configuration (if it exists)
# the associated C2 profile client configuration (if it exists) and handle
# decoding of data
#
def unwrap_packet(raw_bytes)
raw_bytes = self.c2_profile.unwrap_inbound_post(raw_bytes) if self.c2_profile
raw_bytes
self.c2_profile.unwrap_inbound_post(raw_bytes) if self.c2_profile
end
#
+10 -8
View File
@@ -132,18 +132,20 @@ TLV_TYPE_C2_CERT_HASH = TLV_META_TYPE_RAW | 717 # Expected SSL certi
TLV_TYPE_C2_PREFIX = TLV_META_TYPE_RAW | 718 # Data to prepend to the outgoing payload
TLV_TYPE_C2_SUFFIX = TLV_META_TYPE_RAW | 719 # Data to append to the outgoing payload
TLV_TYPE_C2_ENC = TLV_META_TYPE_UINT | 720 # Request encoding flags (Base64|URL|Base64url)
TLV_TYPE_C2_SKIP_COUNT = TLV_META_TYPE_UINT | 721 # Number of bytes of the incoming payload to ignore before parsing
TLV_TYPE_C2_UUID_COOKIE = TLV_META_TYPE_STRING | 722 # Name of the cookie to put the UUID in
TLV_TYPE_C2_UUID_GET = TLV_META_TYPE_STRING | 723 # Name of the GET parameter to put the UUID in
TLV_TYPE_C2_UUID_HEADER = TLV_META_TYPE_STRING | 724 # Name of the header to put the UUID in
TLV_TYPE_C2_UUID = TLV_META_TYPE_STRING | 725 # string representation of the UUID for C2s
TLV_TYPE_C2_PREFIX_SKIP = TLV_META_TYPE_UINT | 721 # Size of prefix to skip (in bytes)
TLV_TYPE_C2_SUFFIX_SKIP = TLV_META_TYPE_UINT | 722 # Size of suffix to skip (in bytes)
TLV_TYPE_C2_UUID_COOKIE = TLV_META_TYPE_STRING | 723 # Name of the cookie to put the UUID in
TLV_TYPE_C2_UUID_GET = TLV_META_TYPE_STRING | 724 # Name of the GET parameter to put the UUID in
TLV_TYPE_C2_UUID_HEADER = TLV_META_TYPE_STRING | 725 # Name of the header to put the UUID in
TLV_TYPE_C2_UUID = TLV_META_TYPE_STRING | 726 # string representation of the UUID for C2s
#
# C2 Encoding flags
#
C2_ENCODING_FLAG_B64 = (1 << 0) # straight Base64 encoding
C2_ENCODING_FLAG_B64URL = (1 << 1) # encoding Base64 with URL-safe values
C2_ENCODING_FLAG_URL = (1 << 2) # straight URL encoding
C2_ENCODING_NONE = 0 # No encoding at all
C2_ENCODING_B64 = 1 # Base64 encoding
C2_ENCODING_B64URL = 2 # Base64 encoding with URI-safe characters
C2_ENCODING_URL = 3 # URL encoding
#
# Core flags