PluginWorld
Mc

mcp-security-lab

MCP

Enables hands-on exploration of common MCP security vulnerabilities through locally runnable vulnerable and fixed servers with accompanying exploits and a dashboard.

@ibrahimsaleem · no license · updated today

SECURITY

B

SCORE

54

STARS

0

PLUG IN

git clone https://github.com/ibrahimsaleem/mcp-security-lab.git

See the README to configure this MCP server

README

MCP Security Lab

A small, real, runnable Model Context Protocol setup with seven deliberate vulnerabilities — and their fixes — so you can see each risk actually work instead of just reading about it. Built against the current official MCP spec (2026-07-28) and its security best practices doc, plus the OWASP MCP Top 10.

Everything runs locally. Nothing here talks to a real system you care about.

Documentation

Everything below is readable straight on GitHub — no server needed — and each doc links to the next:

  • docs/owasp-mcp-top10.md — field reference for all ten OWASP MCP Top 10 risk categories, what an MCP server actually is, the end-to-end request path and its three named risk zones, and a link from every category straight to the exploit script that demonstrates it (or the closest one).
  • docs/mcp-stateless-rewrite.md — before/after architecture diagrams for the 2026-07-28 spec's stateless rewrite, a ranked priority list of which OWASP categories are now more critical because of it, a table mapping every spec/roadmap change to a specific OWASP category, and five new vulnerability patterns with concrete failure scenarios.

The same two pages also exist as live, interactive HTML inside the dashboard (guidelines.html and rewrite.html, see Dashboard below) — the content is identical, the dashboard version just links directly into the live exploit runner instead of a source file.

What's covered

File tool Vulnerability Source
run_diagnostics Command Injection OWASP MCP05
search_docs / get_secret_config Tool (Description) Poisoning OWASP MCP03
get_cart State Handle Hijacking MCP Security Best Practices
proxy_fetch_data Token Passthrough / Confused Deputy MCP Security Best Practices
read_user_file Insufficient Auth + No Audit Trail OWASP MCP07 / MCP08
read_calendar_events Privilege Escalation via Scope Creep OWASP MCP02
launch_from_config Client Launch Injection (Supply Chain RCE) OWASP MCP04 · 2026 research

servers/vulnerable_server.py has all seven, broken on purpose. servers/fixed_server.py has the same seven tools, patched — run the same exploit against both and watch the outcome flip.

Every OWASP MCP Top 10 category now links to a demo (or the closest real one) from the dashboard's guidelines page — including MCP09 (Shadow MCP Servers), which is a governance gap rather than a single-tool-call bug, so it's illustrated by the lab itself rather than a dedicated exploit script.

Staying current

MCP moves fast — the 2026-07-28 spec release was the protocol's biggest rewrite yet (it went fully stateless), and a new roadmap covering the next release was published just days ago, on 2026-08-22. Two places track what's changed since this lab was built and how it shifts the risk picture:

  • docs/owasp-mcp-top10.md — the "What's changed" section, or the live version at http://127.0.0.1:8000/guidelines.html#2026-updates.
  • docs/mcp-stateless-rewrite.md — the full breakdown, including which OWASP categories are now more critical and why, or the live version at http://127.0.0.1:8000/rewrite.html.

Setup

python -m venv .venv
source .venv/bin/activate         # Windows: .venv\Scripts\activate
pip install -r requirements.txt

Verified against mcp SDK v1.26.0 — the servers import FastMCP from mcp.server.fastmcp (some tutorials show it imported straight from mcp.server, or reference a MCPServer class from a mcp.server.mcpserver module that doesn't exist in this SDK line; same API either way). If your installed version differs, pip show mcp and check dir(mcp.server) for the right class name — everything else (the .tool() decorator, .run(transport="stdio")) is unchanged.

The command-injection exploit works even without a real ping binary on your machine — the point is proving the shell metacharacter (;) gets interpreted at all, which the "PWNED" marker file confirms.

Set whichever key(s) you want to use as environment variables — never put a real key in a file you might commit or paste into a chat:

export OPENAI_API_KEY="sk-..."
# and/or
export GEMINI_API_KEY="AI..."

Run the exploits

Six of the seven don't need an LLM at all — they call the MCP server directly to prove the flaw lives in the server, not in model behavior:

# Command injection
python exploits/exploit_cmd_injection.py servers/vulnerable_server.py
python exploits/exploit_cmd_injection.py servers/fixed_server.py

# State handle hijacking (guessing another user's cart ID)
python exploits/exploit_state_hijack.py servers/vulnerable_server.py
python exploits/exploit_state_hijack.py servers/fixed_server.py

# Token passthrough / confused deputy
python exploits/exploit_token_passthrough.py servers/vulnerable_server.py
python exploits/exploit_token_passthrough.py servers/fixed_server.py

# Insufficient auth + no audit trail (reading another user's file by ID)
python exploits/exploit_auth_audit.py servers/vulnerable_server.py
python exploits/exploit_auth_audit.py servers/fixed_server.py

# Privilege escalation via scope creep (redirecting a read-only tool's token)
python exploits/exploit_scope_creep.py servers/vulnerable_server.py
python exploits/exploit_scope_creep.py servers/fixed_server.py

# Client launch injection / supply chain RCE (untrusted config picks command/args)
python exploits/exploit_launch_injection.py servers/vulnerable_server.py
python exploits/exploit_launch_injection.py servers/fixed_server.py

Tool poisoning needs a real model, because the vulnerability is whether the model follows hidden instructions in a tool's own description:

python exploits/exploit_tool_poisoning.py servers/vulnerable_server.py openai
python exploits/exploit_tool_poisoning.py servers/vulnerable_server.py gemini
python exploits/exploit_tool_poisoning.py servers/fixed_server.py openai

Dashboard

A local web dashboard shows all 7 vulnerabilities as cards, lets you view the vulnerable-vs-fixed code diff for each, and click a button to run the real exploit script against either server — the output streams into a live terminal panel in the browser and ends with a VULNERABLE/SAFE verdict.

pip install -r requirements.txt -r ui/requirements.txt
uvicorn ui.server:app --reload --port 8000

Then open http://127.0.0.1:8000. Set OPENAI_API_KEY / GEMINI_API_KEY in the terminal you launch uvicorn from if you want to run the tool-poisoning card — the dashboard never asks for a key itself, it just reads whichever of those environment variables are already set on the machine running it.

Talk to it yourself

agent.py is a general-purpose MCP client + agent loop — point it at either server and either provider and chat with it interactively:

python agent.py servers/vulnerable_server.py openai
python agent.py servers/fixed_server.py gemini

Try asking it to search the docs, check a cart, or run diagnostics on a host, and watch the [llm -> tool] / [tool -> llm] lines to see exactly what it decided to call and what came back.

What's NOT fully covered here

Two things from the current MCP security doc are real but harder to demo in a small local lab, worth reading about even if you don't build them:

  • SSRF via OAuth discovery — a malicious server pointing a client's metadata fetch at 169.254.169.254 (cloud instance metadata). Needs an actual OAuth flow and a network to attack.
  • Mix-up / localhost redirect URI impersonation — needs a real multi-authorization-server setup to demonstrate meaningfully. The 2026-07-28 spec added a concrete mitigation for this (clients must now validate the iss parameter, RFC 9207, before redeeming an auth code) — see the guidelines page's "2026 Updates" section.

Both are described in detail at https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/security_best_practices.

Extending this

  • Add another vulnerable tool and its exploit script following the same pattern: comment the flaw, write the exploit, write the fix.
  • Try running the same exploit against a real third-party MCP server you install (with permission, on infra you own) — the state-hijack and no-auth patterns show up constantly in quickly-built servers.
  • Wire AUDIT_LOG in fixed_server.py into a real log sink and build a small detection rule for the .denied events — that's basically MCP08 (audit/telemetry) made concrete.

SIMILAR PLUGINS