mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-09-20 06:45:53 +03:00
Add 55 new skills across 3 new domains + 6 undercovered areas (762 -> 817)
Demand-driven expansion targeting the fastest-growing 2025-2026 threat and
skills categories (ISC2/WEF/CrowdStrike/Mandiant signals):
- AI Security (NEW domain, 12 skills): LLM red-teaming with garak/PyRIT,
prompt injection (direct/indirect/RAG), MCP tool-poisoning, agentic tool
invocation, guardrails, model/data poisoning, system-prompt leakage,
embedding/vector weaknesses, model extraction, continuous red-teaming
- Supply Chain Security (NEW domain, 5 skills): SBOMs, dependency confusion,
malicious-npm triage, typosquatting, SLSA/Sigstore provenance
- Hardware & Firmware Security (NEW domain, 4 skills): CHIPSEC/UEFI audit,
Secure Boot bypass, TPM measured-boot attestation, ESP bootkit hunting
- Identity (10): Entra ID/ROADtools, GraphRunner, AADInternals, ADCS/Certipy,
shadow credentials, coercion, BloodHound CE, device-code phishing, SSO abuse
- Cloud-native (8): Stratus, Pacu, CloudFox, container escape, K8s RBAC,
Falco, Trivy, kube-bench
- Offensive C2 (6): Sliver, Havoc, NetExec, DPAPI, NTLM relay ESC8, redirectors
- DFIR (6): Hayabusa, Chainsaw, KAPE, Velociraptor, EZ Tools, Plaso
- Backfill (4): OpenCTI, MISP, honeytokens, post-quantum crypto migration
Each skill follows the repo taxonomy (SKILL.md + references/{standards,api-reference}.md
+ scripts/agent.py + LICENSE), with researched real tool commands (no placeholders),
complete frontmatter, and ATT&CK/ATLAS + NIST CSF mappings. Updates README domain
table, skill count, and index.json.
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding any notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. Please do not remove or change
|
||||
the license header comment from a contributed file except when
|
||||
necessary.
|
||||
|
||||
Copyright 2026 mukul975
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,218 @@
|
||||
---
|
||||
name: auditing-mcp-servers-for-tool-poisoning
|
||||
description: Scan Model Context Protocol servers and tool metadata for poisoning, SSRF, and unauthenticated exposure.
|
||||
domain: cybersecurity
|
||||
subdomain: ai-security
|
||||
tags:
|
||||
- ai-security
|
||||
- mcp
|
||||
- tool-poisoning
|
||||
- agent-security
|
||||
- mcp-scan
|
||||
- ssrf
|
||||
- supply-chain
|
||||
- rug-pull
|
||||
version: '1.0'
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
nist_csf:
|
||||
- MANAGE-2.2
|
||||
mitre_attack:
|
||||
- AML.T0010
|
||||
---
|
||||
# Auditing MCP Servers for Tool Poisoning
|
||||
|
||||
> **Authorized-use-only notice:** Auditing MCP servers can connect to and probe live tool endpoints. Only scan servers you own or are authorized to assess. Treat scanned tool descriptions as untrusted input — do not load an unaudited MCP server into a privileged agent. Probing third-party MCP endpoints for SSRF or auth weaknesses without permission may be illegal.
|
||||
|
||||
## Overview
|
||||
|
||||
The Model Context Protocol (MCP) lets AI agents discover and call external tools advertised by MCP servers. Each tool exposes a name and a natural-language **description** that the agent's LLM reads *before* deciding to call it. In early 2025, Invariant Labs disclosed that this description field is an attack surface: a malicious server can embed hidden instructions in a tool's description (a **tool poisoning attack**, OWASP **MCP03:2025**), and a capable model will silently follow them — exfiltrating files, leaking secrets, or redirecting tool calls — while returning a normal-looking response to the user. Because tool descriptions are loaded into the agent's context, tool poisoning is effectively indirect prompt injection delivered through the supply chain (MITRE ATLAS **AML.T0010 ML Supply Chain Compromise**).
|
||||
|
||||
Beyond poisoning, MCP servers introduce classic infrastructure risks: **tool shadowing** (a malicious server overrides a trusted tool's behavior), **rug pulls** (a tool's description changes after the user approved it), **toxic flows** (a combination of tools that enables data exfiltration), **SSRF** in tools that fetch URLs server-side, and **unauthenticated exposure** of MCP servers bound to network interfaces. This skill audits MCP servers end-to-end using Invariant Labs' **mcp-scan** for static and runtime analysis, plus manual checks for SSRF and authentication, and tool pinning to catch rug pulls.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Before adding a new MCP server to an agent stack (Claude Desktop, Cursor, VS Code, Windsurf, custom agents).
|
||||
- During a security review of an internally developed MCP server.
|
||||
- When validating that approved tools have not silently changed (rug-pull detection).
|
||||
- As a CI/CD gate that scans MCP configs and SKILL/tool definitions on every change.
|
||||
- During incident response when an agent took unexpected actions consistent with a poisoned tool.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.10+ and `uv` (for `uvx`), or pip.
|
||||
- The MCP config file(s) you want to scan (e.g. `~/.cursor/mcp.json`, `~/.vscode/mcp.json`, Claude Desktop config).
|
||||
- Install the tooling:
|
||||
|
||||
```bash
|
||||
# uv provides uvx (recommended runner for mcp-scan)
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh # or: pipx install uv
|
||||
|
||||
# mcp-scan (Invariant Labs) — no global install needed with uvx
|
||||
uvx mcp-scan@latest --help
|
||||
|
||||
# For the runtime proxy mode (separate extra)
|
||||
uvx --with "mcp-scan[proxy]" mcp-scan@latest proxy --help
|
||||
|
||||
# Manual probing helpers
|
||||
pip install requests mcp
|
||||
```
|
||||
|
||||
## Objectives
|
||||
|
||||
- Statically scan all installed MCP servers for tool poisoning, shadowing, rug pulls, and toxic flows.
|
||||
- Inspect raw tool/prompt/resource descriptions for hidden or obfuscated instructions.
|
||||
- Pin tool hashes to detect post-approval description changes (rug-pull defense).
|
||||
- Test URL-fetching tools for server-side request forgery (SSRF).
|
||||
- Verify MCP servers are authenticated and not exposed on untrusted interfaces.
|
||||
- Optionally enforce runtime guardrails with the mcp-scan proxy.
|
||||
|
||||
## MITRE ATT&CK Mapping
|
||||
|
||||
| ID | Official Name | Relevance |
|
||||
|----|---------------|-----------|
|
||||
| AML.T0010 | ML Supply Chain Compromise | A poisoned third-party MCP server is a supply-chain compromise of the agent |
|
||||
| AML.T0051.001 | LLM Prompt Injection: Indirect | Poisoned tool descriptions are indirect injection into the agent context |
|
||||
| AML.T0053 | LLM Plugin Compromise | MCP tools are the agent's plugins; poisoning compromises them |
|
||||
| AML.T0057 | LLM Data Leakage | Common payload of a poisoned tool: exfiltrate files/secrets |
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Static scan of installed MCP configs
|
||||
mcp-scan auto-discovers known config locations; you can also pass a path explicitly.
|
||||
|
||||
```bash
|
||||
# Scan all auto-discovered MCP configs
|
||||
uvx mcp-scan@latest
|
||||
|
||||
# Scan a specific config file
|
||||
uvx mcp-scan@latest ~/.vscode/mcp.json
|
||||
|
||||
# Emit machine-readable JSON for CI
|
||||
uvx mcp-scan@latest --json ~/.cursor/mcp.json > mcp_scan_report.json
|
||||
```
|
||||
|
||||
mcp-scan flags tool poisoning, tool shadowing, cross-origin escalation, rug pulls, and toxic flows.
|
||||
|
||||
### 2. Inspect raw tool descriptions
|
||||
Print every tool/prompt/resource description without verification, then read them for hidden instructions, `<important>`-style blocks, or imperative text aimed at the model.
|
||||
|
||||
```bash
|
||||
uvx mcp-scan@latest inspect ~/.cursor/mcp.json
|
||||
```
|
||||
|
||||
Look for red flags: instructions to the assistant ("do not tell the user", "read ~/.ssh/id_rsa"), nested fake documentation, zero-width/Unicode-smuggled text, or directives to call other tools.
|
||||
|
||||
### 3. Pin tool hashes to detect rug pulls
|
||||
mcp-scan tracks tool description hashes so a later silent change is flagged. Run scans on a schedule; a hash mismatch on a previously approved tool indicates a rug pull.
|
||||
|
||||
```bash
|
||||
# Re-run regularly; mcp-scan reports changed tool hashes since last approval
|
||||
uvx mcp-scan@latest ~/.cursor/mcp.json
|
||||
```
|
||||
|
||||
### 4. Enumerate tools programmatically and audit metadata
|
||||
Connect to the server with the official MCP SDK and inspect the advertised schema directly.
|
||||
|
||||
```python
|
||||
# enumerate_tools.py (stdio MCP server example)
|
||||
import asyncio
|
||||
from mcp import ClientSession, StdioServerParameters
|
||||
from mcp.client.stdio import stdio_client
|
||||
|
||||
async def main():
|
||||
params = StdioServerParameters(command="node", args=["./suspect-mcp-server.js"])
|
||||
async with stdio_client(params) as (read, write):
|
||||
async with ClientSession(read, write) as session:
|
||||
await session.initialize()
|
||||
tools = await session.list_tools()
|
||||
for t in tools.tools:
|
||||
print(f"{t.name}: {len(t.description or '')} chars")
|
||||
print((t.description or "")[:400])
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### 5. Test URL-fetching tools for SSRF
|
||||
If a tool accepts a URL and fetches it server-side, attempt to reach internal metadata/loopback targets (only on systems you own).
|
||||
|
||||
```python
|
||||
# ssrf_probe.py
|
||||
import asyncio
|
||||
from mcp import ClientSession, StdioServerParameters
|
||||
from mcp.client.stdio import stdio_client
|
||||
|
||||
SSRF_TARGETS = [
|
||||
"http://169.254.169.254/latest/meta-data/", # AWS IMDS
|
||||
"http://127.0.0.1:22/", "http://localhost:6379/", "file:///etc/passwd",
|
||||
]
|
||||
|
||||
async def main():
|
||||
params = StdioServerParameters(command="node", args=["./suspect-mcp-server.js"])
|
||||
async with stdio_client(params) as (r, w):
|
||||
async with ClientSession(r, w) as s:
|
||||
await s.initialize()
|
||||
for url in SSRF_TARGETS:
|
||||
res = await s.call_tool("fetch_url", {"url": url})
|
||||
body = str(res.content)[:200]
|
||||
print(f"[SSRF?] {url} -> {body}")
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### 6. Verify authentication and network exposure
|
||||
Check that remote MCP servers (HTTP/SSE transport) require authentication and are not bound to `0.0.0.0` on untrusted networks.
|
||||
|
||||
```bash
|
||||
# Confirm whether an SSE/HTTP MCP endpoint responds without credentials
|
||||
curl -s -i http://mcp-host:8000/sse | head -n 20
|
||||
|
||||
# Check listening interfaces of a locally running MCP server
|
||||
ss -tlnp | grep -E ':(8000|3000|6277)'
|
||||
```
|
||||
|
||||
An MCP endpoint that returns tool listings or accepts `tools/call` without auth is unauthenticated exposure — remediate with a token/OAuth and bind to localhost or an authenticated gateway.
|
||||
|
||||
### 7. Enforce runtime guardrails (optional)
|
||||
For continuous protection, route agent MCP traffic through the mcp-scan proxy, which checks tool calls, data-flow constraints, PII, and indirect injection in real time.
|
||||
|
||||
```bash
|
||||
uvx --with "mcp-scan[proxy]" mcp-scan@latest proxy
|
||||
```
|
||||
|
||||
### 8. Report findings
|
||||
Document each finding with server, tool, evidence (the poisoned description / SSRF response / unauth listing), severity, and ATLAS mapping. Recommend removing or sandboxing poisoned servers, adding auth, pinning approved tools, and enabling the proxy.
|
||||
|
||||
## Tools and Resources
|
||||
|
||||
| Tool | Purpose | Source |
|
||||
|------|---------|--------|
|
||||
| mcp-scan | Static + runtime MCP security scanner | https://github.com/invariantlabs-ai/mcp-scan |
|
||||
| MCP Python SDK | Programmatic tool enumeration / calls | https://github.com/modelcontextprotocol/python-sdk |
|
||||
| OWASP MCP Top 10 | MCP risk reference (MCP03 Tool Poisoning) | https://owasp.org/www-project-mcp-top-10/ |
|
||||
| Invariant Labs blog | Tool poisoning disclosure | https://invariantlabs.ai/blog/introducing-mcp-scan |
|
||||
| MITRE ATLAS | AI threat technique taxonomy | https://atlas.mitre.org/ |
|
||||
|
||||
## MCP Threat Reference
|
||||
|
||||
| Threat | Description | Detection |
|
||||
|--------|-------------|-----------|
|
||||
| Tool poisoning | Hidden instructions in tool description | mcp-scan scan / inspect |
|
||||
| Tool shadowing | Malicious server overrides trusted tool | mcp-scan cross-origin checks |
|
||||
| Rug pull | Description changes after approval | mcp-scan tool pinning (hash) |
|
||||
| Toxic flow | Tool combo enabling exfiltration | mcp-scan toxic-flow analysis |
|
||||
| SSRF | URL-fetch tool reaches internal targets | ssrf_probe against owned server |
|
||||
| Unauth exposure | MCP endpoint with no auth | curl/ss interface and auth check |
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [ ] All installed MCP configs statically scanned with mcp-scan
|
||||
- [ ] Raw tool/prompt/resource descriptions inspected for hidden instructions
|
||||
- [ ] Tool hashes pinned and rug-pull detection enabled
|
||||
- [ ] Tools enumerated programmatically via the MCP SDK
|
||||
- [ ] URL-fetching tools tested for SSRF against owned targets
|
||||
- [ ] Authentication and network exposure of remote servers verified
|
||||
- [ ] Runtime proxy guardrails evaluated or deployed where appropriate
|
||||
- [ ] Findings mapped to MITRE ATLAS AML.T0010 and OWASP MCP03:2025
|
||||
- [ ] Severity assigned and remediation documented for each finding
|
||||
- [ ] Re-scan scheduled to catch future rug pulls
|
||||
@@ -0,0 +1,59 @@
|
||||
# API Reference — MCP Server Auditing
|
||||
|
||||
## mcp-scan CLI (Invariant Labs)
|
||||
|
||||
Run via uvx (no global install): `uvx mcp-scan@latest`
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `mcp-scan` / `mcp-scan scan [config]` | Statically scan MCP configs for poisoning, shadowing, rug pulls, toxic flows |
|
||||
| `mcp-scan inspect [config]` | Print tool/prompt/resource descriptions without verification |
|
||||
| `mcp-scan proxy` | Runtime proxy: monitor and guardrail MCP traffic (requires `[proxy]` extra) |
|
||||
| `--json` | Emit machine-readable JSON report |
|
||||
|
||||
Examples:
|
||||
```bash
|
||||
uvx mcp-scan@latest ~/.vscode/mcp.json
|
||||
uvx mcp-scan@latest inspect ~/.cursor/mcp.json
|
||||
uvx --with "mcp-scan[proxy]" mcp-scan@latest proxy
|
||||
```
|
||||
|
||||
mcp-scan features: tool pinning (hash-based rug-pull detection), cross-origin escalation checks, toxic-flow analysis.
|
||||
|
||||
## MCP Python SDK
|
||||
|
||||
Install: `pip install mcp`
|
||||
|
||||
| API | Description |
|
||||
|-----|-------------|
|
||||
| `StdioServerParameters(command, args)` | Define a stdio MCP server to launch |
|
||||
| `stdio_client(params)` | Async context manager yielding (read, write) streams |
|
||||
| `ClientSession(read, write)` | MCP client session |
|
||||
| `session.initialize()` | Perform MCP handshake |
|
||||
| `session.list_tools()` | Return advertised tools (`.tools[].name`, `.description`, `.inputSchema`) |
|
||||
| `session.list_prompts()` | List advertised prompts |
|
||||
| `session.list_resources()` | List advertised resources |
|
||||
| `session.call_tool(name, args)` | Invoke a tool (use for SSRF probing on owned servers) |
|
||||
|
||||
## Common MCP config locations
|
||||
|
||||
| Client | Path |
|
||||
|--------|------|
|
||||
| Cursor | `~/.cursor/mcp.json` |
|
||||
| VS Code | `~/.vscode/mcp.json` |
|
||||
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) |
|
||||
|
||||
## SSRF probe targets (owned systems only)
|
||||
|
||||
| Target | Purpose |
|
||||
|--------|---------|
|
||||
| `http://169.254.169.254/latest/meta-data/` | AWS instance metadata (IMDS) |
|
||||
| `http://metadata.google.internal/` | GCP metadata |
|
||||
| `http://127.0.0.1:<port>/` | Loopback services |
|
||||
| `file:///etc/passwd` | Local file disclosure |
|
||||
|
||||
## External References
|
||||
|
||||
- mcp-scan README: https://github.com/invariantlabs-ai/mcp-scan/blob/main/README.md
|
||||
- MCP spec: https://modelcontextprotocol.io/specification
|
||||
- MCP Python SDK: https://github.com/modelcontextprotocol/python-sdk
|
||||
@@ -0,0 +1,32 @@
|
||||
# Standards and References — Auditing MCP Servers for Tool Poisoning
|
||||
|
||||
## MITRE ATLAS References
|
||||
|
||||
| Technique ID | Name | Tactic | Rationale |
|
||||
|--------------|------|--------|-----------|
|
||||
| AML.T0010 | ML Supply Chain Compromise | Initial Access | A poisoned third-party MCP server compromises the agent supply chain |
|
||||
| AML.T0051.001 | LLM Prompt Injection: Indirect | Initial Access | Poisoned tool descriptions are indirect injection into agent context |
|
||||
| AML.T0053 | LLM Plugin Compromise | Execution | MCP tools are the agent's plugins; poisoning compromises them |
|
||||
| AML.T0057 | LLM Data Leakage | Exfiltration | Poisoned tools commonly exfiltrate files/secrets |
|
||||
|
||||
## NIST AI RMF References
|
||||
|
||||
| ID | Name | Rationale |
|
||||
|----|------|-----------|
|
||||
| MANAGE-2.2 | Mechanisms are in place and applied to sustain the value of deployed AI systems | Auditing third-party MCP tools manages/sustains safe agent operation |
|
||||
|
||||
## OWASP MCP Top 10 (2025)
|
||||
|
||||
| ID | Name | Rationale |
|
||||
|----|------|-----------|
|
||||
| MCP03:2025 | Tool Poisoning | Primary risk this skill audits |
|
||||
| MCP01:2025 | Prompt Injection | Poisoned descriptions inject the agent |
|
||||
|
||||
## Official Resources
|
||||
|
||||
- mcp-scan (Invariant Labs): https://github.com/invariantlabs-ai/mcp-scan
|
||||
- Invariant Labs tool-poisoning disclosure: https://invariantlabs.ai/blog/introducing-mcp-scan
|
||||
- OWASP MCP Top 10: https://owasp.org/www-project-mcp-top-10/
|
||||
- Model Context Protocol spec: https://modelcontextprotocol.io/
|
||||
- MITRE ATLAS: https://atlas.mitre.org/
|
||||
- NIST AI RMF: https://www.nist.gov/itl/ai-risk-management-framework
|
||||
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env python3
|
||||
# For authorized MCP server auditing only. Do not scan servers you do not control
|
||||
# or lack written permission to assess.
|
||||
"""MCP tool-poisoning audit agent.
|
||||
|
||||
Two modes:
|
||||
static -- run Invariant Labs mcp-scan over an MCP config and parse results,
|
||||
plus a local heuristic scan of tool descriptions in the config.
|
||||
enum -- launch a stdio MCP server, enumerate tools, and heuristically flag
|
||||
poisoned descriptions (hidden instructions, smuggled unicode).
|
||||
|
||||
Examples:
|
||||
python agent.py static --config ~/.cursor/mcp.json
|
||||
python agent.py enum --command node --args ./suspect-mcp-server.js
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
# Heuristics for instructions aimed at the assistant inside a tool description.
|
||||
POISON_PATTERNS = [
|
||||
r"do not (tell|inform|mention to) the user",
|
||||
r"ignore (previous|prior|all) instructions",
|
||||
r"<important>|<system>|\[system\]",
|
||||
r"read .*(\.ssh|id_rsa|\.env|credentials|passwd)",
|
||||
r"(send|exfiltrate|post) .* to https?://",
|
||||
r"before (using|calling) (this|any) tool,? (you must|always)",
|
||||
r"call (the )?\w+ tool (first|before)",
|
||||
]
|
||||
SMUGGLE = re.compile(r"[---\U000e0000-\U000e007f]")
|
||||
|
||||
|
||||
def heuristic_flags(description: str):
|
||||
flags = []
|
||||
low = (description or "").lower()
|
||||
for pat in POISON_PATTERNS:
|
||||
if re.search(pat, low):
|
||||
flags.append(f"pattern:{pat}")
|
||||
if SMUGGLE.search(description or ""):
|
||||
flags.append("unicode-smuggling")
|
||||
if len(description or "") > 1500:
|
||||
flags.append("oversized-description")
|
||||
return flags
|
||||
|
||||
|
||||
def run_static(args):
|
||||
findings = {"ts": datetime.now(timezone.utc).isoformat(),
|
||||
"config": args.config, "atlas": "AML.T0010", "scanner": None,
|
||||
"heuristic": []}
|
||||
|
||||
# 1. Invoke mcp-scan if available
|
||||
runner = shutil.which("uvx") or shutil.which("mcp-scan")
|
||||
if runner:
|
||||
cmd = ([runner, "mcp-scan@latest", "--json", args.config]
|
||||
if "uvx" in runner else [runner, "--json", args.config])
|
||||
try:
|
||||
res = subprocess.run(cmd, capture_output=True, text=True, timeout=240)
|
||||
try:
|
||||
findings["scanner"] = json.loads(res.stdout)
|
||||
except json.JSONDecodeError:
|
||||
findings["scanner"] = {"raw": res.stdout[:4000], "stderr": res.stderr[:1000]}
|
||||
except subprocess.TimeoutExpired:
|
||||
findings["scanner"] = {"error": "mcp-scan timed out"}
|
||||
else:
|
||||
findings["scanner"] = {"error": "uvx/mcp-scan not found; install uv: "
|
||||
"curl -LsSf https://astral.sh/uv/install.sh | sh"}
|
||||
|
||||
# 2. Local heuristic scan of any descriptions embedded in the config
|
||||
try:
|
||||
with open(args.config, encoding="utf-8") as fh:
|
||||
cfg = json.load(fh)
|
||||
for desc in _walk_descriptions(cfg):
|
||||
f = heuristic_flags(desc)
|
||||
if f:
|
||||
findings["heuristic"].append({"flags": f, "snippet": desc[:200]})
|
||||
except (OSError, json.JSONDecodeError) as exc:
|
||||
findings["heuristic"].append({"error": str(exc)})
|
||||
|
||||
print(json.dumps(findings, indent=2))
|
||||
return findings
|
||||
|
||||
|
||||
def _walk_descriptions(obj):
|
||||
"""Yield any 'description' string values found anywhere in a nested config."""
|
||||
if isinstance(obj, dict):
|
||||
for k, v in obj.items():
|
||||
if k == "description" and isinstance(v, str):
|
||||
yield v
|
||||
else:
|
||||
yield from _walk_descriptions(v)
|
||||
elif isinstance(obj, list):
|
||||
for item in obj:
|
||||
yield from _walk_descriptions(item)
|
||||
|
||||
|
||||
def run_enum(args):
|
||||
try:
|
||||
import asyncio
|
||||
from mcp import ClientSession, StdioServerParameters
|
||||
from mcp.client.stdio import stdio_client
|
||||
except ImportError:
|
||||
print("Install: pip install mcp", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
async def _enum():
|
||||
params = StdioServerParameters(command=args.command, args=args.args or [])
|
||||
out = {"ts": datetime.now(timezone.utc).isoformat(),
|
||||
"server": f"{args.command} {' '.join(args.args or [])}",
|
||||
"atlas": "AML.T0010", "tools": []}
|
||||
async with stdio_client(params) as (r, w):
|
||||
async with ClientSession(r, w) as s:
|
||||
await s.initialize()
|
||||
tools = await s.list_tools()
|
||||
for t in tools.tools:
|
||||
flags = heuristic_flags(t.description or "")
|
||||
out["tools"].append({
|
||||
"name": t.name,
|
||||
"desc_len": len(t.description or ""),
|
||||
"flags": flags,
|
||||
"verdict": "POISONED?" if flags else "clean",
|
||||
})
|
||||
print(json.dumps(out, indent=2))
|
||||
return out
|
||||
|
||||
try:
|
||||
asyncio.run(_enum())
|
||||
except Exception as exc: # connection/protocol errors
|
||||
print(f"[!] MCP enumeration failed: {exc}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="MCP tool-poisoning audit agent")
|
||||
sub = ap.add_subparsers(dest="mode", required=True)
|
||||
|
||||
ps = sub.add_parser("static", help="Run mcp-scan + heuristic scan on a config")
|
||||
ps.add_argument("--config", required=True, help="Path to MCP config JSON")
|
||||
|
||||
pe = sub.add_parser("enum", help="Enumerate tools from a stdio MCP server")
|
||||
pe.add_argument("--command", required=True, help="Server launch command, e.g. node")
|
||||
pe.add_argument("--args", nargs="*", help="Arguments to the server command")
|
||||
|
||||
args = ap.parse_args()
|
||||
if args.mode == "static":
|
||||
run_static(args)
|
||||
else:
|
||||
run_enum(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user