Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion lib/msf/core/exploit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,14 @@ def is_payload_compatible?(name)
return false unless p

# Skip over payloads that are too big
return false if payload_space && p.cached_size && p.cached_size > payload_space
if payload_space
csize = p.cached_size
if csize.nil?
meta = Msf::Modules::Metadata::Cache.instance.module_reference(p.refname)
csize = meta.payload_cached_size if meta && meta.respond_to?(:payload_cached_size)
end
return false if csize && csize > payload_space
end

begin
pi = p.new
Expand Down
16 changes: 16 additions & 0 deletions lib/msf/core/modules/metadata/obj.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ def build_platform_list(platform_string)
attr_reader :stage_refname
# @return [String, nil] Name of the stager if applicable
attr_reader :stager_refname
# @return [Integer, nil] Cached size of the payload if applicable
attr_reader :payload_cached_size

def initialize(module_instance, obj_hash = nil)
unless obj_hash.nil?
Expand Down Expand Up @@ -205,6 +207,18 @@ def initialize(module_instance, obj_hash = nil)
@adapter_refname = module_instance.adapter_refname
@adapted_refname = module_instance.adapted_refname
end
if module_instance.respond_to?(:cached_size)
@payload_cached_size = module_instance.cached_size
if @payload_cached_size.nil? && module_instance.dynamic_size?
begin
require 'msf/util/payload_cached_size'
opts = Msf::Util::PayloadCachedSize.module_options(module_instance)
@payload_cached_size = module_instance.replicant.generate_simple(opts).bytesize
rescue => e
elog("Failed to generate a default size for dynamic payload #{module_instance.refname}: #{e.class} #{e.message}")
end
end
end

# Due to potentially non-standard ASCII we force UTF-8 to ensure no problem with JSON serialization
force_encoding(::Encoding::UTF_8)
Expand Down Expand Up @@ -253,6 +267,7 @@ def to_json(*args)
'staged' => @staged,
'stage_refname' => @stage_refname,
'stager_refname' => @stager_refname,
'payload_cached_size'=> @payload_cached_size,
}.compact
data.merge!(payload_data)
end
Expand Down Expand Up @@ -322,6 +337,7 @@ def init_from_hash(obj_hash)
@staged = obj_hash['staged']
@stage_refname = obj_hash['stage_refname']
@stager_refname = obj_hash['stager_refname']
@payload_cached_size = obj_hash['payload_cached_size']
end

def sort_platform_string
Expand Down
2 changes: 1 addition & 1 deletion spec/api/json_rpc_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ def mock_rack_env(mock_rack_env_value)
host: host_ip,
analyze_options: {
payloads: [
'linux/x86/meterpreter_reverse_http'
'windows/meterpreter_reverse_http'
]
}
}
Expand Down
79 changes: 79 additions & 0 deletions spec/lib/msf/core/exploit_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require 'spec_helper'

RSpec.describe Msf::Exploit do
let(:framework) { instance_double(Msf::Framework) }
let(:payloads) { double('Payloads') }
let(:subject) do
mod = Class.new(Msf::Exploit).new
allow(mod).to receive(:framework).and_return(framework)
allow(mod).to receive(:payload_space).and_return(2000)
allow(mod).to receive(:compatible?).and_return(true)
allow(mod).to receive(:privileged).and_return(true)
mod
end

before do
allow(framework).to receive(:payloads).and_return(payloads)
end

describe '#is_payload_compatible?' do
let(:payload_name) { 'test/payload' }
let(:payload_class) { double('PayloadClass', refname: payload_name) }
let(:payload_instance) { double('PayloadInstance', privileged: true) }

before do
allow(payloads).to receive(:[]).with(payload_name).and_return(payload_class)
allow(payload_class).to receive(:new).and_return(payload_instance)
end

context 'when payload cached_size is present' do
it 'returns false if cached_size is greater than payload_space' do
allow(payload_class).to receive(:cached_size).and_return(3000)
expect(subject.is_payload_compatible?(payload_name)).to be_falsey
end

it 'returns true if cached_size is less than or equal to payload_space' do
allow(payload_class).to receive(:cached_size).and_return(1000)
expect(subject.is_payload_compatible?(payload_name)).to be_truthy
end
end

context 'when payload cached_size is nil (e.g. dynamic)' do
before do
allow(payload_class).to receive(:cached_size).and_return(nil)
end

context 'and cache contains payload_cached_size' do
let(:cache_instance) { instance_double(Msf::Modules::Metadata::Cache) }
let(:meta) { double('Metadata', payload_cached_size: 2500) }

before do
allow(Msf::Modules::Metadata::Cache).to receive(:instance).and_return(cache_instance)
allow(cache_instance).to receive(:module_reference).with(payload_name).and_return(meta)
end

it 'returns false if metadata payload_cached_size is greater than payload_space' do
expect(subject.is_payload_compatible?(payload_name)).to be_falsey
end

it 'returns true if metadata payload_cached_size is less than payload_space' do
allow(meta).to receive(:payload_cached_size).and_return(1000)
expect(subject.is_payload_compatible?(payload_name)).to be_truthy
end
end

context 'and cache does not contain payload_cached_size' do
let(:cache_instance) { instance_double(Msf::Modules::Metadata::Cache) }

before do
allow(Msf::Modules::Metadata::Cache).to receive(:instance).and_return(cache_instance)
allow(cache_instance).to receive(:module_reference).with(payload_name).and_return(nil)
end

it 'returns true (bypasses size check)' do
expect(subject.is_payload_compatible?(payload_name)).to be_truthy
end
end
end
end
end
108 changes: 108 additions & 0 deletions spec/lib/msf/core/modules/metadata/obj_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
require 'spec_helper'
require 'msf/core/modules/metadata/obj'

RSpec.describe Msf::Modules::Metadata::Obj do
let(:module_instance) do
double('ModuleInstance',
name: 'Test Payload',
realname: 'payload/windows/test',
aliases: [],
disclosure_date: nil,
rank: 300,
type: 'payload',
description: 'Test Description',
author: ['test_author'],
references: [],
post_auth?: false,
default_cred?: false,
platform_to_s: 'Windows',
platform: Msf::Module::PlatformList.new('Windows'),
arch_to_s: 'x86',
datastore: { 'RPORT' => 4444 },
file_path: '/modules/payloads/test.rb',
refname: 'windows/test',
needs_cleanup: false,
has_check?: false,
notes: {},
session_types: [],
payload_type: Msf::Payload::Type::Single,
staged?: false,
cached_size: 1024,
dynamic_size?: false
)
end

before do
allow(module_instance.class).to receive(:refname).and_return('windows/test')
allow(::File).to receive(:mtime).and_return(Time.now)
end

describe 'serialization and deserialization' do
it 'correctly serializes and deserializes payload_cached_size' do
obj = described_class.new(module_instance)
expect(obj.payload_cached_size).to eq(1024)

json = obj.to_json
hash = JSON.parse(json)

expect(hash['payload_cached_size']).to eq(1024)

restored_obj = described_class.from_hash(hash)
expect(restored_obj.payload_cached_size).to eq(1024)
end

context 'with dynamic payload_cached_size' do
let(:module_instance_dynamic) do
double('ModuleInstanceDynamic',
name: 'Dynamic Payload',
realname: 'payload/windows/dynamic',
aliases: [],
disclosure_date: nil,
rank: 300,
type: 'payload',
description: 'Test Description',
author: ['test_author'],
references: [],
post_auth?: false,
default_cred?: false,
platform_to_s: 'Windows',
platform: Msf::Module::PlatformList.new('Windows'),
arch_to_s: 'x86',
datastore: { 'RPORT' => 4444 },
file_path: '/modules/payloads/dynamic.rb',
refname: 'windows/dynamic',
needs_cleanup: false,
has_check?: false,
notes: {},
session_types: [],
payload_type: Msf::Payload::Type::Single,
staged?: false,
cached_size: nil,
dynamic_size?: true
)
end

before do
allow(module_instance_dynamic.class).to receive(:refname).and_return('windows/dynamic')

replicant = double('Replicant')
generated_payload = double('GeneratedPayload', bytesize: 250000)
allow(replicant).to receive(:generate_simple).and_return(generated_payload)
allow(module_instance_dynamic).to receive(:replicant).and_return(replicant)
end

it 'generates a size and serializes/deserializes it' do
obj = described_class.new(module_instance_dynamic)
expect(obj.payload_cached_size).to eq(250000)

json = obj.to_json
hash = JSON.parse(json)

expect(hash['payload_cached_size']).to eq(250000)

restored_obj = described_class.from_hash(hash)
expect(restored_obj.payload_cached_size).to eq(250000)
end
end
end
end
Loading