Skip to content

MCP

Analyze and exploit Model Context Protocol (MCP) servers.

Overview

The mcp module covers both local MCP configuration analysis and remote MCP server exploitation. It supports local config parsing (Claude Desktop, VS Code, Cursor), remote enumeration over HTTP/SSE, and guarded poison probes across multiple attack modes.

The HTTP transport handles both standard JSON responses and streamable-HTTP servers that return Server-Sent Events (SSE) on POST. Requests include Accept: application/json, text/event-stream, and text/event-stream responses are automatically parsed to extract the embedded JSON-RPC payload from data: lines. Mixed-case URL suffixes (e.g., /SSE, /Sse) are correctly normalized during transport detection.

Subcommands

Read-Only (no --force-exploit required)

Subcommand Description
analyze Analyze a local MCP configuration file
enum Enumerate a remote MCP HTTP/SSE endpoint (add --read to also retrieve resources + prompts)
env-extract Extract environment variables from MCP server processes via tool reflection and error leakage

Gated (requires --force-exploit)

Subcommand Description
config-hijack Write and verify a hijacked local config entry with backup/rollback
poison Send exploit probes to an MCP server (9 modes)
chain Automated multi-step credential exfiltration kill chain
sandbox-escape Probe an MCP filesystem read tool for a path-based sandbox escape (CVE-2025-53109 / -53110 class)
ssti Probe an MCP rendering/formatting tool for server-side template injection (Jinja2)
sampling Advertise the sampling capability and detect a server that drives the client's LLM (server-initiated sampling/createMessage)
elicitation Advertise the elicitation capability and detect a server that phishes the client's user (server-initiated elicitation/create)
roots Advertise the roots capability and detect a server harvesting the client machine's filesystem roots (server-initiated roots/list)
complete Enumerate server-side values through completion/complete (prompt + resource-template arguments)
logging Raise the server log level (logging/setLevel) and capture the log output it pushes to clients
subscribe Establish a resources/subscribe push channel onto a resource
auth Probe the endpoint's authorization posture: anonymous access, OAuth metadata discovery, and open dynamic client registration

Flags

Common

Flag Required Description
--target For enum, poison MCP server URL (e.g., http://127.0.0.1:3000)
--header No Custom HTTP headers. Repeatable.
--config For analyze, config-hijack Path to MCP config file
--read No (enum) Also retrieve each resource (resources/read) and prompt (prompts/get), not just list them
--tool No (sampling, elicitation, roots, logging, env-extract) Probe a single named tool instead of every enumerated one
--level No (logging) Log level to request: debug (default), info, notice, warning, error, critical, alert, emergency
--uri No (subscribe) Resource URI to subscribe to (default: every listed resource)

Config-Hijack Flags

Flag Required Description
--server No Server name to add or replace. Defaults to aipostex-hijack.
--url Either --url or --command Remote MCP URL to install in the config.
--command Either --url or --command Local stdio command to launch from the config.
--arg No Argument for --command. Repeatable.
--env No Environment assignment for the entry (KEY=VALUE). Repeatable.
--entry-transport No Transport value to write. Defaults to http/sse from --url or stdio from --command.

Poison Flags

Flag Required Description
--mode Yes Attack mode: generic, ssrf-cloud, cmd-inject, path-traversal, type-field, default-value, example-inject, error-message, enum-poison
--tool For generic Tool name to target
--payload For generic Payload string
--attempts No Number of payload attempts
--target-alias For ssrf-cloud Cloud provider: aws-imds, gcp-metadata, azure-imds. Mutually exclusive with --url.
--url For ssrf-cloud Custom SSRF target URL instead of a built-in cloud alias
--command For cmd-inject Command to inject
--path For path-traversal Path traversal string

Reading resources & prompts (enum --read)

enum alone lists an MCP server's tools, resources, and prompts — but a bare list is only names/URIs. enum --read goes one step further and retrieves them:

  • resources/read — fetches each listed resource's actual body (a file, config, or record the server exposes). Reading server-side data you may not be meant to see is a direct data-access vector distinct from enumeration.
  • prompts/get — renders each listed prompt template. A server-supplied prompt can embed system context, credentials, or a prompt injection the client's model would execute — retrieving the template surfaces it.

Each item whose body is recovered is graded access / read-confirmed (you read the data). Nothing is redacted; any secrets in the content surface downstream through the credential index (report view --credentials). Resources/prompts the server does not expose are simply skipped (honest — a server with tools only has nothing to read here).

aipostex mcp --target http://127.0.0.1:3000 enum --read

Poison Modes

generic

Sends arbitrary payload to a specified tool. Tests prompt injection and tool manipulation.

ssrf-cloud

Probes fetch-like tools for SSRF access to cloud metadata endpoints (AWS IMDS, GCP metadata, Azure IMDS).

cmd-inject

Targets shell/process-like tools with command injection payloads.

path-traversal

Targets file-read/write tools with path traversal sequences.

type-field (Full-Schema Poisoning)

Injects instruction text into JSON Schema type field definitions. Based on CyberArk's Full-Schema Poisoning research.

default-value (Full-Schema Poisoning)

Sets parameter default values to trigger unintended behavior (e.g., default path pointing to sensitive file).

example-inject (Full-Schema Poisoning)

Places prompt injection payloads in the examples array of parameter definitions.

error-message (Full-Schema Poisoning)

Crafts tool responses with error messages containing instructions for the LLM.

enum-poison (Full-Schema Poisoning)

Adds values to enum arrays that contain embedded instructions.

Environment Extraction

The env-extract subcommand (read-only) attempts to extract environment variables from MCP server processes through:

  1. Tool reflection -- asking exec-capable tools to print their environment
  2. Error message leakage -- sending malformed requests to trigger verbose errors containing env vars
  3. Known env var patterns -- scanning for OPENAI_API_KEY, ANTHROPIC_API_KEY, HF_TOKEN, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AZURE_OPENAI_KEY, GOOGLE_API_KEY, LANGCHAIN_API_KEY, WANDB_API_KEY

Chain (Kill Chain Automation)

The chain subcommand (gated) automates the multi-step credential exfiltration kill chain:

  1. Enumerate -- discover available tools and schemas
  2. Score tools -- identify high-value tools (file, exec, fetch, cloud)
  3. Environment probe -- run env-extract against discovered tools
  4. Cloud metadata probe -- attempt SSRF to AWS/GCP/Azure metadata endpoints via fetch-capable tools
  5. Report -- generate chain summary with full attack path documentation

Flags: --cloud (aws/gcp/azure/all), --skip-metadata

Sandbox Escape

The sandbox-escape subcommand (gated) tests whether an MCP filesystem read tool enforces its advertised directory boundary — the class of flaw assigned CVE-2025-53109 / CVE-2025-53110. It sends a prefix-check-bypass path (the allowed prefix followed by traversal), a percent-encoded traversal variant, and a bare absolute path, and checks whether the tool returns content from outside the sandbox. A read-like tool is auto-detected when --tool is not given (pure listing tools are excluded).

The escape is only claimed when the response carries a runtime-only marker — a Unix passwd signature such as root:x:0:0 or :/bin/bash — that cannot be hallucinated. Enforcing the sandbox → Info, reachable; a confirmed read outside the directory → High, impact / read-confirmed.

Flags: --tool (auto-detected if empty), --path-arg (name of the tool's path argument, default path), --allowed-prefix (the tool's advertised allowed directory, default /data/documents), --escape-target (file outside the sandbox to try to read, default /etc/passwd).

SSTI

The ssti subcommand (gated) sends template-injection payloads to an MCP rendering / formatting tool and checks the output for evaluation. It prioritizes the Jinja2 {{ lipsum.__globals__.keys() }} signal — whose dict_keys / __globals__ / 'os' / builtins output only exists inside the server's Python runtime and so cannot be hallucinated — over bare arithmetic ({{7*7}}49). A render-like tool is auto-detected when --tool is not given.

Grading is honest about how strongly injection was proven: a globals leak confirms an SSTI that reaches the Python runtime (a code-execution surface) → High, impact / read-confirmed; a bare-arithmetic evaluation alone → Medium, impact / influenced; literal, un-evaluated output → Info, reachable.

Flags: --tool (auto-detected if empty), --arg (name of the tool argument that gets rendered, default content).

Sampling Abuse

MCP sampling lets a server ask the connected client to run an LLM completion (sampling/createMessage). A malicious server abuses it in the reverse direction of everything else here — instead of the client driving the server, the server drives the client's model: it can exfiltrate the client's conversation context or use the victim's paid model as a free proxy. This surface is invisible to enum — a tools/list shows nothing, because the abuse only happens when a tool is invoked.

The sampling subcommand (gated) advertises the sampling client capability during initialize — the precondition for a server to attempt the abuse — then invokes each tool (or a single --tool) and watches the response for a server-initiated sampling/createMessage request. The request bytes are kept as raw evidence, so credchain surfaces anything sensitive the server tried to feed the model.

aipostex mcp --target http://127.0.0.1:3000 sampling --force-exploit
aipostex mcp --target http://127.0.0.1:3000 sampling --tool summarize --force-exploit

Grading is deliberately conservative: aipostex advertises sampling but never answers the request, so a hit confirms the server's abuse behavior, not a victim client's compliance. A captured server-initiated request → High, access / influenced; the run always emits an info summary of how many tools were probed and how many issued a request. Over Streamable HTTP the SDK delivers a server→client request on the standalone GET event stream (not the tool-call response), so aipostex opens that stream alongside the tool call and watches both — a create_message-abusing tool that flushes the request and then blocks is still caught. stdio servers are probed best-effort. Because it invokes tools, this is an active action and requires --force-exploit.

Elicitation Phishing

MCP elicitation (2025 spec) lets a server prompt the connected client's user for structured input mid-tool-call (elicitation/create). A malicious server abuses it to phish"enter your API key to continue", "confirm this transfer" — or to inject an approval the user never intended. Like sampling, it is a server→client request, invisible to enum, and only fires when a tool is invoked.

The elicitation subcommand (gated) advertises the elicitation client capability, invokes each tool (or a single --tool), and captures a server-initiated elicitation/create request using the same GET-event-stream machinery as sampling. A hit → High, access / influenced; aipostex never answers the prompt, so no user is actually phished — the finding confirms the server's behavior, not a victim's response. The request (its message + requested schema) is kept as raw evidence.

aipostex mcp --target http://127.0.0.1:3000 elicitation --force-exploit

Roots Harvesting

MCP roots let a client tell a server which local directories it may work in. A malicious server flips that around: it issues roots/list to make the connected client disclose its filesystem layout — project directories, mounted shares, home paths. That is reconnaissance of the client machine, and like sampling and elicitation it only fires when a tool is invoked.

roots (gated) advertises the capability, invokes each tool (or --tool), and captures a server-initiated roots/list off the event stream. A hit → Medium, access / influenced — the server's harvesting behavior is confirmed, but aipostex never answers, so no path is actually disclosed.

aipostex mcp --target http://127.0.0.1:3000 roots --force-exploit

Completion Enumeration

completion/complete powers argument autocomplete for prompts and resource templates. A server that answers it will happily enumerate server-side values — account IDs, ticket numbers, usernames, file paths — that no resources/list or prompts/list call exposes. Autocomplete becomes a quiet enumeration primitive.

complete (read-only, no tool invoked) asks for the unfiltered value set of every declared prompt argument and every resource-template argument. Templates matter: they are served from resources/templates/list, which resources/list does not include, so a whole parameterized data surface (records://customers/{account_id}) is invisible to a plain enumeration — enum now lists them too. Values returned → Medium, access / read-confirmed, with the values as evidence.

aipostex mcp --target http://127.0.0.1:3000 complete

Log Harvesting

logging/setLevel lets a client choose how verbose the server is. If the server accepts it unauthenticated, an attacker turns the verbosity up and then reads whatever the server's debug output contains — backend URLs, arguments, identifiers, and not infrequently tokens.

logging (gated — it changes server state) sets the level (--level, default debug), then invokes tools and captures any notifications/message the server pushes. The level change being accepted → Medium, access / influenced; a captured log notification → Medium, access / read-confirmed, with the raw notification as evidence so any secret in it reaches the credential index.

aipostex mcp --target http://127.0.0.1:3000 logging --force-exploit
aipostex mcp --target http://127.0.0.1:3000 logging --level debug --tool lookup_ticket --force-exploit

Resource Subscription

resources/subscribe establishes a standing push channel: the server notifies on every change to a resource, with no repeated polling and no new request. Accepted without authorization, it is durable read access to changing internal data.

subscribe (gated — it writes server-side state) subscribes to every listed resource, or one given by --uri. Accepted → Medium, access / influenced (the subscription exists; a pushed update is not claimed unless observed). A server that does not implement the method is reported honestly as unsupported rather than as a failure.

aipostex mcp --target http://127.0.0.1:3000 subscribe --force-exploit
aipostex mcp --target http://127.0.0.1:3000 subscribe --uri internal://ops/runbook --force-exploit

Authorization Posture

MCP's 2025 authorization spec layers OAuth 2.1 over the HTTP transport: the server is a protected resource (RFC 9728) that points at an authorization server (RFC 8414), and clients obtain audience-bound bearer tokens. In practice the common failures are (1) auth simply not enforced — the endpoint answers anonymous requests — and (2) open dynamic client registration, where anyone can mint an OAuth client.

The auth subcommand probes all three, no token required:

  • Enforcement — sends an unauthenticated initialize. If it is accepted, the endpoint is anonymously reachable and its tools are callable by anyone who can reach it (Medium, access / read-confirmed, with the anon tool count). If it 401s, the WWW-Authenticate challenge is captured (Info, recon).
  • Discovery — fetches /.well-known/oauth-protected-resource (or the resource_metadata URL from the challenge) and the authorization server's /.well-known/oauth-authorization-server, enumerating the issuer, endpoints, scopes, and any registration endpoint (Info, recon; raw metadata as evidence).
  • Open registration — if a registration endpoint is advertised, --force-exploit submits an unauthenticated RFC 7591 registration. A minted client_id means open DCR (High, access / influenced) — an attacker can self-provision clients. Without --force-exploit the endpoint is only reported, not exercised.
aipostex mcp --target http://127.0.0.1:3000 auth
aipostex mcp --target http://127.0.0.1:3000 auth --force-exploit   # also tests open DCR

Examples

# Analyze local config
./aipostex mcp analyze --config ~/.config/Claude/claude_desktop_config.json

# Enumerate remote endpoint
./aipostex mcp --target http://127.0.0.1:3000 enum

# Generic poison probe (gated)
./aipostex mcp --target http://127.0.0.1:3000 poison \
  --mode generic --tool fetch \
  --payload "Ignore previous instructions." \
  --attempts 2 --force-exploit

# SSRF cloud metadata (gated)
./aipostex mcp --target http://127.0.0.1:3000 poison \
  --mode ssrf-cloud --target-alias aws-imds --force-exploit

# Command injection (gated)
./aipostex mcp --target http://127.0.0.1:3000 poison \
  --mode cmd-inject --command id --force-exploit

# Path traversal (gated)
./aipostex mcp --target http://127.0.0.1:3000 poison \
  --mode path-traversal --path ../../etc/passwd --force-exploit

# Full-schema poisoning: type field injection (gated)
./aipostex mcp --target http://127.0.0.1:3000 poison \
  --mode type-field --force-exploit

# Full-schema poisoning: default value injection (gated)
./aipostex mcp --target http://127.0.0.1:3000 poison \
  --mode default-value --payload /etc/passwd --force-exploit

# Environment variable extraction (read-only)
./aipostex mcp --target http://127.0.0.1:3000 env-extract

# Write a hijacked remote MCP config entry (gated)
./aipostex mcp config-hijack \
  --config ~/.config/Claude/claude_desktop_config.json \
  --server aipostex-hijack \
  --url http://127.0.0.1:3000/mcp \
  --force-exploit

# Filesystem sandbox escape (gated)
./aipostex mcp --target http://127.0.0.1:3000 sandbox-escape \
  --allowed-prefix /data/documents --force-exploit

# Server-side template injection (gated)
./aipostex mcp --target http://127.0.0.1:3000 ssti \
  --tool render_report --arg report_data --force-exploit

# Automated credential chain (gated)
./aipostex mcp --target http://127.0.0.1:3000 chain --force-exploit

# Chain targeting only AWS metadata
./aipostex mcp --target http://127.0.0.1:3000 chain \
  --cloud aws --force-exploit

Transport Compatibility

The MCP client supports three transport modes:

Transport How It Works
HTTP (JSON) POST JSON-RPC to target URL, receive JSON response
HTTP (SSE) POST JSON-RPC to target URL, receive text/event-stream with JSON-RPC in data: lines
stdio Spawn local process, exchange NDJSON over stdin/stdout

When targeting an endpoint that ends in /sse (case-insensitive), the client automatically rewrites the POST target to /message on the same base URL. This handles the common pattern where SSE MCP servers expose an SSE event stream at /sse and accept commands at /message.

Analyze Capabilities

The analyze command parses local MCP config files and identifies:

  • Transport choices -- stdio vs HTTP/SSE per server
  • Command execution -- local commands configured to run (npx, uvx, python, node)
  • Plaintext credentials -- API keys and tokens in environment variables (redacted in console)
  • Non-loopback exposure -- servers binding to non-localhost addresses
  • Inspector/debug exposure -- MCP Inspector or debug tooling configured
  • Tool shadowing -- tool name collisions across configured servers
  • Remote URL correlation -- remote MCP URLs that suggest follow-on enum or poison commands

Enum Capabilities

The enum command classifies discovered tools into capability buckets:

  • fetch -- HTTP fetch tools (SSRF potential)
  • file -- file read/write tools (traversal potential)
  • exec / process -- command execution tools
  • inspector -- MCP Inspector or debug tooling

Each classification includes a confidence score and suggested exploit modes.

What each landed level means here

The landed axis records what actually landed on the MCP server. The mcp module tops out at execution-confirmed; it does not claim takeover-capable.

landed What produces it in mcp
reachable enum (endpoint responds; tools/inspector discovered); poison in a schema mode (type-field, default-value, example-inject, error-message, enum-poison), or when the tool returns an error, or ssrf-cloud with no provider marker; sandbox-escape when the tool enforces its directory boundary; ssti when the payloads are returned as literal, un-evaluated text
influenced config-hijack after a local config entry is written and reparsed; poison --mode generic (payload accepted); poison --mode cmd-inject (a command-output marker appeared, but a substring match is not nonce-confirmed execution, so it stays "likely"); poison --mode path-traversal before a file signature is confirmed; ssti when only bare arithmetic evaluated (no runtime globals leak)
read-confirmed env-extract when a real credential is leaked (env value returned); poison --mode ssrf-cloud when a provider marker is returned; poison --mode path-traversal when a file-read is confirmed (file-read-confirmed); sandbox-escape when a runtime passwd marker confirms a read outside the sandbox; ssti when the Jinja2 globals leak confirms the injection reaches the Python runtime; chain credential-exfiltration steps
execution-confirmed chain cloud-metadata step — a fetch-capable tool processes an SSRF URL and returns AWS/GCP/Azure metadata provider markers

Operator console

To call a server's tools by hand, the shell verb (aipostex mcp … shell --force-exploit) opens an interactive tool-caller: type <tool> {"arg":"value"} to invoke a discovered tool, :tools to list them, and the session is mined for credentials on exit. It is an execution shell, so it requires --force-exploit; you drive every call, nothing chains on its own. MCP has no one-shot request verb — the JSON-RPC session is stateful, so use the shell.

Vulnerability Templates

aipostex includes 20 MCP-specific vulnerability templates that run automatically during scan targets and discover network. These cover infrastructure exposure, CVEs, and server-specific vulnerabilities from the vulnerablemcp database.

Infrastructure Exposure

Template What It Detects
mcp-auth-001 / 002 Unauthenticated SSE and HTTP transports
mcp-auth-003 / 005 MCP Inspector UI and API exposed without auth
mcp-auth-004 DNS rebinding via Host header trust
mcp-session-001 Session IDs leaked in SSE endpoint URL query parameters

CVEs and Server-Specific

Template CVE What It Detects
cve-2025-65513 CVE-2025-65513 Fetch MCP Server SSRF via IP validation bypass
cve-2025-49596 CVE-2025-49596 MCP Inspector RCE (versions < 0.14.1)
cve-2025-66414 CVE-2025-66414/66416 Official MCP SDK DNS rebinding (TS < 1.24.0, Python < 1.23.0)
cve-2025-53355 CVE-2025-53355 Kubernetes MCP server command injection via kubectl tools
cve-2025-53967 CVE-2025-53967 Framelink Figma MCP server RCE via curl fallback
cve-2025-59163 CVE-2025-59163 Vet MCP server DNS rebinding
tra-2025-36 TRA-2025-36 Microsoft Learn MCP server SSRF via docs_fetch tool
mcp-enum-006 CVE-2025-10193 Neo4j MCP Cypher server exposure and DNS rebinding

Run templates against an MCP endpoint:

./aipostex scan targets http://127.0.0.1:3000 --tags mcp

See Built-in Templates for the full template reference.

Workflow Progression

discover network / discover files (discovers MCP config or endpoint)
  → scan targets --tags mcp (run vulnerability templates)
  → mcp analyze --config <path> (local config analysis)
    → mcp config-hijack --config <path> --url <remote-mcp-url> --force-exploit (verified local config write)
  → mcp enum --target <url> (remote tool enumeration)
    → mcp env-extract (credential probing, read-only)
    → mcp poison --mode <mode> (exploit validation, gated)
    → mcp sandbox-escape (filesystem read-tool path escape, gated)
    → mcp ssti (rendering-tool template injection, gated)
    → mcp chain (automated credential exfiltration, gated)