-
Notifications
You must be signed in to change notification settings - Fork 14.5k
Initial support for Malleable C2 Profiles in HTTP Meterpreter #20419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 6.5
Are you sure you want to change the base?
Changes from 1 commit
5025992
3ccd8e5
fe7705d
f2d3120
2d7f8b4
300d16e
71d943d
42b027d
d589da9
c571e7d
5def53e
76954a6
fa5881e
bbdf45a
6496e7f
f82fe8e
1abbb70
f93d308
ba5e097
2c4eaff
8c4f7fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,20 +3,26 @@ | |
## | ||
# This module contains helper functions for parsing and loading malleable | ||
# C2 profiles into ruby objects. | ||
OJ marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# | ||
# See https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics/malleable-c2_main.htm | ||
## | ||
|
||
require 'strscan' | ||
require 'rex/post/meterpreter/packet' | ||
|
||
# Handle escape sequences in the strings provided by the c2 profile | ||
class String | ||
def from_c2_string_value | ||
module Msf::Payload::MalleableC2 | ||
|
||
MET = Rex::Post::Meterpreter | ||
MC2 = Msf::Payload::MalleableC2 | ||
|
||
# Handle escape sequences in the strings provided by the c2 profile | ||
def self.from_c2_string_value(s) | ||
# Support substitution of a subset of escape characters: | ||
# \r, \t, \n, \\, \x.. | ||
# Not supporting \u at this point. | ||
# We do in a single regex and parse each as we go, as this avoids the | ||
# potential for double-encoding. | ||
self.gsub(/\\(x(..)|r|n|t|\\)/) {|b| | ||
s.gsub(/\\(x(..)|r|n|t|\\)/) {|b| | ||
case b[1] | ||
when 'x' | ||
[b[2, 4].to_i(16)].pack('C') | ||
|
@@ -31,11 +37,6 @@ def from_c2_string_value | |
end | ||
} | ||
end | ||
end | ||
|
||
module Msf::Payload::MalleableC2 | ||
|
||
MET = Rex::Post::Meterpreter | ||
|
||
class Token | ||
attr_reader :type, :value | ||
|
@@ -95,13 +96,15 @@ class Lexer | |
|
||
def initialize(file) | ||
@tokens = [] | ||
tokenize(File.read(file)) | ||
tokenize(File.binread(file)) | ||
end | ||
|
||
def is_block_keyword?(word) | ||
BLOCK_KEYWORDS.include?(word) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not a blocker: Introducing constants for the regex union like |
||
end | ||
|
||
private | ||
|
||
def tokenize(text) | ||
OJ marked this conversation as resolved.
Show resolved
Hide resolved
|
||
scanner = StringScanner.new(text) | ||
|
||
|
@@ -113,7 +116,6 @@ def tokenize(text) | |
# comment | ||
next | ||
elsif scanner.scan(/\"(\\.|[^"])*\"/) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm.. I guess so? though I can't see where we would want to specify that somewhere and it still have meaning. |
||
#@tokens << Token.new(:string, scanner.matched[1..-2]) | ||
@tokens << Token.new(:string, scanner.matched[1..-2]) | ||
elsif scanner.scan(/[a-zA-Z0-9_\-\.\/]+/) | ||
OJ marked this conversation as resolved.
Show resolved
Hide resolved
|
||
word = scanner.matched | ||
|
@@ -122,7 +124,10 @@ def tokenize(text) | |
elsif scanner.scan(/[{};]/) | ||
@tokens << Token.new(:symbol, scanner.matched) | ||
else | ||
raise "Unexpected token near: #{scanner.peek(20)}" | ||
preceding_lines = scanner.string[0..scanner.pos].split("\n") | ||
row = preceding_lines.length | ||
col = preceding_lines.last&.size || 1 | ||
raise "Unexpected token near #{row}:#{col}: #{scanner.peek(20).split("\n").first}" | ||
end | ||
end | ||
end | ||
|
@@ -243,7 +248,7 @@ def to_tlv | |
|
||
post_tlv.add_tlv(MET::TLV_TYPE_C2_ENC, enc_flags) if enc_flags != 0 | ||
|
||
prepend_data = client_output.get_directive('prepend').map{|d|d.args[0]}.("") | ||
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? | ||
append_data = client_output.get_directive('append').map{|d|d.args[0]}.join("") | ||
post_tlv.add_tlv(MET::TLV_TYPE_C2_SUFFIX, append_data) unless append_data.empty? | ||
|
@@ -292,7 +297,7 @@ class ParsedSet | |
attr_accessor :key, :value | ||
def initialize(key, value) | ||
@key = key.downcase | ||
@value = value.from_c2_string_value | ||
@value = MC2.from_c2_string_value(value) | ||
end | ||
end | ||
|
||
|
@@ -340,7 +345,7 @@ class ParsedDirective | |
attr_accessor :type, :args | ||
def initialize(type, args) | ||
@type = type.downcase | ||
@args = args.map {|a| a.from_c2_string_value} | ||
@args = args.map {|a| MC2.from_c2_string_value(a)} | ||
end | ||
end | ||
|
||
|
@@ -362,7 +367,7 @@ def parse(file) | |
elsif current_token.type == :keyword && @lexer.is_block_keyword?(current_token.value) | ||
profile.sections << parse_section | ||
else | ||
raise "Unexpected token at tope level: #{current_token.type}=#{current_token.value}" | ||
raise "Unexpected token at top level: #{current_token.type}=#{current_token.value}" | ||
end | ||
end | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.