我的MCP安全扫描器错过了2026年最糟糕的MCP RCE:这里是一道规则的修复
My MCP Security Scanner Missed 2026's Worst MCP RCE: Here Is the One-Rule Fix
一个静态分析器, 扫描MCP(模式背景协议)服务器, 规矩应该是一条贯穿一切的道路 本周我用自己的研究笔记坐下来,并做了简单的检查:MCP007会不会抓住今年针对MCP服务器披露的4个真正的路径逆向CVE? 它会错过每一个。 包括最烂的 现实世界背景 以下是2026年实际作为CVES发运的,均以MCP服务器发运,均分享同根原因: CVE Server Sink Effect CVE-2026-40576 Excellent-mcp-server文件写道 Traversal CVE-2026-84201 appium-mcp-server Pathers traversal CVE-2026-44336 PraisonAI MCP Python...
The hook A few months back I shipped , a static analyzer that scans MCP (Model Context Protocol) servers for the vulnerability classes that keep showing up in this ecosystem: command injection, SSRF, and path traversal. Rule was supposed to be the path traversal catch-all. This week I sat down with my own research notes and ran a simple gut-check: would MCP007 have caught the four real path-traversal CVEs disclosed against MCP servers this year? It would have missed every single one. Including the worst one. Real-world context Here is what actually shipped as CVEs in 2026, all in MCP servers, all sharing the same root cause: CVE Server Sink Impact CVE-2026-40576 excel-mcp-server file write Path traversal CVE-2026-84201 appium-mcp-server Path traversal CVE-2026-44336 PraisonAI MCP Python write RCE via site-packages injection CVE-2026-27825 mcp-atlassian CVSS 9.1, unauthenticated RCE (chained with SSRF CVE-2026-27826 to overwrite or drop a cron entry) Four different maintainers, four different tools, the exact same blind spot: a file path built from caller-controlled input, written without a directory-boundary check. The bug in is the nastiest: no auth needed, no restart needed, straight to a shell. So I opened my own rule file and read the docstring out loud: There it is. My rule was scoped to reads from day one, and every real-world exploit this year happened on the write side. A scanner whose entire job is catching this bug class was structurally blind to the half of it that is actually landing CVSS 9+ scores. Architecture: how MCP007 actually works The rules in are simple on purpose: line-scan regex matching without an AST, so they run fast across any language mcpscan supports. Each rule has three regex layers: The original sink regex, verbatim from : Notice: is in there, but Python's is also the write sink (). The regex does not care about mode, so in theory some writes already slip through. But , , , , and were not matched at all. That is the actual gap that let the shape of CVE-2026-27825 through. Step-by-step: the fix Since and already do what is needed (detecting a dynamically-built path and escalating severity when a literal shows up), the fix is additive: a second sink pattern, reusing the same detection pipeline. 1. Add write-sink regexes next to the read ones: 2. Check both sink families per line, not just one: I bumped write-sink hits straight to even without a literal . An attacker-controlled destination path is a worse primitive than an attacker-controlled source path, because the payload usually rides along in the same request (as seen in ). 3. Prove it against the real shape. Drop this into a fixture and run the scanner: Before the patch, that fixture produced zero findings. That is the whole bug, in one diff. Gotchas and edge cases mode ambiguity. is technically read and write. I did not try to parse complex mode strings. The regex also matches , , modes (with optional ) as an additional signal, and duplicate matches against are harmless because is a tuple checked in order with . False positives on legitimate atomic writes. Code that runs will now flag, correctly. That is still an unvalidated destination path even if it is only a tempfile suffix. Do not suppress this; validate the base path instead. Severity inflation. Escalating all write-sink hits to HIGH (not just ones with a literal ) will produce more HIGH findings than before. That is intentional given the CVE data. If you fork this, expect your triage backlog to grow, which is the point. Regex limitations. It still will not catch a path built three functions away from the sink. That is a real limitation of the entire rule, not something this patch fixes. It is worth noting in your own documentation so users do not place blind trust in a clean scan. Actionable takeaways If you maintain a security scanner, periodically re-run it against your own list of recent real CVEs. Rules drift stale silently: nothing breaks, it just quietly stops catching the vulnerabilities that matter. When a rule docstring says "read" and the threat landscape has moved to "write", that is a signal, not a footnote. Read your own comments critically. Reuse your detection pipeline ( and here) instead of writing a parallel rule file. This keeps the surface area small and makes it easier to keep both sink families in sync. Write-sink path traversal deserves a harder default severity than read-sink. An attacker-controlled destination with attacker-controlled content is a strictly worse primitive than an attacker-controlled source. The patch above is about 15 lines. The gap it closes took four independent CVEs and one CVSS 9.1 unauthenticated RCE chain to surface. Check your own blind spots before someone else's CVE does it for you.