Files
metasploit-gs/lib/rex/post/meterpreter/extensions/bofloader/bofloader.rb
T
2023-07-19 11:39:00 +01:00

171 lines
5.6 KiB
Ruby

# -*- coding: binary -*-
require 'rex/post/meterpreter/extensions/bofloader/tlv'
require 'rex/post/meterpreter/extensions/bofloader/command_ids'
require 'rexml/document'
require 'set'
module Rex
module Post
module Meterpreter
module Extensions
module Bofloader
###
#
# Bofloader extension - Executes a beacon object file in
# the current meterpreter session.
#
# Kevin Haubris (@kev169)
# Kevin Clark (@GuhnooPlusLinux)
# TrustedSec (@TrustedSec)
#
###
class BofPackingError < RuntimeError
end
# Code referenced from: https://github.com/trustedsec/COFFLoader/blob/main/beacon_generate.py
# Emulates the native Cobalt Strike bof_pack() function.
# Documented here: https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#bof_pack
#
# Type Description Unpack With (C)
# --------|---------------------------------------|------------------------------
# b | binary data | BeaconDataExtract
# i | 4-byte integer | BeaconDataInt
# s | 2-byte short integer | BeaconDataShort
# z | zero-terminated+encoded string | BeaconDataExtract
# Z | zero-terminated wide-char string | (wchar_t *)BeaconDataExtract
class BofPack
def initialize
reset
end
def add_binary(binary)
# Add binary data to the buffer
binary = binary.bytes if binary.is_a? String
b_length = binary.length
binary = [b_length] + binary
buf = binary.pack("I<c#{b_length}")
@size += buf.length
@buffer << buf
end
def add_int(dint)
@buffer << [dint.to_i].pack('I<')
@size += 4
end
def add_short(short)
@buffer << [short.to_i].pack('s<')
@size += 2
end
def add_str(str)
str = str.encode('utf-8').bytes
str << 0x00 # Null terminated strings...
s_length = str.length
str = [s_length] + str
buf = str.pack("I<c#{s_length}")
@size += buf.length
@buffer << buf
end
def add_wstr(wstr)
wstr = wstr.encode('utf-16le').bytes
wstr << 0x00 << 0x00 # Null terminated wide string
s_length = wstr.length
wstr = [s_length] + wstr
buf = wstr.pack("I<c#{s_length}")
@size += buf.length
@buffer << buf
end
def finalize_buffer
output = [@size].pack('I<') + @buffer
reset
output
end
def reset
@buffer = ''
@size = 0
end
def bof_pack(fstring, args)
# Wrapper function to pack an entire bof command line into a buffer
if fstring.nil? || args.nil?
return finalize_buffer
end
if fstring.length != args.length
raise BofPackingError, 'Mismatched format and argument lengths'
end
fstring.chars.zip(args).each do |c, arg|
case c
when 'b'
add_binary(arg)
when 'i'
add_int(arg)
when 's'
add_short(arg)
when 'z'
add_str(arg)
when 'Z'
add_wstr(arg)
else
raise BofPackingError, "Invalid character in format string: #{c}. Must be one of \"b, i, s, z, Z\""
end
end
# return the packed bof_string
finalize_buffer
end
end
# Beacon object file (BOF) loader
class Bofloader < Extension
def self.extension_id
EXTENSION_ID_BOFLOADER
end
# Typical extension initialization routine.
#
# @param client (see Extension#initialize)
def initialize(client)
super(client, 'bofloader')
client.register_extension_aliases(
[
{
'name' => 'bofloader',
'ext' => self
},
]
)
end
def execute(bof_data, args_format: nil, args: nil, entry: 'go')
request = Packet.create_request(COMMAND_ID_BOFLOADER_EXECUTE)
# Pack up beacon object file data and arguments into one single binary blob
# Hardcode the entrypoint to "go" (CobaltStrike approved)
bof = BofPack.new
packed_args = bof.bof_pack(args_format, args)
# Send the meterpreter TLV packet and get the output back
request.add_tlv(TLV_TYPE_BOFLOADER_EXECUTE_BUFFER, bof_data)
request.add_tlv(TLV_TYPE_BOFLOADER_EXECUTE_BUFFER_ENTRY, entry)
request.add_tlv(TLV_TYPE_BOFLOADER_EXECUTE_ARGUMENTS, packed_args)
response = client.send_request(request)
return response.get_tlv_value(TLV_TYPE_BOFLOADER_EXECUTE_RESULT)
end
end
end
end
end
end
end