notes 2026-09-08 · 9 min read

MCP Servers as an Attack Surface: Tools and Tokens

MCP servers as an attack surface: tools as executable capability, token audience binding, confused deputy problems and prompt injection with real reach.

MCP Servers as an Attack Surface: Tools and Tokens

An MCP Server may have access to the filesystem, Git and databases. Browsers, cloud services and internal APIs may sit behind the same process. The model only needs to call one tool to turn that access into action.

Model Context Protocol has grown because it standardizes how AI applications connect to external tools.

That convenience creates a new pentest surface.

The protocol defines how clients and servers communicate. Real risk comes from the capabilities a server exposes, the identity it runs under, and the way the application decides when a tool may be called.

The examples below assume a lab or an authorized environment.

MCP trust boundaries

Start by identifying what is actually running

MCP commonly appears in two very different deployment styles.

A local server may be launched by the client over stdio.

A remote server may be reached over the network.

A local server runs with the permissions of the process context that launches it.

If the client runs as your desktop user, the local server can usually reach resources available to that user.

That makes a local MCP server much closer to installed software than to a distant API.

The official MCP trust model makes the same point. Local servers should be trusted like other software installed on the machine.

That difference changes the first pentest question.

For a local server, ask which files and sockets the process can reach. Then check environment variables and commands.

For a remote server, ask how tools are authenticated, which resources the remote identity can reach, and how tokens and metadata are validated.

Tools are executable capabilities

An MCP Server may advertise tools such as:

1
2
3
4
5
6
7
8
read_file
write_file
run_query
git_status
git_commit
create_issue
deploy_service
send_email

The tool name says very little.

The implementation matters.

Can read_file accept any path?

Does run_query enforce read-only SQL?

Can git_commit modify any repository visible to the host?

Which cloud credential does deploy_service use?

Can send_email target any recipient?

Treat each tool like an API endpoint with side effects.

The schema validates structure.

Authorization decides who may use the capability.

Input validation decides how far an argument can travel.

A protocol-correct server can still be implemented unsafely

Consider:

1
2
3
4
5
6
{
  "name": "read_project_file",
  "arguments": {
    "path": "docs/readme.md"
  }
}

Unsafe implementation:

1
2
target = PROJECT_ROOT + "/" + path
return open(target).read()

Now test:

1
../../../../etc/passwd

That is path traversal inside an MCP tool.

The same bug could exist in a traditional HTTP API.

MCP changes discovery and transport, not the underlying vulnerability class.

The same applies to command injection, SQL injection, SSRF, and unsafe deserialization inside tools.

Local servers raise the impact of path and command injection

A server launched over stdio may receive client arguments and perform local operations.

If implementation code does this:

1
subprocess.run("git diff " + user_arg, shell=True)

the security issue comes from placing untrusted input inside a shell.

Executing configured server processes is expected behavior for local transport.

That distinction prevents false positives.

MCP is allowed to start local processes.

A vulnerability appears when the implementation grants more control than the intended tool contract.

Reference servers are not automatically production-ready

The official servers repository explicitly describes many servers as reference implementations.

They demonstrate protocol features and SDK usage.

They should not be treated as hardened production components by default.

That became concrete through advisories published in 2025 and 2026 involving path validation, symlink handling, and argument injection in Git server implementations.

The practical lesson is simple.

Example code still needs a threat model before it reaches developer workstations or corporate infrastructure.

Remote authorization needs token audience binding

The current MCP authorization specification requires audience binding.

The client includes a resource parameter during authorization.

The MCP Server validates that the access token was issued specifically for that server.

This blocks a dangerous pattern:

1
2
3
4
5
6
7
token issued for Service A
        |
        v
MCP Server accepts it without audience validation
        |
        v
token is reused outside the original resource

The specification also forbids token passthrough to upstream APIs.

If an MCP Server calls an external API, it needs a credential appropriate for that upstream resource.

Passing the client’s token into another service mixes trust boundaries.

Client tokens and upstream tokens need separation

Confused deputy problems are easy to create

Consider an MCP Server acting as a proxy to GitHub.

The user authorizes the MCP Server.

The server then calls GitHub on the user’s behalf.

If a malicious client can make that proxy reuse authorization codes, tokens, or consent belonging to another client, the MCP Server becomes a confused deputy.

Modern MCP authorization guidance addresses this explicitly.

A pentest should inspect client registration, consent and redirect URIs. Then validate issuer, audience and token exchange.

Weak OAuth inside MCP is still weak OAuth.

Metadata discovery can become SSRF

Official MCP security guidance calls out SSRF during OAuth discovery.

A client may fetch URLs provided through:

1
2
3
4
resource metadata
authorization server metadata
token endpoint
authorization endpoint

A malicious MCP Server may point those fields at internal resources.

Example:

1
http://169.254.169.254/

or:

1
http://10.20.30.40/admin

The interesting part is that the SSRF target may be the MCP client itself.

A client running inside sensitive infrastructure needs destination filtering, HTTPS requirements in production, and careful handling of metadata supplied by servers.

State handles need authorization too

Some servers keep state outside the protocol and return a handle.

Example:

1
2
3
{
  "cart_id": "932184"
}

Later tools receive that handle again.

If possession of the handle becomes the only authorization check, the application has recreated IDOR.

A predictable or leaked handle may expose another user’s state.

The 2026 MCP security guidance calls this state handle hijacking.

Bind handles server-side to the authenticated identity.

Do not trust a client-supplied user_id to decide ownership.

Tool descriptions participate in the attack surface

An LLM uses metadata when deciding which tool to call.

Name.

Description.

Schema.

That content influences behavior.

A malicious description may try to push the model toward another tool or toward sending data it should not send.

This matters for threat modeling.

The MCP project security policy also distinguishes this behavior from protocol defects.

An LLM invoking an unexpected tool does not automatically become an MCP vulnerability.

The pentest needs to evaluate whether the application placed correct authorization and consent around sensitive tools.

The practical bug lives in the complete system.

Prompt injection gets more dangerous when tools have power

A web page may contain malicious instructions.

An agent reads the page.

The agent also has a send_email tool.

Or write_file.

Or deploy.

The prompt injection now has a path toward an external effect.

Mitigation cannot depend only on asking the model to ignore malicious instructions.

The application needs narrow tool capabilities.

Separate read and write tools.

Require confirmation for irreversible actions where appropriate.

Validate tool parameters in code.

Use minimal credentials.

Log invocations.

Prompt injection becomes less damaging when the available tool has very little power.

Environment secrets belong in the threat model

Local servers often receive credentials through environment variables:

1
2
3
4
GITHUB_TOKEN
AWS_PROFILE
DATABASE_URL
SLACK_BOT_TOKEN

If a tool exposes arbitrary file reads, command execution, or unsafe debug output, those secrets enter the blast radius.

Logs may also expose Authorization, cookies, or API keys.

Official MCP quickstart security notes explicitly recommend redacting those values.

I inspect environment and logging early.

A harmless-looking tool may run inside a process holding broad credentials.

Filesystem tools need real sandboxing

A prefix allowlist helps.

Canonical path resolution is still required.

Symlinks matter.

A path may begin inside the project and resolve outside it.

Example:

1
/project/logs/latest -> /home/user/.ssh/

Validating the string before resolving the final path leaves room for bypass.

This exact class has appeared in MCP server advisories.

For filesystem tools, I test traversal, absolute paths and symlinks. Then I test case behavior and races between validation and file open.

Command tools should avoid the shell

Consider a tool:

1
git_diff(repository, revision)

A safer implementation passes arguments as an array:

1
2
3
4
subprocess.run(
    ["git", "-C", repo, "diff", revision],
    check=True
)

repo and revision still require validation.

The shell interpretation layer is gone.

Implementations using sh -c, shell=True, or string concatenation deserve immediate argument-injection testing.

Network tools need minimal egress

A fetch_url tool is intentionally capable of server-side requests.

That can be a legitimate feature.

Now the control needs to move to destination handling.

Scheme.

Hostname.

DNS resolution.

Redirects.

Private ranges.

Link-local addresses.

Cloud metadata.

Ports.

Timeouts.

Response size.

If the tool exists only to fetch public documentation, it should not reach localhost or RFC1918 networks.

Pentesting starts with a capability inventory

I build a map with four categories.

Transport.

Identity.

Tools.

Reachable resources.

Then I connect them.

stdio runs as a developer account.

read_file reaches the home directory.

Environment variables contain cloud credentials.

fetch_url reaches a VPC.

A remote server accepts tokens with the wrong audience.

Each relationship becomes a technical hypothesis.

MCP pentest method

A small lab already shows the risk

Consider a local MCP Server exposing two tools:

1
2
read_file(path)
run_git(args)

The process runs as user developer.

developer has an SSH key and a GitHub token in the environment.

read_file validates only path.startswith("/home/developer/project").

A symlink inside the project points to ~/.ssh.

run_git uses shell=True.

The protocol works correctly.

The system is still unsafe because of two classic implementation flaws.

MCP makes those capabilities easy to discover and chain.

Hardening that changes the risk

Run local servers under minimal identities.

Use a container or sandbox when the tool does not need the entire host.

Restrict filesystem roots and resolve paths safely.

Avoid shells for command execution.

Limit network egress.

Implement remote MCP authorization correctly.

Validate token audience.

Reject token passthrough.

Keep scopes narrow.

Add consent or additional policy around write operations where the impact justifies it.

Log actions without storing secrets.

Treat external content as untrusted data even when an LLM sits between input and tool execution.

The question that makes MCP concrete

MCP standardizes capability discovery and invocation.

That concentrates trust boundaries that used to be scattered across plugins, scripts, APIs, and automations.

For offensive security, the central question is direct:

What power does this server have, and which inputs can reach that power?

Once that answer is clear, MCP security becomes application security plus OAuth. Filesystem security, command execution and authorization complete the picture.

References

Model Context Protocol, Security Best Practices.

Model Context Protocol, Authorization Security Considerations.

Model Context Protocol, Security Policy and Trust Model.

Model Context Protocol Servers, Security Advisories.

This post is licensed under CC BY 4.0 by the author.