MCP server support for "dangerous" actions & new tools#21632
MCP server support for "dangerous" actions & new tools#21632cdelafuente-r7 wants to merge 10 commits into
Conversation
- module_execute, module_check, module_results, running_stats, session_list, session_stop, session_read, session_write
| default: 0 | ||
| } | ||
| }, | ||
| required: [:workspace] |
There was a problem hiding this comment.
Removing workspace from required since it defaults to the default workspace if not provided. This also applies to the other DB tools.
4125e03 to
8b0ebbb
Compare
| {"status" => "ready"} | ||
| else | ||
| error(404, "Results not found for module instance #{uuid}") | ||
| error(500, "Results not found for module instance #{uuid}") |
There was a problem hiding this comment.
It is an application level error and not an HTTP error (cannot find the requested resource).
| res.message = e.http_msg | ||
| rescue ::StandardError => e | ||
| elog('Unknown Exception', error: e) | ||
| rescue ::StandardError, ::ScriptError => e |
There was a problem hiding this comment.
::ScriptError is there to catch any ::NotImplementedError (e.g. check method not implemented)
There was a problem hiding this comment.
Let's see if we can make the run_simple changes work here too
I think it may also need updated here
metasploit-framework/lib/msf/core/exploit/local.rb
Lines 22 to 24 in 1617b3e
| mod.init_ui(nil, nil) | ||
| end | ||
|
|
||
| run_uuid = Rex::Text.rand_text_alphanumeric(24) |
There was a problem hiding this comment.
Investigate using a real UUID for this
| 'RunAsJob' => true, | ||
| 'Options' => opts | ||
| 'Options' => opts, | ||
| 'JobListener' => self.job_status_tracker |
There was a problem hiding this comment.
This will also need updated when ou make exploit work like the rest
| { | ||
| "job_id" => mod.job_id, | ||
| "uuid" => mod.uuid | ||
| "uuid" => mod.run_uuid || mod.uuid |
There was a problem hiding this comment.
Let's verify if the old uuid actually makes sense to fall back to
| # all inherited mixins, so 100 is a comfortable ceiling. This is a | ||
| # defence-in-depth cap against DoS-style input abuse from an MCP client, | ||
| # not a transport limit (MCP itself does not restrict payload size). | ||
| MODULE_OPTIONS_MAX_KEYS = 100 |
There was a problem hiding this comment.
should this kind of validation live on the RPC side of things rather than MCP?
| 'Values must be scalars (string, integer, float, boolean, or null). ' \ | ||
| 'Example: {"RHOSTS": "192.0.2.10", "RPORT": 445}. ' \ | ||
| 'No nested objects or arrays.', | ||
| additionalProperties: { type: %w[string integer number boolean null] } |
There was a problem hiding this comment.
Verify this includes floats
| minLength: 1, | ||
| maxLength: 50 |
| results: raw_result['results'] || [] | ||
| } | ||
|
|
||
| metadata = { query_time: (Time.now - start_time).round(3) } |
There was a problem hiding this comment.
Rex::StopWatch or MonotonicClock (check my spelling there)
Description
This adds three related features to the MCP server:
Exploit,Auxiliary,PostandEvasionmodules now return a(run_uuid, job_id)pair frommodule.execute, which can be polled viamodule.results. This is invisible to msfconsole users but is a prerequisite for the new MCP tools.The eight new tools
msf_module_executemodule.executeread_only_hint: false, destructive_hint: true, idempotent_hint: falsemsf_module_checkmodule.checkread_only_hint: false, destructive_hint: true, idempotent_hint: falsemsf_module_resultsmodule.resultsread_only_hint: true, destructive_hint: false, idempotent_hint: truemsf_running_statsmodule.running_statsread_only_hint: true, destructive_hint: false, idempotent_hint: truemsf_session_listsession.listread_only_hint: true, destructive_hint: false, idempotent_hint: truemsf_session_stopsession.stopread_only_hint: false, destructive_hint: true, idempotent_hint: falsemsf_session_readsession.interactive_readread_only_hint: true, destructive_hint: false, idempotent_hint: truemsf_session_writesession.interactive_writeread_only_hint: false, destructive_hint: true, idempotent_hint: falseDangerous-mode gate
Dangerous tools fail closed. The gate is opened by any one of:
--enable-dangerous-actionson themsfmcpdCLI.MSF_MCP_DANGEROUS_ACTIONS=true(or1,yes,on) in the environment.mcp.dangerous_actions: trueinconfig/mcp_config.yaml(or the JSON-RPC variant).Related Issue:
This resolves #21270.
Breaking Changes
Minor changes:
module.executeRPC for exploit, post, and evasion modules:uuidfield semantics changed (now 24-character)module.resultsRPC: HTTP status code changed for unknown UUIDs (404 -> 500)rescue ::StandardError->After: rescue ::StandardError, ::ScriptErrorVerification Steps
Follow the steps in the documentation to setup and start the RPC and MCP servers.
The verification can be done manually using the MCP Inspector and test the MCP tools individually:
Or you can use your favorite LLM to test live. (see the documentation)
I also included an integration test script that simulates a MCP client and performs standard operations with the MCP tools. The script can be found here:
tools/dev/msfmcp_integration_test.rbAI Usage Disclosure
AI was used to help designing and implementing these changes (Copilot).
Pre-Submission Checklist
documentation/modules(new modules only)lib/changes)Hardware and Complex Software Module Guidance
If your module targets specialized hardware (routers, IoT, PLCs, etc.) or complex software (licensed, multi-service, or multi-version), provide a pcap, screen recording, or video showing successful execution.
Email sanitized pcaps/recordings to msfdev@metasploit.com — remove real IPs, credentials, and hostnames before sending. If hardware/software is unavailable, explain in the PR description.
Responsiveness and PR Takeover Policy
We want every contribution to make it into the project. If approximately 2 weeks pass after a review request without a comment or code update from you, the team may take over the PR and complete the work on your behalf.
If this happens, you will remain credited as a co-author on the final commit — your contribution is always recognized.
This policy exists to keep the project moving forward. It is not a reflection on the quality of your work or your involvement. Life happens, and we would rather finish the work together than let a good contribution go stale.