CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/post/meterpreter/extensions/bofloader/bofloader.rb
Views: 11791
1
# -*- coding: binary -*-
2
3
require 'rex/post/meterpreter/extensions/bofloader/tlv'
4
require 'rex/post/meterpreter/extensions/bofloader/command_ids'
5
require 'rexml/document'
6
require 'set'
7
8
module Rex
9
module Post
10
module Meterpreter
11
module Extensions
12
module Bofloader
13
###
14
#
15
# Bofloader extension - Executes a beacon object file in
16
# the current meterpreter session.
17
#
18
# Kevin Haubris (@kev169)
19
# Kevin Clark (@GuhnooPlusLinux)
20
# TrustedSec (@TrustedSec)
21
#
22
###
23
24
class BofPackingError < RuntimeError
25
end
26
27
# Code referenced from: https://github.com/trustedsec/COFFLoader/blob/main/beacon_generate.py
28
# Emulates the native Cobalt Strike bof_pack() function.
29
# Documented here: https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#bof_pack
30
#
31
# Type Description Unpack With (C)
32
# --------|---------------------------------------|------------------------------
33
# b | binary data | BeaconDataExtract
34
# i | 4-byte integer | BeaconDataInt
35
# s | 2-byte short integer | BeaconDataShort
36
# z | zero-terminated+encoded string | BeaconDataExtract
37
# Z | zero-terminated wide-char string | (wchar_t *)BeaconDataExtract
38
class BofPack
39
def initialize
40
reset
41
end
42
43
def add_binary(binary)
44
# Add binary data to the buffer
45
binary = binary.bytes if binary.is_a? String
46
b_length = binary.length
47
binary = [b_length] + binary
48
buf = binary.pack("I<c#{b_length}")
49
@size += buf.length
50
@buffer << buf
51
end
52
53
def add_int(dint)
54
@buffer << [dint.to_i].pack('I<')
55
@size += 4
56
end
57
58
def add_short(short)
59
@buffer << [short.to_i].pack('s<')
60
@size += 2
61
end
62
63
def add_str(str)
64
str = str.encode('utf-8').bytes
65
str << 0x00 # Null terminated strings...
66
s_length = str.length
67
str = [s_length] + str
68
buf = str.pack("I<c#{s_length}")
69
@size += buf.length
70
@buffer << buf
71
end
72
73
def add_wstr(wstr)
74
wstr = wstr.encode('utf-16le').bytes
75
wstr << 0x00 << 0x00 # Null terminated wide string
76
s_length = wstr.length
77
wstr = [s_length] + wstr
78
buf = wstr.pack("I<c#{s_length}")
79
@size += buf.length
80
@buffer << buf
81
end
82
83
def finalize_buffer
84
output = [@size].pack('I<') + @buffer
85
reset
86
output
87
end
88
89
def reset
90
@buffer = ''
91
@size = 0
92
end
93
94
def bof_pack(fstring, args)
95
# Wrapper function to pack an entire bof command line into a buffer
96
if fstring.nil? || args.nil?
97
return finalize_buffer
98
end
99
100
if fstring.length != args.length
101
raise BofPackingError, 'Mismatched format and argument lengths'
102
end
103
104
fstring.chars.zip(args).each do |c, arg|
105
case c
106
when 'b'
107
add_binary(arg)
108
when 'i'
109
add_int(arg)
110
when 's'
111
add_short(arg)
112
when 'z'
113
add_str(arg)
114
when 'Z'
115
add_wstr(arg)
116
else
117
raise BofPackingError, "Invalid character in format string: #{c}. Must be one of \"b, i, s, z, Z\""
118
end
119
end
120
121
# return the packed bof_string
122
finalize_buffer
123
end
124
end
125
126
# Beacon object file (BOF) loader
127
class Bofloader < Extension
128
129
def self.extension_id
130
EXTENSION_ID_BOFLOADER
131
end
132
133
# Typical extension initialization routine.
134
#
135
# @param client (see Extension#initialize)
136
def initialize(client)
137
super(client, 'bofloader')
138
139
client.register_extension_aliases(
140
[
141
{
142
'name' => 'bofloader',
143
'ext' => self
144
},
145
]
146
)
147
end
148
149
def execute(bof_data, args_format: nil, args: nil, entry: 'go')
150
request = Packet.create_request(COMMAND_ID_BOFLOADER_EXECUTE)
151
152
# Pack up beacon object file data and arguments into one single binary blob
153
# Hardcode the entrypoint to "go" (CobaltStrike approved)
154
bof = BofPack.new
155
packed_args = bof.bof_pack(args_format, args)
156
157
# Send the meterpreter TLV packet and get the output back
158
request.add_tlv(TLV_TYPE_BOFLOADER_EXECUTE_BUFFER, bof_data)
159
request.add_tlv(TLV_TYPE_BOFLOADER_EXECUTE_BUFFER_ENTRY, entry)
160
request.add_tlv(TLV_TYPE_BOFLOADER_EXECUTE_ARGUMENTS, packed_args)
161
response = client.send_request(request)
162
return response.get_tlv_value(TLV_TYPE_BOFLOADER_EXECUTE_RESULT)
163
end
164
165
end
166
end
167
end
168
end
169
end
170
end
171
172