2015-04-25 11:25:29 +00:00
|
|
|
# -*- coding: binary -*-
|
|
|
|
require 'msf/core/payload/uuid'
|
2015-04-26 03:41:31 +00:00
|
|
|
require 'msf/core/payload/windows'
|
2015-04-25 11:25:29 +00:00
|
|
|
require 'msf/core/reflective_dll_loader'
|
2016-08-26 19:13:38 +00:00
|
|
|
require 'rex/socket/x509_certificate'
|
2017-06-05 10:20:42 +00:00
|
|
|
require 'securerandom'
|
2015-04-25 11:25:29 +00:00
|
|
|
|
|
|
|
class Rex::Payloads::Meterpreter::Config
|
|
|
|
|
|
|
|
include Msf::ReflectiveDLLLoader
|
|
|
|
|
|
|
|
URL_SIZE = 512
|
2015-04-26 02:11:14 +00:00
|
|
|
UA_SIZE = 256
|
|
|
|
PROXY_HOST_SIZE = 128
|
|
|
|
PROXY_USER_SIZE = 64
|
|
|
|
PROXY_PASS_SIZE = 64
|
|
|
|
CERT_HASH_SIZE = 20
|
2015-04-25 11:25:29 +00:00
|
|
|
|
|
|
|
def initialize(opts={})
|
|
|
|
@opts = opts
|
2015-05-11 23:50:58 +00:00
|
|
|
if opts[:ascii_str] == true
|
2015-05-04 08:58:55 +00:00
|
|
|
@to_str = self.method(:to_ascii)
|
|
|
|
else
|
|
|
|
@to_str = self.method(:to_wchar_t)
|
|
|
|
end
|
2015-04-25 11:25:29 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def to_b
|
2015-04-26 02:11:14 +00:00
|
|
|
config_block
|
2015-04-25 11:25:29 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2015-04-26 02:11:14 +00:00
|
|
|
def is_x86?
|
|
|
|
@opts[:arch] == ARCH_X86
|
|
|
|
end
|
|
|
|
|
2015-05-04 08:58:55 +00:00
|
|
|
def to_str(item, size)
|
|
|
|
@to_str.call(item, size)
|
|
|
|
end
|
|
|
|
|
2015-04-25 11:25:29 +00:00
|
|
|
def to_wchar_t(item, size)
|
2015-05-17 23:26:47 +00:00
|
|
|
to_ascii(item, size).unpack('C*').pack('v*')
|
2015-05-04 08:58:55 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def to_ascii(item, size)
|
|
|
|
item.to_s.ljust(size, "\x00")
|
2015-04-25 11:25:29 +00:00
|
|
|
end
|
|
|
|
|
2015-04-26 02:11:14 +00:00
|
|
|
def session_block(opts)
|
2015-05-17 07:29:47 +00:00
|
|
|
uuid = opts[:uuid].to_raw
|
2015-05-11 23:05:58 +00:00
|
|
|
exit_func = Msf::Payload::Windows.exit_types[opts[:exitfunk]]
|
2015-04-25 11:25:29 +00:00
|
|
|
|
2017-06-05 10:20:42 +00:00
|
|
|
# if no session guid is given then we'll just pass the blank
|
|
|
|
# guid through. this is important for stageless payloads
|
|
|
|
if opts[:stageless] == true
|
|
|
|
session_guid = "\x00" * 16
|
|
|
|
else
|
|
|
|
session_guid = [SecureRandom.uuid.gsub(/-/, '')].pack('H*')
|
|
|
|
end
|
|
|
|
|
2015-04-25 11:25:29 +00:00
|
|
|
session_data = [
|
2015-04-26 02:11:14 +00:00
|
|
|
0, # comms socket, patched in by the stager
|
2015-04-26 03:41:31 +00:00
|
|
|
exit_func, # exit function identifer
|
2015-04-26 02:11:14 +00:00
|
|
|
opts[:expiration], # Session expiry
|
2017-06-05 10:20:42 +00:00
|
|
|
uuid, # the UUID
|
|
|
|
session_guid # the Session GUID
|
2015-04-26 02:11:14 +00:00
|
|
|
]
|
|
|
|
|
2017-07-05 06:14:40 +00:00
|
|
|
session_data.pack('QVVA*A*')
|
2015-04-25 11:25:29 +00:00
|
|
|
end
|
|
|
|
|
2015-04-26 02:11:14 +00:00
|
|
|
def transport_block(opts)
|
2015-04-25 11:25:29 +00:00
|
|
|
# Build the URL from the given parameters, and pad it out to the
|
|
|
|
# correct size
|
2015-04-27 21:44:21 +00:00
|
|
|
lhost = opts[:lhost]
|
2015-04-28 07:41:43 +00:00
|
|
|
if lhost && opts[:scheme].start_with?('http') && Rex::Socket.is_ipv6?(lhost)
|
2015-04-27 21:44:21 +00:00
|
|
|
lhost = "[#{lhost}]"
|
|
|
|
end
|
|
|
|
|
|
|
|
url = "#{opts[:scheme]}://#{lhost}:#{opts[:lport]}"
|
2015-04-25 11:25:29 +00:00
|
|
|
url << "#{opts[:uri]}/" if opts[:uri]
|
2015-05-11 23:05:58 +00:00
|
|
|
url << "?#{opts[:scope_id]}" if opts[:scope_id]
|
2015-04-25 11:25:29 +00:00
|
|
|
|
2015-04-26 02:11:14 +00:00
|
|
|
# if the transport URI is for a HTTP payload we need to add a stack
|
|
|
|
# of other stuff
|
2015-04-28 03:02:31 +00:00
|
|
|
pack = 'A*VVV'
|
2015-04-25 11:25:29 +00:00
|
|
|
transport_data = [
|
2015-05-04 08:58:55 +00:00
|
|
|
to_str(url, URL_SIZE), # transport URL
|
2015-04-27 23:44:48 +00:00
|
|
|
opts[:comm_timeout], # communications timeout
|
|
|
|
opts[:retry_total], # retry total time
|
2015-04-28 03:02:31 +00:00
|
|
|
opts[:retry_wait] # retry wait time
|
2015-04-26 02:11:14 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
if url.start_with?('http')
|
2015-05-11 07:24:02 +00:00
|
|
|
proxy_host = ''
|
|
|
|
if opts[:proxy_host] && opts[:proxy_port]
|
|
|
|
prefix = 'http://'
|
2017-05-22 11:47:37 +00:00
|
|
|
prefix = 'socks=' if opts[:proxy_type].to_s.downcase == 'socks'
|
2015-05-11 07:24:02 +00:00
|
|
|
proxy_host = "#{prefix}#{opts[:proxy_host]}:#{opts[:proxy_port]}"
|
|
|
|
end
|
|
|
|
proxy_host = to_str(proxy_host || '', PROXY_HOST_SIZE)
|
2015-05-04 08:58:55 +00:00
|
|
|
proxy_user = to_str(opts[:proxy_user] || '', PROXY_USER_SIZE)
|
|
|
|
proxy_pass = to_str(opts[:proxy_pass] || '', PROXY_PASS_SIZE)
|
|
|
|
ua = to_str(opts[:ua] || '', UA_SIZE)
|
2015-04-26 02:11:14 +00:00
|
|
|
|
|
|
|
cert_hash = "\x00" * CERT_HASH_SIZE
|
2015-04-27 23:44:48 +00:00
|
|
|
cert_hash = opts[:ssl_cert_hash] if opts[:ssl_cert_hash]
|
2015-04-26 02:11:14 +00:00
|
|
|
|
|
|
|
# add the HTTP specific stuff
|
|
|
|
transport_data << proxy_host # Proxy host name
|
|
|
|
transport_data << proxy_user # Proxy user name
|
|
|
|
transport_data << proxy_pass # Proxy password
|
|
|
|
transport_data << ua # HTTP user agent
|
|
|
|
transport_data << cert_hash # SSL cert hash for verification
|
|
|
|
|
|
|
|
# update the packing spec
|
|
|
|
pack << 'A*A*A*A*A*'
|
|
|
|
end
|
|
|
|
|
|
|
|
# return the packed transport information
|
|
|
|
transport_data.pack(pack)
|
2015-04-25 11:25:29 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def extension_block(ext_name, file_extension)
|
|
|
|
ext_name = ext_name.strip.downcase
|
2015-05-04 21:42:00 +00:00
|
|
|
ext, o = load_rdi_dll(MetasploitPayloads.meterpreter_path("ext_server_#{ext_name}",
|
|
|
|
file_extension))
|
2015-04-25 11:25:29 +00:00
|
|
|
|
2015-05-17 23:26:47 +00:00
|
|
|
extension_data = [ ext.length, ext ].pack('VA*')
|
2015-04-25 11:25:29 +00:00
|
|
|
end
|
|
|
|
|
2015-11-10 09:18:47 +00:00
|
|
|
def extension_init_block(name, value)
|
|
|
|
# for now, we're going to blindly assume that the value is a path to a file
|
|
|
|
# which contains the data that gets passed to the extension
|
|
|
|
content = ::File.read(value)
|
|
|
|
data = [
|
|
|
|
name,
|
|
|
|
"\x00",
|
|
|
|
content.length,
|
|
|
|
content
|
|
|
|
]
|
|
|
|
|
|
|
|
data.pack('A*A*VA*')
|
|
|
|
end
|
|
|
|
|
2015-04-26 02:11:14 +00:00
|
|
|
def config_block
|
2015-04-25 11:25:29 +00:00
|
|
|
# start with the session information
|
2015-04-26 02:11:14 +00:00
|
|
|
config = session_block(@opts)
|
2015-04-25 11:25:29 +00:00
|
|
|
|
|
|
|
# then load up the transport configurations
|
2015-04-26 02:11:14 +00:00
|
|
|
(@opts[:transports] || []).each do |t|
|
2015-04-25 11:25:29 +00:00
|
|
|
config << transport_block(t)
|
|
|
|
end
|
|
|
|
|
2015-04-28 03:02:31 +00:00
|
|
|
# terminate the transports with NULL (wchar)
|
|
|
|
config << "\x00\x00"
|
2015-04-25 11:25:29 +00:00
|
|
|
|
2015-04-28 03:02:31 +00:00
|
|
|
# configure the extensions - this will have to change when posix comes
|
|
|
|
# into play.
|
2015-04-26 02:11:14 +00:00
|
|
|
file_extension = 'x86.dll'
|
2015-04-27 23:44:48 +00:00
|
|
|
file_extension = 'x64.dll' unless is_x86?
|
2015-04-26 02:11:14 +00:00
|
|
|
|
|
|
|
(@opts[:extensions] || []).each do |e|
|
|
|
|
config << extension_block(e, file_extension)
|
2015-04-25 11:25:29 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# terminate the extensions with a 0 size
|
2015-11-10 10:01:23 +00:00
|
|
|
config << [0].pack('V')
|
2015-04-25 11:25:29 +00:00
|
|
|
|
2015-11-10 09:18:47 +00:00
|
|
|
# wire in the extension init data
|
|
|
|
(@opts[:ext_init] || '').split(':').each do |cfg|
|
|
|
|
name, value = cfg.split(',')
|
|
|
|
config << extension_init_block(name, value)
|
|
|
|
end
|
|
|
|
|
|
|
|
# terminate the ext init config with a final null byte
|
|
|
|
config << "\x00"
|
|
|
|
|
2015-04-25 11:25:29 +00:00
|
|
|
# and we're done
|
|
|
|
config
|
|
|
|
end
|
|
|
|
end
|