From 1908f279cb3e5ea02e0d98222ba2b5c277b5863f Mon Sep 17 00:00:00 2001 From: cgranleese-r7 Date: Wed, 8 Jul 2026 12:38:00 +0100 Subject: [PATCH 1/2] Add malleablec2 tests --- .../shared_meterpreter_acceptance.yml | 2 +- spec/acceptance/meterpreter_spec.rb | 2 +- .../malleable_c2/base64_transforms.profile | 54 +++++++ .../malleable_c2/minimal_uris_headers.profile | 52 +++++++ .../lib/msf/core/payload/malleable_c2_spec.rb | 45 ++++++ spec/support/acceptance/session/java.rb | 62 ++++++++ spec/support/acceptance/session/mettle.rb | 146 ++++++++++++++++++ spec/support/acceptance/session/php.rb | 66 ++++++++ spec/support/acceptance/session/python.rb | 70 +++++++++ .../acceptance/session/windows_meterpreter.rb | 138 +++++++++++++++++ 10 files changed, 635 insertions(+), 2 deletions(-) create mode 100644 spec/file_fixtures/malleable_c2/base64_transforms.profile create mode 100644 spec/file_fixtures/malleable_c2/minimal_uris_headers.profile create mode 100644 spec/lib/msf/core/payload/malleable_c2_spec.rb diff --git a/.github/workflows/shared_meterpreter_acceptance.yml b/.github/workflows/shared_meterpreter_acceptance.yml index a5ead47544831..6d0a0e1b09432 100644 --- a/.github/workflows/shared_meterpreter_acceptance.yml +++ b/.github/workflows/shared_meterpreter_acceptance.yml @@ -97,7 +97,7 @@ jobs: runs-on: ${{ matrix.os }} - timeout-minutes: 50 + timeout-minutes: 180 env: RAILS_ENV: test diff --git a/spec/acceptance/meterpreter_spec.rb b/spec/acceptance/meterpreter_spec.rb index ded5fbaddcf38..f1d84fdb897a0 100644 --- a/spec/acceptance/meterpreter_spec.rb +++ b/spec/acceptance/meterpreter_spec.rb @@ -136,7 +136,7 @@ def initialize(path) end console.sendline payload.handler_command(default_module_datastore: default_module_datastore) - console.recvuntil(/Started reverse TCP handler[^\n]*\n/) + console.recvuntil(/Started (?:reverse TCP|HTTPS? reverse) handler[^\n]*\n/) payload_process = executed_payload session_id = nil diff --git a/spec/file_fixtures/malleable_c2/base64_transforms.profile b/spec/file_fixtures/malleable_c2/base64_transforms.profile new file mode 100644 index 0000000000000..e2545483225b5 --- /dev/null +++ b/spec/file_fixtures/malleable_c2/base64_transforms.profile @@ -0,0 +1,54 @@ +# base64_transforms.profile +# Purpose: exercises base64 encoding on server->client GET responses and +# prepend/append transforms on client->server POST bodies. +# Used by acceptance tests to confirm wrap/unwrap encoding round-trips correctly. + +set useragent "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"; + +http-get { + set uri "/updates/check"; + + client { + header "Accept" "application/json"; + + metadata { + parameter "v"; + } + } + + server { + header "Content-Type" "application/octet-stream"; + + output { + base64; + prepend "START_"; + append "_END"; + print; + } + } +} + +http-post { + set uri "/updates/report"; + + client { + header "Content-Type" "application/octet-stream"; + + id { + parameter "uid"; + } + + output { + base64; + print; + } + } + + server { + header "Content-Type" "text/plain"; + + output { + print; + } + } +} diff --git a/spec/file_fixtures/malleable_c2/minimal_uris_headers.profile b/spec/file_fixtures/malleable_c2/minimal_uris_headers.profile new file mode 100644 index 0000000000000..51c552ceb0c2f --- /dev/null +++ b/spec/file_fixtures/malleable_c2/minimal_uris_headers.profile @@ -0,0 +1,52 @@ +# minimal_uris_headers.profile +# Purpose: exercises custom URI routing + response headers; no encoding directives. +# Used by acceptance tests to confirm the handler registers profile URIs and that +# the payload reaches a session over those URIs. + +set useragent "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"; + +http-get { + set uri "/jquery-3.3.1.min.js"; + + client { + header "Accept" "text/javascript, application/javascript"; + header "Referer" "https://www.example.com/"; + + metadata { + parameter "callback"; + } + } + + server { + header "Content-Type" "application/javascript; charset=utf-8"; + header "Cache-Control" "max-age=604800"; + + output { + print; + } + } +} + +http-post { + set uri "/jquery-3.3.1.min.js/save"; + + client { + header "Content-Type" "application/octet-stream"; + + id { + parameter "id"; + } + + output { + print; + } + } + + server { + header "Content-Type" "text/plain; charset=utf-8"; + + output { + print; + } + } +} diff --git a/spec/lib/msf/core/payload/malleable_c2_spec.rb b/spec/lib/msf/core/payload/malleable_c2_spec.rb new file mode 100644 index 0000000000000..476ecc97222da --- /dev/null +++ b/spec/lib/msf/core/payload/malleable_c2_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Msf::Payload::MalleableC2 do + let(:fixture_path) { File.join(Msf::Config.install_root, 'spec', 'file_fixtures', 'malleable_c2') } + + describe Msf::Payload::MalleableC2::Parser do + subject(:parser) { described_class.new } + + describe '#parse' do + context 'with minimal_uris_headers.profile' do + it 'returns a ParsedProfile without raising' do + path = File.join(fixture_path, 'minimal_uris_headers.profile') + result = parser.parse(path) + expect(result).to be_a(Msf::Payload::MalleableC2::ParsedProfile) + end + end + + context 'with base64_transforms.profile' do + it 'returns a ParsedProfile without raising' do + path = File.join(fixture_path, 'base64_transforms.profile') + result = parser.parse(path) + expect(result).to be_a(Msf::Payload::MalleableC2::ParsedProfile) + end + end + + context 'with a non-existent path' do + it 'raises an exception' do + expect { + parser.parse('/nonexistent/path.profile') + }.to raise_error(Exception) + end + end + end + + describe 'ParsedProfile#uris' do + it 'returns the URIs declared in minimal_uris_headers.profile' do + path = File.join(fixture_path, 'minimal_uris_headers.profile') + profile = parser.parse(path) + expect(profile.uris).to contain_exactly('/jquery-3.3.1.min.js', '/jquery-3.3.1.min.js/save') + end + end + end +end diff --git a/spec/support/acceptance/session/java.rb b/spec/support/acceptance/session/java.rb index 5149ba8ab7a94..ae46039d12a59 100644 --- a/spec/support/acceptance/session/java.rb +++ b/spec/support/acceptance/session/java.rb @@ -1,6 +1,8 @@ require_relative './shared' module Acceptance::Session::Java + MALLEABLE_C2_FIXTURE_PATH = File.expand_path('../../../../../spec/file_fixtures/malleable_c2', __FILE__) + JAVA_METERPRETER = { payloads: [ { @@ -17,6 +19,66 @@ module Acceptance::Session::Java spawn: 0 } } + }, + { + name: "java/meterpreter_reverse_http", + extension: ".jar", + platforms: [:osx, :linux, :windows], + execute_cmd: ["java", "-jar", "${payload_path}"], + generate_options: { + '-f': "jar" + }, + datastore: { + global: {}, + module: { + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "java/meterpreter_reverse_http", + extension: ".jar", + platforms: [:osx, :linux, :windows], + execute_cmd: ["java", "-jar", "${payload_path}"], + generate_options: { + '-f': "jar" + }, + datastore: { + global: {}, + module: { + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } + }, + { + name: "java/meterpreter_reverse_https", + extension: ".jar", + platforms: [:osx, :linux, :windows], + execute_cmd: ["java", "-jar", "${payload_path}"], + generate_options: { + '-f': "jar" + }, + datastore: { + global: {}, + module: { + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "java/meterpreter_reverse_https", + extension: ".jar", + platforms: [:osx, :linux, :windows], + execute_cmd: ["java", "-jar", "${payload_path}"], + generate_options: { + '-f': "jar" + }, + datastore: { + global: {}, + module: { + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } } ], module_tests: [ diff --git a/spec/support/acceptance/session/mettle.rb b/spec/support/acceptance/session/mettle.rb index 76935197e4cff..a1c9017047551 100644 --- a/spec/support/acceptance/session/mettle.rb +++ b/spec/support/acceptance/session/mettle.rb @@ -1,6 +1,8 @@ require_relative './shared' module Acceptance::Session::Mettle + MALLEABLE_C2_FIXTURE_PATH = File.expand_path('../../../../../spec/file_fixtures/malleable_c2', __FILE__) + METTLE_METERPRETER = { payloads: [ { @@ -36,6 +38,150 @@ module Acceptance::Session::Mettle MeterpreterDebugBuild: true } } + }, + { + name: "linux/x64/meterpreter_reverse_http", + extension: "", + platforms: [:linux], + executable: true, + execute_cmd: ["${payload_path}"], + generate_options: { + '-f': "elf" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "linux/x64/meterpreter_reverse_http", + extension: "", + platforms: [:linux], + executable: true, + execute_cmd: ["${payload_path}"], + generate_options: { + '-f': "elf" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } + }, + { + name: "osx/x64/meterpreter_reverse_http", + extension: "", + platforms: [:osx], + executable: true, + execute_cmd: ["${payload_path}"], + generate_options: { + '-f': "macho" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "osx/x64/meterpreter_reverse_http", + extension: "", + platforms: [:osx], + executable: true, + execute_cmd: ["${payload_path}"], + generate_options: { + '-f': "macho" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } + }, + { + name: "linux/x64/meterpreter_reverse_https", + extension: "", + platforms: [:linux], + executable: true, + execute_cmd: ["${payload_path}"], + generate_options: { + '-f': "elf" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "linux/x64/meterpreter_reverse_https", + extension: "", + platforms: [:linux], + executable: true, + execute_cmd: ["${payload_path}"], + generate_options: { + '-f': "elf" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } + }, + { + name: "osx/x64/meterpreter_reverse_https", + extension: "", + platforms: [:osx], + executable: true, + execute_cmd: ["${payload_path}"], + generate_options: { + '-f': "macho" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "osx/x64/meterpreter_reverse_https", + extension: "", + platforms: [:osx], + executable: true, + execute_cmd: ["${payload_path}"], + generate_options: { + '-f': "macho" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } } ], module_tests: [ diff --git a/spec/support/acceptance/session/php.rb b/spec/support/acceptance/session/php.rb index f36e791ede2e4..e3d88163c0230 100644 --- a/spec/support/acceptance/session/php.rb +++ b/spec/support/acceptance/session/php.rb @@ -1,6 +1,8 @@ require_relative './shared' module Acceptance::Session::Php + MALLEABLE_C2_FIXTURE_PATH = File.expand_path('../../../../../spec/file_fixtures/malleable_c2', __FILE__) + PHP_METERPRETER = { payloads: [ { @@ -17,6 +19,70 @@ module Acceptance::Session::Php MeterpreterDebugBuild: true } } + }, + { + name: "php/meterpreter_reverse_http", + extension: ".php", + platforms: [:osx, :linux, :windows], + execute_cmd: ["php", "${payload_path}"], + generate_options: { + '-f': "raw" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "php/meterpreter_reverse_http", + extension: ".php", + platforms: [:osx, :linux, :windows], + execute_cmd: ["php", "${payload_path}"], + generate_options: { + '-f': "raw" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } + }, + { + name: "php/meterpreter_reverse_https", + extension: ".php", + platforms: [:osx, :linux, :windows], + execute_cmd: ["php", "${payload_path}"], + generate_options: { + '-f': "raw" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "php/meterpreter_reverse_https", + extension: ".php", + platforms: [:osx, :linux, :windows], + execute_cmd: ["php", "${payload_path}"], + generate_options: { + '-f': "raw" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } } ], module_tests: [ diff --git a/spec/support/acceptance/session/python.rb b/spec/support/acceptance/session/python.rb index 5df5196572c81..b20303d774ba4 100644 --- a/spec/support/acceptance/session/python.rb +++ b/spec/support/acceptance/session/python.rb @@ -1,6 +1,8 @@ require_relative './shared' module Acceptance::Session::Python + MALLEABLE_C2_FIXTURE_PATH = File.expand_path('../../../../../spec/file_fixtures/malleable_c2', __FILE__) + PYTHON_METERPRETER = { payloads: [ { @@ -18,6 +20,74 @@ module Acceptance::Session::Python PythonMeterpreterDebug: true } } + }, + { + name: "python/meterpreter_reverse_http", + extension: ".py", + platforms: [:osx, :linux, :windows], + execute_cmd: ["python", "${payload_path}"], + generate_options: { + '-f': "raw" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + PythonMeterpreterDebug: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "python/meterpreter_reverse_http", + extension: ".py", + platforms: [:osx, :linux, :windows], + execute_cmd: ["python", "${payload_path}"], + generate_options: { + '-f': "raw" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + PythonMeterpreterDebug: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } + }, + { + name: "python/meterpreter_reverse_https", + extension: ".py", + platforms: [:osx, :linux, :windows], + execute_cmd: ["python", "${payload_path}"], + generate_options: { + '-f': "raw" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + PythonMeterpreterDebug: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "python/meterpreter_reverse_https", + extension: ".py", + platforms: [:osx, :linux, :windows], + execute_cmd: ["python", "${payload_path}"], + generate_options: { + '-f': "raw" + }, + datastore: { + global: {}, + module: { + MeterpreterTryToFork: false, + PythonMeterpreterDebug: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } } ], module_tests: [ diff --git a/spec/support/acceptance/session/windows_meterpreter.rb b/spec/support/acceptance/session/windows_meterpreter.rb index a223c0acb6c92..05642c73babe2 100644 --- a/spec/support/acceptance/session/windows_meterpreter.rb +++ b/spec/support/acceptance/session/windows_meterpreter.rb @@ -1,6 +1,8 @@ require_relative './shared' module Acceptance::Session::WindowsMeterpreter + MALLEABLE_C2_FIXTURE_PATH = File.expand_path('../../../../../spec/file_fixtures/malleable_c2', __FILE__) + WINDOWS_METERPRETER = { payloads: [ { @@ -38,6 +40,142 @@ module Acceptance::Session::WindowsMeterpreter MeterpreterDebugBuild: false } } + }, + { + name: "windows/meterpreter_reverse_http", + extension: ".exe", + platforms: [:windows], + execute_cmd: ["${payload_path}"], + executable: true, + generate_options: { + '-f': "exe" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "windows/meterpreter_reverse_http", + extension: ".exe", + platforms: [:windows], + execute_cmd: ["${payload_path}"], + executable: true, + generate_options: { + '-f': "exe" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } + }, + { + name: "windows/x64/meterpreter_reverse_http", + extension: ".exe", + platforms: [:windows], + execute_cmd: ["${payload_path}"], + executable: true, + generate_options: { + '-f': "exe" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: false, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "windows/x64/meterpreter_reverse_http", + extension: ".exe", + platforms: [:windows], + execute_cmd: ["${payload_path}"], + executable: true, + generate_options: { + '-f': "exe" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: false, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } + }, + { + name: "windows/meterpreter_reverse_https", + extension: ".exe", + platforms: [:windows], + execute_cmd: ["${payload_path}"], + executable: true, + generate_options: { + '-f': "exe" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "windows/meterpreter_reverse_https", + extension: ".exe", + platforms: [:windows], + execute_cmd: ["${payload_path}"], + executable: true, + generate_options: { + '-f': "exe" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: true, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } + }, + { + name: "windows/x64/meterpreter_reverse_https", + extension: ".exe", + platforms: [:windows], + execute_cmd: ["${payload_path}"], + executable: true, + generate_options: { + '-f': "exe" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: false, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'minimal_uris_headers.profile') + } + } + }, + { + name: "windows/x64/meterpreter_reverse_https", + extension: ".exe", + platforms: [:windows], + execute_cmd: ["${payload_path}"], + executable: true, + generate_options: { + '-f': "exe" + }, + datastore: { + global: {}, + module: { + MeterpreterDebugBuild: false, + MALLEABLEC2: File.join(MALLEABLE_C2_FIXTURE_PATH, 'base64_transforms.profile') + } + } } ], module_tests: [ From e98124650c49b5ca3caa9b6c2f3103d4f506f03b Mon Sep 17 00:00:00 2001 From: adfoster-r7 Date: Fri, 17 Jul 2026 10:31:19 +0100 Subject: [PATCH 2/2] Update malleable payload tests --- .../shared_meterpreter_acceptance.yml | 2 +- lib/msf/core/post/file.rb | 9 +++++--- spec/acceptance/meterpreter_spec.rb | 8 +++++-- .../lib/msf/core/payload/malleable_c2_spec.rb | 2 +- spec/support/acceptance/session/java.rb | 10 +++++++-- spec/support/acceptance/session/mettle.rb | 8 +++++++ spec/support/acceptance/session/php.rb | 22 ++++++++++--------- spec/support/acceptance/session/python.rb | 10 +++++++-- .../acceptance/session/windows_meterpreter.rb | 20 +++++++++++++---- 9 files changed, 66 insertions(+), 25 deletions(-) diff --git a/.github/workflows/shared_meterpreter_acceptance.yml b/.github/workflows/shared_meterpreter_acceptance.yml index 6d0a0e1b09432..a5ead47544831 100644 --- a/.github/workflows/shared_meterpreter_acceptance.yml +++ b/.github/workflows/shared_meterpreter_acceptance.yml @@ -97,7 +97,7 @@ jobs: runs-on: ${{ matrix.os }} - timeout-minutes: 180 + timeout-minutes: 50 env: RAILS_ENV: test diff --git a/lib/msf/core/post/file.rb b/lib/msf/core/post/file.rb index ae68df49fd789..5107d197d1fe4 100644 --- a/lib/msf/core/post/file.rb +++ b/lib/msf/core/post/file.rb @@ -879,12 +879,15 @@ def _read_file_meterpreter(file_name) fd = session.fs.file.new(file_name, 'rb') data = ''.b - data << fd.read - data << fd.read until fd.eof? + loop do + chunk = fd.read + break if chunk.nil? || chunk.empty? + + data << chunk + end data rescue EOFError - # Sometimes fd isn't marked EOF in time? data rescue ::Rex::Post::Meterpreter::RequestError => e print_error("Failed to open file: #{file_name}: #{e}") diff --git a/spec/acceptance/meterpreter_spec.rb b/spec/acceptance/meterpreter_spec.rb index f1d84fdb897a0..2f280a191ff04 100644 --- a/spec/acceptance/meterpreter_spec.rb +++ b/spec/acceptance/meterpreter_spec.rb @@ -332,8 +332,10 @@ def get_file_attachment_contents(path) meterpreter_config[:module_tests].each do |module_test| describe module_test[:name].to_s, focus: module_test[:focus] do + c2_profile = payload_config.dig(:datastore, :module, :MALLEABLEC2) + c2_suffix = c2_profile ? " (malleable c2: #{File.basename(c2_profile)})" : '' it( - "#{Acceptance::Session.current_platform}/#{meterpreter_runtime_name} meterpreter successfully opens a session for the #{payload_config[:name].inspect} payload and passes the #{module_test[:name].inspect} tests", + "#{Acceptance::Session.current_platform}/#{meterpreter_runtime_name} meterpreter successfully opens a session for the #{payload_config[:name].inspect} payload and passes the #{module_test[:name].inspect} tests#{c2_suffix}", if: ( # Run if ENV['SESSION'] = 'java php' etc Acceptance::Session.run_meterpreter?(meterpreter_config) && @@ -343,7 +345,9 @@ def get_file_attachment_contents(path) Acceptance::Session.supported_platform?(payload_config) && Acceptance::Session.supported_platform?(module_test) && # Skip tests that are explicitly skipped, or won't pass in the current environment - !Acceptance::Session.skipped_module_test?(module_test, allure_test_environment) + !Acceptance::Session.skipped_module_test?(module_test, allure_test_environment) && + # Skip module tests that the payload has explicitly opted out of + !Array(payload_config[:skip_module_tests]).include?(module_test[:name]) ), # test metadata - will appear in allure report module_test: module_test[:name] diff --git a/spec/lib/msf/core/payload/malleable_c2_spec.rb b/spec/lib/msf/core/payload/malleable_c2_spec.rb index 476ecc97222da..aa853be1399ea 100644 --- a/spec/lib/msf/core/payload/malleable_c2_spec.rb +++ b/spec/lib/msf/core/payload/malleable_c2_spec.rb @@ -29,7 +29,7 @@ it 'raises an exception' do expect { parser.parse('/nonexistent/path.profile') - }.to raise_error(Exception) + }.to raise_error(Errno::ENOENT) end end end diff --git a/spec/support/acceptance/session/java.rb b/spec/support/acceptance/session/java.rb index ae46039d12a59..457164fa17d09 100644 --- a/spec/support/acceptance/session/java.rb +++ b/spec/support/acceptance/session/java.rb @@ -22,6 +22,7 @@ module Acceptance::Session::Java }, { name: "java/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".jar", platforms: [:osx, :linux, :windows], execute_cmd: ["java", "-jar", "${payload_path}"], @@ -37,6 +38,7 @@ module Acceptance::Session::Java }, { name: "java/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".jar", platforms: [:osx, :linux, :windows], execute_cmd: ["java", "-jar", "${payload_path}"], @@ -52,8 +54,10 @@ module Acceptance::Session::Java }, { name: "java/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".jar", - platforms: [:osx, :linux, :windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [:osx, :linux], execute_cmd: ["java", "-jar", "${payload_path}"], generate_options: { '-f': "jar" @@ -67,8 +71,10 @@ module Acceptance::Session::Java }, { name: "java/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".jar", - platforms: [:osx, :linux, :windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [:osx, :linux], execute_cmd: ["java", "-jar", "${payload_path}"], generate_options: { '-f': "jar" diff --git a/spec/support/acceptance/session/mettle.rb b/spec/support/acceptance/session/mettle.rb index a1c9017047551..ecdf25d3acb9c 100644 --- a/spec/support/acceptance/session/mettle.rb +++ b/spec/support/acceptance/session/mettle.rb @@ -41,6 +41,7 @@ module Acceptance::Session::Mettle }, { name: "linux/x64/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: "", platforms: [:linux], executable: true, @@ -59,6 +60,7 @@ module Acceptance::Session::Mettle }, { name: "linux/x64/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: "", platforms: [:linux], executable: true, @@ -77,6 +79,7 @@ module Acceptance::Session::Mettle }, { name: "osx/x64/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: "", platforms: [:osx], executable: true, @@ -95,6 +98,7 @@ module Acceptance::Session::Mettle }, { name: "osx/x64/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: "", platforms: [:osx], executable: true, @@ -113,6 +117,7 @@ module Acceptance::Session::Mettle }, { name: "linux/x64/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: "", platforms: [:linux], executable: true, @@ -131,6 +136,7 @@ module Acceptance::Session::Mettle }, { name: "linux/x64/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: "", platforms: [:linux], executable: true, @@ -149,6 +155,7 @@ module Acceptance::Session::Mettle }, { name: "osx/x64/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: "", platforms: [:osx], executable: true, @@ -167,6 +174,7 @@ module Acceptance::Session::Mettle }, { name: "osx/x64/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: "", platforms: [:osx], executable: true, diff --git a/spec/support/acceptance/session/php.rb b/spec/support/acceptance/session/php.rb index e3d88163c0230..d9b5f222f2f1d 100644 --- a/spec/support/acceptance/session/php.rb +++ b/spec/support/acceptance/session/php.rb @@ -22,6 +22,7 @@ module Acceptance::Session::Php }, { name: "php/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".php", platforms: [:osx, :linux, :windows], execute_cmd: ["php", "${payload_path}"], @@ -38,6 +39,7 @@ module Acceptance::Session::Php }, { name: "php/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".php", platforms: [:osx, :linux, :windows], execute_cmd: ["php", "${payload_path}"], @@ -54,8 +56,10 @@ module Acceptance::Session::Php }, { name: "php/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".php", - platforms: [:osx, :linux, :windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [:osx, :linux], execute_cmd: ["php", "${payload_path}"], generate_options: { '-f': "raw" @@ -70,8 +74,10 @@ module Acceptance::Session::Php }, { name: "php/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".php", - platforms: [:osx, :linux, :windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [:osx, :linux], execute_cmd: ["php", "${payload_path}"], generate_options: { '-f': "raw" @@ -167,22 +173,18 @@ module Acceptance::Session::Php skipped: false, lines: { linux: { - known_failures: [ - "[-] FAILED: should read the binary data we just wrote" - ] + known_failures: [] }, osx: { - known_failures: [ - "[-] FAILED: should read the binary data we just wrote" - ] + known_failures: [] }, windows: { known_failures: [ "[-] [should delete a symbolic link target] FAILED: should delete a symbolic link target", - "[-] [should delete a symbolic link target] Exception: Rex::Post::Meterpreter::RequestError: stdapi_fs_delete_dir: Operation failed: 1", - "[-] FAILED: should read the binary data we just wrote" + "[-] [should delete a symbolic link target] Exception: Rex::Post::Meterpreter::RequestError: stdapi_fs_delete_dir: Operation failed: 1" ] } + } }, { diff --git a/spec/support/acceptance/session/python.rb b/spec/support/acceptance/session/python.rb index b20303d774ba4..2e218bafddc08 100644 --- a/spec/support/acceptance/session/python.rb +++ b/spec/support/acceptance/session/python.rb @@ -23,6 +23,7 @@ module Acceptance::Session::Python }, { name: "python/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".py", platforms: [:osx, :linux, :windows], execute_cmd: ["python", "${payload_path}"], @@ -40,6 +41,7 @@ module Acceptance::Session::Python }, { name: "python/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".py", platforms: [:osx, :linux, :windows], execute_cmd: ["python", "${payload_path}"], @@ -57,8 +59,10 @@ module Acceptance::Session::Python }, { name: "python/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".py", - platforms: [:osx, :linux, :windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [:osx, :linux], execute_cmd: ["python", "${payload_path}"], generate_options: { '-f': "raw" @@ -74,8 +78,10 @@ module Acceptance::Session::Python }, { name: "python/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".py", - platforms: [:osx, :linux, :windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [:osx, :linux], execute_cmd: ["python", "${payload_path}"], generate_options: { '-f': "raw" diff --git a/spec/support/acceptance/session/windows_meterpreter.rb b/spec/support/acceptance/session/windows_meterpreter.rb index 05642c73babe2..9796820ca1f50 100644 --- a/spec/support/acceptance/session/windows_meterpreter.rb +++ b/spec/support/acceptance/session/windows_meterpreter.rb @@ -43,6 +43,7 @@ module Acceptance::Session::WindowsMeterpreter }, { name: "windows/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".exe", platforms: [:windows], execute_cmd: ["${payload_path}"], @@ -60,6 +61,7 @@ module Acceptance::Session::WindowsMeterpreter }, { name: "windows/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".exe", platforms: [:windows], execute_cmd: ["${payload_path}"], @@ -77,6 +79,7 @@ module Acceptance::Session::WindowsMeterpreter }, { name: "windows/x64/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".exe", platforms: [:windows], execute_cmd: ["${payload_path}"], @@ -94,6 +97,7 @@ module Acceptance::Session::WindowsMeterpreter }, { name: "windows/x64/meterpreter_reverse_http", + skip_module_tests: ['post/test/socket_channels'], extension: ".exe", platforms: [:windows], execute_cmd: ["${payload_path}"], @@ -111,8 +115,10 @@ module Acceptance::Session::WindowsMeterpreter }, { name: "windows/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".exe", - platforms: [:windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [], execute_cmd: ["${payload_path}"], executable: true, generate_options: { @@ -128,8 +134,10 @@ module Acceptance::Session::WindowsMeterpreter }, { name: "windows/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".exe", - platforms: [:windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [], execute_cmd: ["${payload_path}"], executable: true, generate_options: { @@ -145,8 +153,10 @@ module Acceptance::Session::WindowsMeterpreter }, { name: "windows/x64/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".exe", - platforms: [:windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [], execute_cmd: ["${payload_path}"], executable: true, generate_options: { @@ -162,8 +172,10 @@ module Acceptance::Session::WindowsMeterpreter }, { name: "windows/x64/meterpreter_reverse_https", + skip_module_tests: ['post/test/socket_channels'], extension: ".exe", - platforms: [:windows], + # TODO: HTTPS payloads broken on Windows environments + platforms: [], execute_cmd: ["${payload_path}"], executable: true, generate_options: {