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.

payload_generator.rb 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. # -*- coding: binary -*-
  2. require 'msf/core/payload/apk'
  3. require 'active_support/core_ext/numeric/bytes'
  4. module Msf
  5. class PayloadGeneratorError < StandardError
  6. end
  7. class EncoderSpaceViolation < PayloadGeneratorError
  8. end
  9. class PayloadSpaceViolation < PayloadGeneratorError
  10. end
  11. class IncompatibleArch < PayloadGeneratorError
  12. end
  13. class IncompatibleEndianess < PayloadGeneratorError
  14. end
  15. class IncompatiblePlatform < PayloadGeneratorError
  16. end
  17. class InvalidFormat < PayloadGeneratorError
  18. end
  19. class PayloadGenerator
  20. # @!attribute add_code
  21. # @return [String] The path to a shellcode file to execute in a separate thread
  22. attr_accessor :add_code
  23. # @!attribute arch
  24. # @return [String] The CPU architecture to build the payload for
  25. attr_accessor :arch
  26. # @!attribute badchars
  27. # @return [String] The bad characters that can't be in the payload
  28. attr_accessor :badchars
  29. # @!attribute cli
  30. # @return [Boolean] Whether this is being run by a CLI script
  31. attr_accessor :cli
  32. # @!attribute datastore
  33. # @return [Hash] The datastore to apply to the payload module
  34. attr_accessor :datastore
  35. # @!attribute encoder
  36. # @return [String] The encoder(s) you want applied to the payload
  37. attr_accessor :encoder
  38. # @!attribute format
  39. # @return [String] The format you want the payload returned in
  40. attr_accessor :format
  41. # @!attribute framework
  42. # @return [Msf::Framework] The framework instance to use for generation
  43. attr_accessor :framework
  44. # @!attribute iterations
  45. # @return [Fixnum] The number of iterations to run the encoder
  46. attr_accessor :iterations
  47. # @!attribute keep
  48. # @return [Boolean] Whether or not to preserve the original functionality of the template
  49. attr_accessor :keep
  50. # @!attribute nops
  51. # @return [Fixnum] The size in bytes of NOP sled to prepend the payload with
  52. attr_accessor :nops
  53. # @!attribute payload
  54. # @return [String] The refname of the payload to generate
  55. attr_accessor :payload
  56. # @!attribute platform
  57. # @return [String] The platform to build the payload for
  58. attr_accessor :platform
  59. # @!attribute smallest
  60. # @return [Boolean] Whether or not to find the smallest possible output
  61. attr_accessor :smallest
  62. # @!attribute space
  63. # @return [Fixnum] The maximum size in bytes of the payload
  64. attr_accessor :space
  65. # @!attribute encoder_space
  66. # @return [Fixnum] The maximum size in bytes of the encoded payload
  67. attr_accessor :encoder_space
  68. # @!attribute stdin
  69. # @return [String] The raw bytes of a payload taken from STDIN
  70. attr_accessor :stdin
  71. # @!attribute template
  72. # @return [String] The path to an executable template to use
  73. attr_accessor :template
  74. # @!attribute var_name
  75. # @return [String] The custom variable string for certain output formats
  76. attr_accessor :var_name
  77. # @param opts [Hash] The options hash
  78. # @option opts [String] :payload (see #payload)
  79. # @option opts [String] :format (see #format)
  80. # @option opts [String] :encoder (see #encoder)
  81. # @option opts [Fixnum] :iterations (see #iterations)
  82. # @option opts [String] :arch (see #arch)
  83. # @option opts [String] :platform (see #platform)
  84. # @option opts [String] :badchars (see #badchars)
  85. # @option opts [String] :template (see #template)
  86. # @option opts [Fixnum] :space (see #space)
  87. # @option opts [Fixnum] :encoder_space (see #encoder_space)
  88. # @option opts [Fixnum] :nops (see #nops)
  89. # @option opts [String] :add_code (see #add_code)
  90. # @option opts [Boolean] :keep (see #keep)
  91. # @option opts [Hash] :datastore (see #datastore)
  92. # @option opts [Msf::Framework] :framework (see #framework)
  93. # @option opts [Boolean] :cli (see #cli)
  94. # @option opts [Boolean] :smallest (see #smallest)
  95. # @raise [KeyError] if framework is not provided in the options hash
  96. def initialize(opts={})
  97. @add_code = opts.fetch(:add_code, '')
  98. @arch = opts.fetch(:arch, '')
  99. @badchars = opts.fetch(:badchars, '')
  100. @cli = opts.fetch(:cli, false)
  101. @datastore = opts.fetch(:datastore, {})
  102. @encoder = opts.fetch(:encoder, '')
  103. @format = opts.fetch(:format, 'raw')
  104. @iterations = opts.fetch(:iterations, 1)
  105. @keep = opts.fetch(:keep, false)
  106. @nops = opts.fetch(:nops, 0)
  107. @payload = opts.fetch(:payload, '')
  108. @platform = opts.fetch(:platform, '')
  109. @space = opts.fetch(:space, 1.gigabyte)
  110. @stdin = opts.fetch(:stdin, nil)
  111. @template = opts.fetch(:template, '')
  112. @var_name = opts.fetch(:var_name, 'buf')
  113. @smallest = opts.fetch(:smallest, false)
  114. @encoder_space = opts.fetch(:encoder_space, @space)
  115. @framework = opts.fetch(:framework)
  116. raise ArgumentError, "Invalid Payload Selected" unless payload_is_valid?
  117. raise ArgumentError, "Invalid Format Selected" unless format_is_valid?
  118. # In smallest mode, override the payload @space & @encoder_space settings
  119. if @smallest
  120. @space = 0
  121. @encoder_space = 1.gigabyte
  122. end
  123. end
  124. # This method takes the shellcode generated so far and adds shellcode from
  125. # a supplied file. The added shellcode is executed in a separate thread
  126. # from the main payload.
  127. # @param shellcode [String] The shellcode to add to
  128. # @return [String] the combined shellcode which executes the added code in a separate thread
  129. def add_shellcode(shellcode)
  130. if add_code.present? and platform_list.platforms.include? Msf::Module::Platform::Windows and arch == "x86"
  131. cli_print "Adding shellcode from #{add_code} to the payload"
  132. shellcode_file = File.open(add_code)
  133. shellcode_file.binmode
  134. added_code = shellcode_file.read
  135. shellcode_file.close
  136. shellcode = ::Msf::Util::EXE.win32_rwx_exec_thread(shellcode,0,'end')
  137. shellcode << added_code
  138. else
  139. shellcode.dup
  140. end
  141. end
  142. # This method takes a payload module and tries to reconcile a chosen
  143. # arch with the arches supported by the module.
  144. # @param mod [Msf::Payload] The module class to choose an arch for
  145. # @return [String] String form of the Arch if a valid arch found
  146. # @return [Nil] if no valid arch found
  147. def choose_arch(mod)
  148. if arch.blank?
  149. @arch = mod.arch.first
  150. cli_print "No Arch selected, selecting Arch: #{arch} from the payload"
  151. datastore['ARCH'] = arch if mod.kind_of?(Msf::Payload::Generic)
  152. return mod.arch.first
  153. elsif mod.arch.include? arch
  154. datastore['ARCH'] = arch if mod.kind_of?(Msf::Payload::Generic)
  155. return arch
  156. else
  157. return nil
  158. end
  159. end
  160. # This method takes a payload module and tries to reconcile a chosen
  161. # platform with the platforms supported by the module.
  162. # @param mod [Msf::Payload] The module class to choose a platform for
  163. # @return [Msf::Module::PlatformList] The selected platform list
  164. def choose_platform(mod)
  165. # By default, platform_list will at least return Msf::Module::Platform
  166. # if there is absolutely no pre-configured platform info at all
  167. chosen_platform = platform_list
  168. if chosen_platform.platforms.empty?
  169. chosen_platform = mod.platform
  170. cli_print "No platform was selected, choosing #{chosen_platform.platforms.first} from the payload"
  171. @platform = mod.platform.platforms.first.to_s.split("::").last
  172. elsif (chosen_platform & mod.platform).empty?
  173. chosen_platform = Msf::Module::PlatformList.new
  174. end
  175. begin
  176. platform_object = Msf::Module::Platform.find_platform(platform)
  177. rescue ArgumentError
  178. platform_object = nil
  179. end
  180. if mod.kind_of?(Msf::Payload::Generic) && mod.send(:module_info)['Platform'].empty? && platform_object
  181. datastore['PLATFORM'] = platform
  182. end
  183. chosen_platform
  184. end
  185. # This method takes the shellcode generated so far and iterates through
  186. # the chosen or compatible encoders. It attempts to encode the payload
  187. # with each encoder until it finds one that works.
  188. # @param shellcode [String] The shellcode to encode
  189. # @return [String] The encoded shellcode
  190. def encode_payload(shellcode)
  191. shellcode = shellcode.dup
  192. encoder_list = get_encoders
  193. if encoder_list.empty?
  194. cli_print "No encoder or badchars specified, outputting raw payload"
  195. return shellcode
  196. end
  197. results = {}
  198. cli_print "Found #{encoder_list.count} compatible encoders"
  199. encoder_list.each do |encoder_mod|
  200. cli_print "Attempting to encode payload with #{iterations} iterations of #{encoder_mod.refname}"
  201. begin
  202. encoder_mod.available_space = @encoder_space unless @smallest
  203. results[encoder_mod.refname] = run_encoder(encoder_mod, shellcode.dup)
  204. break unless @smallest
  205. rescue ::Msf::EncoderSpaceViolation => e
  206. cli_print "#{encoder_mod.refname} failed with #{e.message}"
  207. next
  208. rescue ::Msf::EncodingError => e
  209. cli_print "#{encoder_mod.refname} failed with #{e.message}"
  210. next
  211. end
  212. end
  213. if results.keys.length == 0
  214. raise ::Msf::EncodingError, "No Encoder Succeeded"
  215. end
  216. # Return the shortest encoding of the payload
  217. chosen_encoder = results.keys.sort{|a,b| results[a].length <=> results[b].length}.first
  218. cli_print "#{chosen_encoder} chosen with final size #{results[chosen_encoder].length}"
  219. results[chosen_encoder]
  220. end
  221. # This returns a hash for the exe format generation of payloads
  222. # @return [Hash] The hash needed for generating an executable format
  223. def exe_options
  224. opts = { inject: keep }
  225. unless template.blank?
  226. opts[:template_path] = File.dirname(template)
  227. opts[:template] = File.basename(template)
  228. end
  229. opts
  230. end
  231. # This method takes the payload shellcode and formats it appropriately based
  232. # on the selected output format.
  233. # @param shellcode [String] the processed shellcode to be formatted
  234. # @return [String] The final formatted form of the payload
  235. def format_payload(shellcode)
  236. case format.downcase
  237. when "js_be"
  238. if Rex::Arch.endian(arch) != ENDIAN_BIG
  239. raise IncompatibleEndianess, "Big endian format selected for a non big endian payload"
  240. else
  241. ::Msf::Simple::Buffer.transform(shellcode, format, @var_name)
  242. end
  243. when *::Msf::Simple::Buffer.transform_formats
  244. ::Msf::Simple::Buffer.transform(shellcode, format, @var_name)
  245. when *::Msf::Util::EXE.to_executable_fmt_formats
  246. ::Msf::Util::EXE.to_executable_fmt(framework, arch, platform_list, shellcode, format, exe_options)
  247. else
  248. raise InvalidFormat, "you have selected an invalid payload format"
  249. end
  250. end
  251. # This method generates Java payloads which are a special case.
  252. # They can be generated in raw or war formats, which respectively
  253. # produce a JAR or WAR file for the java payload.
  254. # @return [String] Java payload as a JAR or WAR file
  255. def generate_java_payload
  256. payload_module = framework.payloads.create(payload)
  257. payload_module.datastore.merge!(datastore)
  258. case format
  259. when "raw", "jar"
  260. if payload_module.respond_to? :generate_jar
  261. payload_module.generate_jar.pack
  262. else
  263. payload_module.generate
  264. end
  265. when "war"
  266. if payload_module.respond_to? :generate_war
  267. payload_module.generate_war.pack
  268. else
  269. raise InvalidFormat, "#{payload} is not a Java payload"
  270. end
  271. when "axis2"
  272. if payload_module.respond_to? :generate_axis2
  273. payload_module.generate_axis2.pack
  274. else
  275. raise InvalidFormat, "#{payload} is not a Java payload"
  276. end
  277. else
  278. raise InvalidFormat, "#{format} is not a valid format for Java payloads"
  279. end
  280. end
  281. # This method is a wrapper around all of the other methods. It calls the correct
  282. # methods in order based on the supplied options and returns the finished payload.
  283. # @return [String] A string containing the bytes of the payload in the format selected
  284. def generate_payload
  285. if platform == "java" or arch == "java" or payload.start_with? "java/"
  286. raw_payload = generate_java_payload
  287. cli_print "Payload size: #{raw_payload.length} bytes"
  288. gen_payload = raw_payload
  289. elsif payload.start_with? "android/" and not template.blank?
  290. cli_print "Using APK template: #{template}"
  291. apk_backdoor = ::Msf::Payload::Apk::ApkBackdoor::new()
  292. raw_payload = apk_backdoor.backdoor_apk(template, generate_raw_payload)
  293. cli_print "Payload size: #{raw_payload.length} bytes"
  294. gen_payload = raw_payload
  295. else
  296. raw_payload = generate_raw_payload
  297. raw_payload = add_shellcode(raw_payload)
  298. encoded_payload = encode_payload(raw_payload)
  299. encoded_payload = prepend_nops(encoded_payload)
  300. cli_print "Payload size: #{encoded_payload.length} bytes"
  301. gen_payload = format_payload(encoded_payload)
  302. end
  303. if gen_payload.nil?
  304. raise PayloadGeneratorError, 'The payload could not be generated, check options'
  305. elsif gen_payload.length > @space and not @smallest
  306. raise PayloadSpaceViolation, 'The payload exceeds the specified space'
  307. else
  308. if format.to_s != 'raw'
  309. cli_print "Final size of #{format} file: #{gen_payload.length} bytes"
  310. end
  311. gen_payload
  312. end
  313. end
  314. # This method generates the raw form of the payload as generated by the payload module itself.
  315. # @raise [Msf::IncompatiblePlatform] if no platform was selected for a stdin payload
  316. # @raise [Msf::IncompatibleArch] if no arch was selected for a stdin payload
  317. # @raise [Msf::IncompatiblePlatform] if the platform is incompatible with the payload
  318. # @raise [Msf::IncompatibleArch] if the arch is incompatible with the payload
  319. # @return [String] the raw bytes of the payload to be generated
  320. def generate_raw_payload
  321. if payload == 'stdin'
  322. if arch.blank?
  323. raise IncompatibleArch, "You must select an arch for a custom payload"
  324. elsif platform.blank?
  325. raise IncompatiblePlatform, "You must select a platform for a custom payload"
  326. end
  327. stdin
  328. else
  329. payload_module = framework.payloads.create(payload)
  330. chosen_platform = choose_platform(payload_module)
  331. if chosen_platform.platforms.empty?
  332. raise IncompatiblePlatform, "The selected platform is incompatible with the payload"
  333. end
  334. chosen_arch = choose_arch(payload_module)
  335. unless chosen_arch
  336. raise IncompatibleArch, "The selected arch is incompatible with the payload"
  337. end
  338. payload_module.generate_simple(
  339. 'Format' => 'raw',
  340. 'Options' => datastore,
  341. 'Encoder' => nil,
  342. 'MaxSize' => @space,
  343. 'DisableNops' => true
  344. )
  345. end
  346. end
  347. # This method returns an array of encoders that either match the
  348. # encoders selected by the user, or match the arch selected.
  349. # @return [Array<Msf::Encoder>] An array of potential encoders to use
  350. def get_encoders
  351. encoders = []
  352. if encoder.present?
  353. # Allow comma separated list of encoders so users can choose several
  354. encoder.split(',').each do |chosen_encoder|
  355. e = framework.encoders.create(chosen_encoder)
  356. e.datastore.import_options_from_hash(datastore)
  357. encoders << e if e
  358. end
  359. encoders.sort_by { |my_encoder| my_encoder.rank }.reverse
  360. elsif !badchars.empty? && !badchars.nil?
  361. framework.encoders.each_module_ranked('Arch' => [arch], 'Platform' => platform_list) do |name, mod|
  362. e = framework.encoders.create(name)
  363. e.datastore.import_options_from_hash(datastore)
  364. encoders << e if e
  365. end
  366. encoders.select{ |my_encoder| my_encoder.rank != ManualRanking }.sort_by { |my_encoder| my_encoder.rank }.reverse
  367. else
  368. encoders
  369. end
  370. end
  371. # Returns a PlatformList object based on the platform string given at creation.
  372. # @return [Msf::Module::PlatformList] It will be empty if no valid platforms found
  373. def platform_list
  374. if platform.blank?
  375. list = Msf::Module::PlatformList.new
  376. else
  377. begin
  378. list = ::Msf::Module::PlatformList.transform(platform)
  379. rescue
  380. list = Msf::Module::PlatformList.new
  381. end
  382. end
  383. list
  384. end
  385. # This method takes an encoded payload and prepends a NOP Sled to it
  386. # with a size based on the nops value given to the generator.
  387. # @param shellcode [String] The shellcode to prepend the NOPs to
  388. # @return [String] the shellcode with the appropriate nopsled affixed
  389. def prepend_nops(shellcode)
  390. if nops > 0
  391. framework.nops.each_module_ranked('Arch' => [arch]) do |name, mod|
  392. nop = framework.nops.create(name)
  393. raw = nop.generate_sled(nops, {'BadChars' => badchars, 'SaveRegisters' => [ 'esp', 'ebp', 'esi', 'edi' ] })
  394. if raw
  395. cli_print "Successfully added NOP sled from #{name}"
  396. return raw + shellcode
  397. end
  398. end
  399. else
  400. shellcode
  401. end
  402. end
  403. # This method runs a specified encoder, for a number of defined iterations against the shellcode.
  404. # @param encoder_module [Msf::Encoder] The Encoder to run against the shellcode
  405. # @param shellcode [String] The shellcode to be encoded
  406. # @return [String] The encoded shellcode
  407. # @raise [Msf::EncoderSpaceViolation] If the Encoder makes the shellcode larger than the supplied space limit
  408. def run_encoder(encoder_module, shellcode)
  409. iterations.times do |x|
  410. shellcode = encoder_module.encode(shellcode.dup, badchars, nil, platform_list)
  411. cli_print "#{encoder_module.refname} succeeded with size #{shellcode.length} (iteration=#{x})"
  412. if shellcode.length > encoder_space
  413. raise EncoderSpaceViolation, "encoder has made a buffer that is too big"
  414. end
  415. end
  416. shellcode
  417. end
  418. private
  419. # This method prints output to the console if running in CLI mode
  420. # @param [String] message The message to print to the console.
  421. def cli_print(message= '')
  422. $stderr.puts message if cli
  423. end
  424. # This method checks if the Generator's selected format is valid
  425. # @return [True] if the format is valid
  426. # @return [False] if the format is not valid
  427. def format_is_valid?
  428. formats = (::Msf::Util::EXE.to_executable_fmt_formats + ::Msf::Simple::Buffer.transform_formats).uniq
  429. formats.include? format.downcase
  430. end
  431. # This method checks if the Generator's selected payload is valid
  432. # @return [True] if the payload is a valid Metasploit Payload
  433. # @return [False] if the payload is not a valid Metasploit Payload
  434. def payload_is_valid?
  435. (framework.payloads.keys + ['stdin']).include? payload
  436. end
  437. end
  438. end