From 3b4bc9f446578bc0dfd2c5eb469f87ef83394040 Mon Sep 17 00:00:00 2001 From: Nayeraneru Date: Sun, 16 Aug 2026 19:06:58 +0300 Subject: [PATCH 1/8] Demonstration of 2 modules across one shared definition file --- data/vuln_envs/activemq.yml | 5 +++++ .../misc/apache_activemq_rce_cve_2023_46604.rb | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/data/vuln_envs/activemq.yml b/data/vuln_envs/activemq.yml index a0d78e2ee5fee..70a2d6605f54f 100644 --- a/data/vuln_envs/activemq.yml +++ b/data/vuln_envs/activemq.yml @@ -7,6 +7,11 @@ variants: image: docker.io/apache/activemq-classic:5.18.6 default: true + #used by exploit/multi/misc/apache_activemq_rce_cve_2023_46604 + - name: "5.18.2" + version: "5.18.2" + image: docker.io/dinifarb/activemq:5.18.2 + shared: ports: web: 8161 diff --git a/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb b/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb index cb37b0ba5b154..da7fd10f86afc 100644 --- a/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb +++ b/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb @@ -67,6 +67,23 @@ def initialize(info = {}) # The maximum time in seconds to wait for a session. 'WfsDelay' => 30 }, + 'VulnerableEnvironment' => { + 'definition' => 'activemq', + 'default_variant' => '5.18.2', + 'port_mapping' => { 61616 => 'RPORT' }, + 'overrides' => { + 'health_check' => { + 'type' => 'tcp' + }, + 'ci' => { + 'exploit' => { + 'options' => { + 'TARGET' => 1 + } + } + } + } + }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], From c76fd8da3a5f65c9aae9f0b1e6d9264f5d2fdf02 Mon Sep 17 00:00:00 2001 From: Nayeraneru Date: Sun, 16 Aug 2026 20:01:30 +0300 Subject: [PATCH 2/8] Doc Update --- data/vuln_envs/activemq.yml | 1 + docs/test_env/reference_modules.md | 16 +++++++++++++++- .../misc/apache_activemq_rce_cve_2023_46604.rb | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/data/vuln_envs/activemq.yml b/data/vuln_envs/activemq.yml index 70a2d6605f54f..08fca80a64995 100644 --- a/data/vuln_envs/activemq.yml +++ b/data/vuln_envs/activemq.yml @@ -2,6 +2,7 @@ name: activemq description: Apache ActiveMQ Classic with Jolokia API variants: + #used by exploit/multi/http/apache_activemq_jolokia_rce - name: "5.18.6" version: "5.18.6" image: docker.io/apache/activemq-classic:5.18.6 diff --git a/docs/test_env/reference_modules.md b/docs/test_env/reference_modules.md index b72280be997c8..2162bd7304809 100644 --- a/docs/test_env/reference_modules.md +++ b/docs/test_env/reference_modules.md @@ -33,4 +33,18 @@ - **Docker Image:** `eystsen/vulnerablewordpress` - **Credentials:** admin / admin - **Exploit Context:** Authenticated admin access; uploads PHP shell via theme/plugin editor -- **Provisioning:** This image does **not** start ready-to-use. The Dockerfile configures `wp-config.php` to point at a `wordpress` database but never creates the schema or an admin account — WordPress boots straight into the install wizard (`/wp-admin/install.php`), and stays there indefinitely with no admin/admin login until the wizard is submitted. `wordpress.yml` now defines a `provision` step (`type: http_post`, submits `install.php?step=2` with the credentials from `credentials.default`) that runs once the base health check passes, followed by the `verify` check above before the environment is registered as ready. See `04-environment-schema.md` for the general `provision`/`verify` schema this relies on. \ No newline at end of file +- **Provisioning:** This image does **not** start ready-to-use. The Dockerfile configures `wp-config.php` to point at a `wordpress` database but never creates the schema or an admin account — WordPress boots straight into the install wizard (`/wp-admin/install.php`), and stays there indefinitely with no admin/admin login until the wizard is submitted. `wordpress.yml` now defines a `provision` step (`type: http_post`, submits `install.php?step=2` with the credentials from `credentials.default`) that runs once the base health check passes, followed by the `verify` check above before the environment is registered as ready. See `04-environment-schema.md` for the general `provision`/`verify` schema this relies on. + +--- + +## Module 3: Apache ActiveMQ OpenWire RCE (CVE-2023-46604) +- **Path:** `exploit/multi/misc/apache_activemq_rce_cve_2023_46604` +- **Type:** Java message broker (raw OpenWire protocol, not HTTP) +- **Port:** 61616 (broker) — +- **Health Check:** Overrides `activemq.yml`'s shared HTTP check with a `tcp` check on this module's own RPORT-mapped port (61616). The shared HTTP check (against the Jetty web console) fails against this variant's image with `EOFError` - not a broken image, but **AMQ-8018**: since ActiveMQ 5.16.0, the web console binds to `127.0.0.1` *inside* the container by default and is unreachable externally unless an image explicitly patches it (confirmed: Module 1's official `apache/activemq-classic` image doesn't have this problem, so it's specific to how this community image leaves the stock config). Since this module never touches the web console anyway (pure OpenWire on 61616, which binds `0.0.0.0` by default), a TCP check on its own port is both accurate and sidesteps the issue entirely - a real example of the schema's "Module override" case (different health check type, same profile). +- **Why this module specifically:** it's the first real proof that `activemq.yml` works as a genuine shared definition across independent modules, not just independent files that happen to use the same schema. Two unrelated CVEs, two different attack surfaces (HTTP/Jolokia vs. raw OpenWire), one definition file. +- **VulnerableEnvironment Definition:** `activemq` (same file as Module 1 — nothing duplicated: image family, credentials, health check, and `ci.exploit`'s recommended payload are all inherited unchanged) +- **Docker Image:** `dinifarb/activemq:5.18.2` +- **Credentials:** none required — this CVE is unauthenticated +- **Exploit Context:** Unauthenticated; sends crafted OpenWire packet that loads attacker-hosted Spring XML config. Requires TARGET => 1 (Linux) override — default target is Windows. + diff --git a/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb b/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb index da7fd10f86afc..475a3698a536a 100644 --- a/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb +++ b/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb @@ -82,7 +82,7 @@ def initialize(info = {}) } } } - } + } }, 'Notes' => { 'Stability' => [CRASH_SAFE], From f56640af7864a6488c491f8bce18440b16964739 Mon Sep 17 00:00:00 2001 From: Nayeraneru Date: Sun, 16 Aug 2026 23:28:49 +0300 Subject: [PATCH 3/8] Profile demonstrated --- data/vuln_envs/activemq.yml | 10 +++++++--- docs/test_env/reference_modules.md | 8 +++++--- .../multi/misc/apache_activemq_rce_cve_2023_46604.rb | 4 +--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/data/vuln_envs/activemq.yml b/data/vuln_envs/activemq.yml index 08fca80a64995..6ac9ce666eff1 100644 --- a/data/vuln_envs/activemq.yml +++ b/data/vuln_envs/activemq.yml @@ -2,13 +2,13 @@ name: activemq description: Apache ActiveMQ Classic with Jolokia API variants: - #used by exploit/multi/http/apache_activemq_jolokia_rce + #Used by exploit/multi/http/apache_activemq_jolokia_rce - name: "5.18.6" version: "5.18.6" image: docker.io/apache/activemq-classic:5.18.6 default: true - #used by exploit/multi/misc/apache_activemq_rce_cve_2023_46604 + #Used by exploit/multi/misc/apache_activemq_rce_cve_2023_46604 - name: "5.18.2" version: "5.18.2" image: docker.io/dinifarb/activemq:5.18.2 @@ -54,4 +54,8 @@ shared: profiles: default: - description: Standard Apache ActiveMQ with web console and broker \ No newline at end of file + description: Standard ActiveMQ; web console reachable via HTTP/Jolokia. + broker-only: + description: Web console not assumed reachable; only the broker port is health-checked + health_check: + type: tcp \ No newline at end of file diff --git a/docs/test_env/reference_modules.md b/docs/test_env/reference_modules.md index 2162bd7304809..4071878e26f6b 100644 --- a/docs/test_env/reference_modules.md +++ b/docs/test_env/reference_modules.md @@ -12,7 +12,8 @@ ## Module 1: Apache ActiveMQ Jolokia RCE (Mentor Suggested) - **Path:** `exploit/multi/http/apache_activemq_jolokia_rce` - **Type:** Java web application (JMX-over-HTTP) -- **Ports:** 8161 (web console / Jolokia API), 61616 (OpenWire broker) +- **Ports:** 8161 (web console / Jolokia API) +- **Profile:** `default` - **Health Check:** HTTP GET `/api/jolokia/` expecting 200, or GET `/` expecting 200 - **Why:** h00die suggested PR #21497. Has a verified Docker one-liner. Real-world CVE-2026-34197. - **VulnerableEnvironment Definition:** `activemq` @@ -40,8 +41,9 @@ ## Module 3: Apache ActiveMQ OpenWire RCE (CVE-2023-46604) - **Path:** `exploit/multi/misc/apache_activemq_rce_cve_2023_46604` - **Type:** Java message broker (raw OpenWire protocol, not HTTP) -- **Port:** 61616 (broker) — -- **Health Check:** Overrides `activemq.yml`'s shared HTTP check with a `tcp` check on this module's own RPORT-mapped port (61616). The shared HTTP check (against the Jetty web console) fails against this variant's image with `EOFError` - not a broken image, but **AMQ-8018**: since ActiveMQ 5.16.0, the web console binds to `127.0.0.1` *inside* the container by default and is unreachable externally unless an image explicitly patches it (confirmed: Module 1's official `apache/activemq-classic` image doesn't have this problem, so it's specific to how this community image leaves the stock config). Since this module never touches the web console anyway (pure OpenWire on 61616, which binds `0.0.0.0` by default), a TCP check on its own port is both accurate and sidesteps the issue entirely - a real example of the schema's "Module override" case (different health check type, same profile). +- **Port:** 61616 (broker) +- **Profile:** `broker-only` +- **Health Check:** Uses activemq.yml's broker-only profile (tcp on 61616). The default HTTP check fails against this image due to AMQ-8018 (web console binds 127.0.0.1 since 5.16.0). Since the module only needs OpenWire, a TCP profile is accurate and matches the schema guidance to create a new profile when the health check type differs. - **Why this module specifically:** it's the first real proof that `activemq.yml` works as a genuine shared definition across independent modules, not just independent files that happen to use the same schema. Two unrelated CVEs, two different attack surfaces (HTTP/Jolokia vs. raw OpenWire), one definition file. - **VulnerableEnvironment Definition:** `activemq` (same file as Module 1 — nothing duplicated: image family, credentials, health check, and `ci.exploit`'s recommended payload are all inherited unchanged) - **Docker Image:** `dinifarb/activemq:5.18.2` diff --git a/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb b/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb index 475a3698a536a..4151956810144 100644 --- a/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb +++ b/modules/exploits/multi/misc/apache_activemq_rce_cve_2023_46604.rb @@ -70,11 +70,9 @@ def initialize(info = {}) 'VulnerableEnvironment' => { 'definition' => 'activemq', 'default_variant' => '5.18.2', + 'profile' => 'broker-only', 'port_mapping' => { 61616 => 'RPORT' }, 'overrides' => { - 'health_check' => { - 'type' => 'tcp' - }, 'ci' => { 'exploit' => { 'options' => { From cf1cdff1dfa0312c772e0cd8fae89190a654a2b3 Mon Sep 17 00:00:00 2001 From: Nayeraneru Date: Wed, 19 Aug 2026 01:58:22 +0300 Subject: [PATCH 4/8] Add new module(http_version) --- data/vuln_envs/httpd.yml | 28 +++++++++++++++++++ .../auxiliary/scanner/http/http_version.rb | 7 ++++- plugins/test_env.rb | 9 +++--- 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 data/vuln_envs/httpd.yml diff --git a/data/vuln_envs/httpd.yml b/data/vuln_envs/httpd.yml new file mode 100644 index 0000000000000..b4159b5d7c401 --- /dev/null +++ b/data/vuln_envs/httpd.yml @@ -0,0 +1,28 @@ +name: httpd +description: Apache HTTP Server for scanner testing + +variants: + - name: "2.4.57" + version: "2.4.57" + image: docker.io/library/httpd:2.4.57 + default: true + +shared: + ports: + http: 80 + + health_check: + type: http + path: / + expected_status: 200 + interval: 2 + timeout: 2 + retries: 10 + + ci: + validation: + expected_session: false + +profiles: + default: + description: Standard Apache HTTP Server \ No newline at end of file diff --git a/modules/auxiliary/scanner/http/http_version.rb b/modules/auxiliary/scanner/http/http_version.rb index 0c6ebf79a9e8b..f97b7f70d14ec 100644 --- a/modules/auxiliary/scanner/http/http_version.rb +++ b/modules/auxiliary/scanner/http/http_version.rb @@ -16,7 +16,12 @@ def initialize 'Name' => 'HTTP Version Detection', 'Description' => 'Display version information about each system.', 'Author' => 'hdm', - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE, + 'VulnerableEnvironment' => { + 'definition' => 'httpd', + 'default_variant' => '2.4.57', + 'port_mapping' => {80 => 'RPORT'} + } ) register_wmap_options({ diff --git a/plugins/test_env.rb b/plugins/test_env.rb index 7def551916f68..a173079e706a8 100644 --- a/plugins/test_env.rb +++ b/plugins/test_env.rb @@ -1682,7 +1682,7 @@ def build_resolve_environment(mod, env, options) # this specific image/variant), apply it now, before the container is # even started, so it's reflected if the user runs 'show options'. recommended_payload = config.dig('ci', 'exploit', 'payload') - if recommended_payload && mod.datastore['PAYLOAD'] != recommended_payload + if recommended_payload && mod.options.include?('PAYLOAD') && mod.datastore['PAYLOAD'] != recommended_payload print_status("Setting recommended payload for this environment: #{recommended_payload}") mod.datastore['PAYLOAD'] = recommended_payload end @@ -2030,7 +2030,7 @@ def cmd_test_env_exec(args) config = loader.resolve(env_meta.definition, target.env_version, env_meta.profile, env_meta.overrides) rescue nil ci_exploit = config&.dig('ci', 'exploit') || {} - if ci_exploit['payload'] + if ci_exploit['payload'] && mod.options.include?('PAYLOAD') print_status("Setting recommended payload for this environment: #{ci_exploit['payload']}") driver.run_single("set PAYLOAD #{ci_exploit['payload']}") end @@ -2067,8 +2067,9 @@ def cmd_test_env_exec(args) # generation, session creation, and all success/failure messaging # come from that well-tested path rather than being reimplemented # here. See the design note above for why this matters. - print_status("Executing: #{target.exploit_command}") - driver.run_single("exploit") + action = mod.type == 'auxiliary' ? 'run' : 'exploit' + print_status("Executing: #{target.exploit_command.sub(/^exploit/, action)}") + driver.run_single(action) rescue => e print_error("test_env exec failed: #{e.class} - #{e.message}") elog("test_env exec error: #{e.class} - #{e.message}") From dc1e936704b9212f6e447c50db8e746f8d4f3e35 Mon Sep 17 00:00:00 2001 From: Nayeraneru Date: Wed, 19 Aug 2026 02:26:32 +0300 Subject: [PATCH 5/8] Add more module for httpd definition --- modules/auxiliary/scanner/http/http_header.rb | 5 +++++ modules/auxiliary/scanner/http/robots_txt.rb | 7 ++++++- plugins/test_env.rb | 12 ++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/scanner/http/http_header.rb b/modules/auxiliary/scanner/http/http_header.rb index 6633cac2c928c..6714f3b0820e3 100644 --- a/modules/auxiliary/scanner/http/http_header.rb +++ b/modules/auxiliary/scanner/http/http_header.rb @@ -26,6 +26,11 @@ def initialize(info = {}) 'Reliability' => UNKNOWN_RELIABILITY, 'Stability' => UNKNOWN_STABILITY, 'SideEffects' => UNKNOWN_SIDE_EFFECTS + }, + 'VulnerableEnvironment' => { + 'definition' => 'httpd', + 'default_variant' => '2.4.57', + 'port_mapping' => {80 => 'RPORT'} } ) ) diff --git a/modules/auxiliary/scanner/http/robots_txt.rb b/modules/auxiliary/scanner/http/robots_txt.rb index 2f354ab2f77ab..baf30db2a5987 100644 --- a/modules/auxiliary/scanner/http/robots_txt.rb +++ b/modules/auxiliary/scanner/http/robots_txt.rb @@ -17,7 +17,12 @@ def initialize 'Name' => 'HTTP Robots.txt Content Scanner', 'Description' => 'Detect robots.txt files and analyze its content', 'Author' => ['et'], - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE, + 'VulnerableEnvironment' => { + 'definition' => 'httpd', + 'default_variant' => '2.4.57', + 'port_mapping' => {80 => 'RPORT'} + } ) register_options( diff --git a/plugins/test_env.rb b/plugins/test_env.rb index a173079e706a8..b68cdb5e3e327 100644 --- a/plugins/test_env.rb +++ b/plugins/test_env.rb @@ -2057,11 +2057,15 @@ def cmd_test_env_exec(args) # and every subsequent run fails until someone manually finds and # kills the stale process. Picking a fresh free port each time # removes the collision entirely rather than requiring cleanup. - %w[SRVPORT FETCH_SRVPORT].each do |opt| - free_port = free_local_port - driver.run_single("set #{opt} #{free_port}") + if mod.type != 'auxiliary' + %w[SRVPORT FETCH_SRVPORT].each do |opt| + if mod.options.include?(opt) + free_port = free_local_port + driver.run_single("set #{opt} #{free_port}") + end + end end - + # --- Step E: run it. driver.run_single("exploit") reuses the # console's own exploit-execution path - AutoCheck, payload # generation, session creation, and all success/failure messaging From c16451f07239ce006f03fa09efc050699484a920 Mon Sep 17 00:00:00 2001 From: Nayeraneru Date: Wed, 19 Aug 2026 02:29:20 +0300 Subject: [PATCH 6/8] minor fix --- modules/auxiliary/scanner/http/http_header.rb | 19 ++++++------- modules/auxiliary/scanner/http/robots_txt.rb | 28 +++++++++---------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/modules/auxiliary/scanner/http/http_header.rb b/modules/auxiliary/scanner/http/http_header.rb index 6714f3b0820e3..dd873aa17b902 100644 --- a/modules/auxiliary/scanner/http/http_header.rb +++ b/modules/auxiliary/scanner/http/http_header.rb @@ -28,9 +28,9 @@ def initialize(info = {}) 'SideEffects' => UNKNOWN_SIDE_EFFECTS }, 'VulnerableEnvironment' => { - 'definition' => 'httpd', + 'definition' => 'httpd', 'default_variant' => '2.4.57', - 'port_mapping' => {80 => 'RPORT'} + 'port_mapping' => { 80 => 'RPORT' } } ) ) @@ -68,9 +68,8 @@ def run_host(ip) end # Header Names are case insensitive so convert them to upcase - headers_uppercase = headers.inject({}) do |hash, keys| + headers_uppercase = headers.each_with_object({}) do |keys, hash| hash[keys[0].upcase] = keys[1] - hash end ignored_headers.each do |h| @@ -81,18 +80,18 @@ def run_host(ip) end headers_uppercase.to_a.compact.sort - counter = 0; + counter = 0 headers_uppercase.each do |h| header_string = "#{h[0]}: #{h[1]}" print_good "#{peer}: #{header_string}" report_note( - :type => "http.header.#{rport}.#{counter}", - :data => { :header_string => header_string }, - :host => ip, - :port => rport + type: "http.header.#{rport}.#{counter}", + data: { header_string: header_string }, + host: ip, + port: rport ) - counter = counter + 1 + counter += 1 end if counter == 0 print_warning "#{peer}: all detected headers are defined in IGN_HEADER and were ignored " diff --git a/modules/auxiliary/scanner/http/robots_txt.rb b/modules/auxiliary/scanner/http/robots_txt.rb index baf30db2a5987..699258e6ddc32 100644 --- a/modules/auxiliary/scanner/http/robots_txt.rb +++ b/modules/auxiliary/scanner/http/robots_txt.rb @@ -19,15 +19,15 @@ def initialize 'Author' => ['et'], 'License' => MSF_LICENSE, 'VulnerableEnvironment' => { - 'definition' => 'httpd', + 'definition' => 'httpd', 'default_variant' => '2.4.57', - 'port_mapping' => {80 => 'RPORT'} + 'port_mapping' => { 80 => 'RPORT' } } ) register_options( [ - OptString.new('PATH', [ true, "The test path to find robots.txt file", '/']), + OptString.new('PATH', [ true, 'The test path to find robots.txt file', '/']), ] ) @@ -45,15 +45,15 @@ def run_host(target_host) res = send_request_raw({ 'uri' => turl, 'method' => 'GET', - 'version' => '1.0', + 'version' => '1.0' }, 10) - if not res + if !res print_error("[#{target_host}] #{tpath}robots.txt - No response") return end - if not res.body.include?("llow:") + if !res.body.include?('llow:') vprint_status("[#{target_host}] #{tpath}robots.txt - Doesn't contain \"llow:\"") return end @@ -62,20 +62,20 @@ def run_host(target_host) print_good("Contents of Robots.txt:\n#{res.body}") # short url regex - aregex = /llow:[ ]{0,2}(.*?)$/i + aregex = /llow: {0,2}(.*?)$/i result = res.body.scan(aregex).flatten.map { |s| s.strip }.uniq vprint_status("[#{target_host}] #{tpath}robots.txt - #{result.join(', ')}") result.each do |u| report_note( - :host => target_host, - :port => rport, - :proto => 'tcp', - :sname => (ssl ? 'https' : 'http'), - :type => 'ROBOTS_TXT', - :data => { :file => u }, - :update => :unique_data + host: target_host, + port: rport, + proto: 'tcp', + sname: (ssl ? 'https' : 'http'), + type: 'ROBOTS_TXT', + data: { file: u }, + update: :unique_data ) end rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout From 1c9d0800f08211c406e280c4d25e9638aa9de2f7 Mon Sep 17 00:00:00 2001 From: Nayeraneru Date: Wed, 19 Aug 2026 06:23:35 +0300 Subject: [PATCH 7/8] Adding ssh definition and module --- data/vuln_envs/openssh.yml | 32 ++++++++++++++++++++ modules/auxiliary/scanner/ssh/ssh_version.rb | 7 ++++- plugins/test_env.rb | 28 +++++++++++------ 3 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 data/vuln_envs/openssh.yml diff --git a/data/vuln_envs/openssh.yml b/data/vuln_envs/openssh.yml new file mode 100644 index 0000000000000..31ed1fa0b3973 --- /dev/null +++ b/data/vuln_envs/openssh.yml @@ -0,0 +1,32 @@ +name: openssh +description: OpenSSH server for SSH scanner testing + +variants: + - name: "7.2" + version: "7.2p2" + image: docker.io/rastasheep/ubuntu-sshd:16.04 + default: true + +shared: + ports: + ssh: 22 + + credentials: + default: + username: root + password: root + + # TCP check is enough: if port 22 accepts a connection, SSH is ready + health_check: + type: tcp + interval: 2 + timeout: 2 + retries: 10 + + ci: + validation: + expected_session: false + +profiles: + default: + description: Standard OpenSSH server \ No newline at end of file diff --git a/modules/auxiliary/scanner/ssh/ssh_version.rb b/modules/auxiliary/scanner/ssh/ssh_version.rb index 3129572b4d25c..0e2d3ec027d2d 100644 --- a/modules/auxiliary/scanner/ssh/ssh_version.rb +++ b/modules/auxiliary/scanner/ssh/ssh_version.rb @@ -26,7 +26,12 @@ def initialize 'Daniel van Eeden ', # original author 'h00die' # algorithms enhancements ], - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE, + 'VulnerableEnvironment' => { + 'definition' => 'openssh', + 'default_variant' => '7.2', + 'port_mapping' => {22 => 'RPORT'} + } ) register_options( diff --git a/plugins/test_env.rb b/plugins/test_env.rb index b68cdb5e3e327..eaf1f969d20a4 100644 --- a/plugins/test_env.rb +++ b/plugins/test_env.rb @@ -1595,11 +1595,13 @@ def cmd_test_env_build(args) # Step 17: apply datastore to the active module datastore.each do |key, value| - mod.datastore[key] = value + if mod.options.include?(key) + mod.datastore[key] = value + end end # Step 18: display results to user - build_display_results(env_id, config, datastore) + build_display_results(env_id, config, datastore, mod) rescue PortAllocator::NoPortsAvailable => e print_error("No available ports: #{e.message}") @@ -1870,16 +1872,18 @@ def build_register_environment(runtime, container_id, mod, variant, config, allo end # Step 18: Display build results and suggested exploit command. -def build_display_results(env_id, config, datastore) +def build_display_results(env_id, config, datastore, mod) print_good("Environment ready.") print_status("Environment ID: #{env_id}") - datastore.each do |key, value| + + applicable = datastore.select { |k, _v| mod.options.include?(k) } + applicable.each do |key, value| print_status(" #{key.ljust(12)} => #{value}") end - - env = self.class.registry.get(env_id) - print_status("Suggested: #{env.exploit_command}") + action = mod.type == 'auxiliary' ? 'run' : 'exploit' + opts = applicable.map { |k, v| "#{k}=#{v}" }.join(' ') + print_status("Suggested: #{action} #{opts}") end def cmd_test_env_help print_line("Usage: test_env ") @@ -2045,8 +2049,12 @@ def cmd_test_env_exec(args) # RPORT/credentials by hand - single source of truth, and it's the # exact same hash 'test_env build' already showed the user under # "Suggested:", so what runs here always matches what was printed. + applied = {} target.datastore.each do |key, value| - driver.run_single("set #{key} #{value}") + if mod.options.include?(key) + driver.run_single("set #{key} #{value}") + applied[key] = value + end end # --- Step D.5: avoid Rex::BindFailed from stale listeners on @@ -2062,6 +2070,7 @@ def cmd_test_env_exec(args) if mod.options.include?(opt) free_port = free_local_port driver.run_single("set #{opt} #{free_port}") + applied[opt] = free_port end end end @@ -2072,7 +2081,8 @@ def cmd_test_env_exec(args) # come from that well-tested path rather than being reimplemented # here. See the design note above for why this matters. action = mod.type == 'auxiliary' ? 'run' : 'exploit' - print_status("Executing: #{target.exploit_command.sub(/^exploit/, action)}") + opts = applied.map { |k, v| "#{k}=#{v}" }.join(' ') + print_status("Executing: #{action} #{opts}") driver.run_single(action) rescue => e print_error("test_env exec failed: #{e.class} - #{e.message}") From 2785de16e87cf3ed3493531edadf467ae0cf5c13 Mon Sep 17 00:00:00 2001 From: Nayeraneru Date: Thu, 20 Aug 2026 14:27:18 +0300 Subject: [PATCH 8/8] Use option presence instead of module type to guard SRVPORT/FETCH_SRVPORT allocation --- modules/auxiliary/scanner/ssh/ssh_version.rb | 6 +++--- plugins/test_env.rb | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/modules/auxiliary/scanner/ssh/ssh_version.rb b/modules/auxiliary/scanner/ssh/ssh_version.rb index 0e2d3ec027d2d..a92b2afa54a45 100644 --- a/modules/auxiliary/scanner/ssh/ssh_version.rb +++ b/modules/auxiliary/scanner/ssh/ssh_version.rb @@ -28,10 +28,10 @@ def initialize ], 'License' => MSF_LICENSE, 'VulnerableEnvironment' => { - 'definition' => 'openssh', + 'definition' => 'openssh', 'default_variant' => '7.2', - 'port_mapping' => {22 => 'RPORT'} - } + 'port_mapping' => { 22 => 'RPORT' } + } ) register_options( diff --git a/plugins/test_env.rb b/plugins/test_env.rb index eaf1f969d20a4..83cf6989fd192 100644 --- a/plugins/test_env.rb +++ b/plugins/test_env.rb @@ -2065,13 +2065,11 @@ def cmd_test_env_exec(args) # and every subsequent run fails until someone manually finds and # kills the stale process. Picking a fresh free port each time # removes the collision entirely rather than requiring cleanup. - if mod.type != 'auxiliary' - %w[SRVPORT FETCH_SRVPORT].each do |opt| - if mod.options.include?(opt) - free_port = free_local_port - driver.run_single("set #{opt} #{free_port}") - applied[opt] = free_port - end + %w[SRVPORT FETCH_SRVPORT].each do |opt| + if mod.options.include?(opt) + free_port = free_local_port + driver.run_single("set #{opt} #{free_port}") + applied[opt] = free_port end end