Class: Msf::Util::PayloadCachedSize
- Inherits:
-
Object
- Object
- Msf::Util::PayloadCachedSize
- Defined in:
- lib/msf/util/payload_cached_size.rb
Overview
The class provides helper methods for verifying and updating the embedded CachedSize constant within payload modules.
Constant Summary collapse
- OPTS =
{ 'Format' => 'raw', 'Options' => { 'VERBOSE' => false, 'CPORT' => 4444, 'LPORT' => 4444, 'RPORT' => 4444, 'CMD' => '/bin/sh', 'URL' => 'http://a.com', 'PATH' => '/', 'BUNDLE' => 'data/isight.bundle', 'DLL' => 'external/source/byakugan/bin/XPSP2/detoured.dll', 'RC4PASSWORD' => 'Metasploit', 'DNSZONE' => 'corelan.eu', 'PEXEC' => '/bin/sh', 'HttpUserAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0', 'StagerURILength' => 5 }, 'Encoder' => nil, 'DisableNops' => true }
- OPTS_ARCH_X64 =
{ 'DLL' => 'data/vncdll.x64.dll', 'PE' => 'data/vncdll.x64.dll' }.freeze
- OPTS_ARCH_X86 =
{ 'DLL' => 'data/vncdll.x86.dll', 'PE' => 'data/vncdll.x86.dll' }.freeze
- OPTS_IPV4 =
{ 'LHOST' => '223.255.255.255', 'RHOST' => '255.255.255.255', 'KHOST' => '255.255.255.255', 'AHOST' => '255.255.255.255' }.freeze
- OPTS_IPV6 =
{ 'LHOST' => 'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'RHOST' => 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'KHOST' => 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'AHOST' => 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' }.freeze
Class Method Summary collapse
-
.cache_size_errors_for(framework, mod) ⇒ String?
Checks for errors or inconsistencies in the CachedSize value for a payload module.
-
.compute_cached_size(framework, mod) ⇒ Integer, String
Calculates the CachedSize value for a payload module.
-
.is_cached_size_accurate?(framework, mod) ⇒ Boolean
Determines whether a payload’s CachedSize is up to date.
-
.is_dynamic?(framework, mod, generation_count = 10) ⇒ Boolean
Determines whether a payload generates a static sized output.
-
.module_options(mod) ⇒ Hash
Get a set of sane default options for the module so it can generate a payload for size analysis.
-
.update_cache_constant(data, cached_size) ⇒ String
Inserts or updates the CachedSize constant in the text of a payload module.
-
.update_cached_size(mod, cached_size) ⇒ void
Insert or update the CachedSize value into a payload module file.
-
.update_module_cached_size(framework, mod) ⇒ String, Integer
Updates the payload module specified with the current CachedSize.
-
.update_stage_sizes_constant(data, stages_with_sizes) ⇒ String
Inserts or updates the CachedSizeOverrides constant in the text of a payload module, removing any previous CachedSizeStages, # Other stager sizes, or CachedSizeOverrides lines.
-
.update_stager_cached_sizes(mod, stages_with_sizes) ⇒ void
Insert or update the CachedSize value into a payload module file.
-
.update_stager_module_cached_size(framework, stages) ⇒ Integer, String
Updates the stager payload module with the most frequent CachedSize value and sets CachedSizeOverrides for other stages.
Class Method Details
.cache_size_errors_for(framework, mod) ⇒ String?
Checks for errors or inconsistencies in the CachedSize value for a payload module. Returns nil if the cache is correct, or a string describing the error if not.
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/msf/util/payload_cached_size.rb', line 215 def self.cache_size_errors_for(framework, mod) is_payload_size_different_on_each_generation = is_dynamic?(framework,mod) module_marked_as_dynamic = mod.dynamic_size? payload_cached_static_size = mod.cached_size # Validate dynamic scenario return if is_payload_size_different_on_each_generation && module_marked_as_dynamic if is_payload_size_different_on_each_generation && !module_marked_as_dynamic return 'Module generated different sizes for each generation attempt. CacheSize must be set to :dynamic' end if payload_cached_static_size.nil? return 'Module missing CachedSize and not marked as dynamic' end payload_size_after_one_generation = mod.replicant.generate_simple((mod)).bytesize # Validate static scenario return if payload_cached_static_size == payload_size_after_one_generation if payload_cached_static_size != payload_size_after_one_generation return "Module marked as having size #{payload_cached_static_size} but after one generation was #{payload_size_after_one_generation}" end raise "unhandled scenario" end |
.compute_cached_size(framework, mod) ⇒ Integer, String
Calculates the CachedSize value for a payload module
169 170 171 172 173 |
# File 'lib/msf/util/payload_cached_size.rb', line 169 def self.compute_cached_size(framework, mod) return ":dynamic" if is_dynamic?(framework, mod) mod.replicant.generate_simple((mod)).bytesize end |
.is_cached_size_accurate?(framework, mod) ⇒ Boolean
Determines whether a payload’s CachedSize is up to date
202 203 204 205 206 207 |
# File 'lib/msf/util/payload_cached_size.rb', line 202 def self.is_cached_size_accurate?(framework, mod) return true if mod.dynamic_size? && is_dynamic?(framework, mod) return false if mod.cached_size.nil? mod.cached_size == mod.replicant.generate_simple((mod)).bytesize end |
.is_dynamic?(framework, mod, generation_count = 10) ⇒ Boolean
Determines whether a payload generates a static sized output
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/msf/util/payload_cached_size.rb', line 181 def self.is_dynamic?(framework, mod, generation_count=10) return true if mod.class.const_defined?('ForceDynamicCachedSize') && mod.class::ForceDynamicCachedSize opts = (mod) last_bytesize = nil generation_count.times do # Ensure a new module instance is created for each attempt, as some options are randomized on load - such as tmp file path names etc new_mod = framework.payloads.create(mod.refname) bytesize = new_mod.generate_simple(opts).bytesize last_bytesize ||= bytesize if last_bytesize != bytesize return true end end false end |
.module_options(mod) ⇒ Hash
Get a set of sane default options for the module so it can generate a payload for size analysis.
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/msf/util/payload_cached_size.rb', line 248 def self.(mod) opts = OPTS.clone # Assign this way to overwrite the Options key of the newly cloned hash opts['Options'] = opts['Options'].merge(mod.shortname =~ /6/ ? OPTS_IPV6 : OPTS_IPV4) # Extract the AdaptedArch for adaptor payloads, note `mod.adapted_arch` is not part of the public API # at this time, but could be in the future. The use of send is safe for now as it is an internal tool # with automated tests if the API were to change in the future adapted_arch = mod.send(:module_info)['AdaptedArch'] if adapted_arch == ARCH_X64 || mod.arch_to_s == ARCH_X64 opts['Options'].merge!(OPTS_ARCH_X64) elsif adapted_arch == ARCH_X86 || mod.arch_to_s == ARCH_X86 opts['Options'].merge!(OPTS_ARCH_X86) end opts end |
.update_cache_constant(data, cached_size) ⇒ String
Inserts or updates the CachedSize constant in the text of a payload module.
68 69 70 71 72 73 74 |
# File 'lib/msf/util/payload_cached_size.rb', line 68 def self.update_cache_constant(data, cached_size) data. gsub(/^\s*CachedSize\s*=\s*(\d+|:dynamic).*/, ''). gsub(/^(module MetasploitModule)\s*\n/) do |m| "#{m.strip}\n CachedSize = #{cached_size}\n\n" end end |
.update_cached_size(mod, cached_size) ⇒ void
This method returns an undefined value.
Insert or update the CachedSize value into a payload module file
100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/msf/util/payload_cached_size.rb', line 100 def self.update_cached_size(mod, cached_size) mod_data = "" file_path = mod.file_path ::File.open(file_path, 'rb') do |fd| mod_data = fd.read(fd.stat.size) end ::File.open(file_path, 'wb') do |fd| fd.write update_cache_constant(mod_data, cached_size) end end |
.update_module_cached_size(framework, mod) ⇒ String, Integer
Updates the payload module specified with the current CachedSize
138 139 140 141 142 |
# File 'lib/msf/util/payload_cached_size.rb', line 138 def self.update_module_cached_size(framework, mod) cached_size = compute_cached_size(framework, mod) update_cached_size(mod, cached_size) cached_size end |
.update_stage_sizes_constant(data, stages_with_sizes) ⇒ String
Inserts or updates the CachedSizeOverrides constant in the text of a payload module, removing any previous CachedSizeStages, # Other stager sizes, or CachedSizeOverrides lines.
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/msf/util/payload_cached_size.rb', line 82 def self.update_stage_sizes_constant(data, stages_with_sizes) sizes = stages_with_sizes.sort_by { |stage_with_size| stage_with_size[:stage].refname }.map do |stage_with_size| [stage_with_size[:stage].refname, stage_with_size[:size]] end data_without_other_stages = data.gsub(/^\s*CachedSizeOverrides\s*=.*\n/, '') return data_without_other_stages if sizes.empty? data_without_other_stages.gsub(/^\s*(CachedSize\s*=\s*(\d+|:dynamic))\s*\n/) do |m| " #{m.strip}\n CachedSizeOverrides = {#{sizes.map { |(k, v)| %Q{"#{k}" => #{v}}}.join(', ')}}\n\n" end end |
.update_stager_cached_sizes(mod, stages_with_sizes) ⇒ void
This method returns an undefined value.
Insert or update the CachedSize value into a payload module file
119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/msf/util/payload_cached_size.rb', line 119 def self.update_stager_cached_sizes(mod, stages_with_sizes) mod_data = "" file_path = mod.file_path ::File.open(file_path, 'rb') do |fd| mod_data = fd.read(fd.stat.size) end ::File.open(file_path, 'wb') do |fd| fd.write update_stage_sizes_constant( mod_data, stages_with_sizes) end end |
.update_stager_module_cached_size(framework, stages) ⇒ Integer, String
Updates the stager payload module with the most frequent CachedSize value and sets CachedSizeOverrides for other stages.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/msf/util/payload_cached_size.rb', line 149 def self.update_stager_module_cached_size(framework, stages) stages_with_sizes = stages.map do |stage| { stage: stage, size: compute_cached_size(framework, stage) } end most_frequent_cached_size = stages_with_sizes.map { |stage_with_size| stage_with_size[:size] } .select { |size| size.is_a?(Numeric) }.tally.sort_by(&:last).to_h.keys.last new_size = most_frequent_cached_size || stages_with_sizes.first[:size] other_sizes = stages_with_sizes.select { |stage_with_size| stage_with_size[:size] != new_size } update_cached_size(stages.first, new_size) update_stager_cached_sizes(stages.first, other_sizes) new_size end |