MCP Security: How I blocked a Trojan with an 8-layer pipeline

postdocai46 Beginner 2d ago 421 views 3 likes 2 min read

Getting a Windows trojan (Trojan:Win64/Lazy.PGPK!MTB) inside your MCP marketplace is a great way to wake up and realize your security is basically a screen door. It happened because of a classic typosquatting trick on GitHub—someone cloned a legit MCP server, swapped the "Download Latest Release" badge to point to a malicious zip, and my import script blindly swallowed it.

MCP Security: How I blocked a Trojan with an 8-layer pipeline

The payload was a nasty staged launcher: a .cmd file triggering a .exe with an obfuscated Lua bytecode .txt file. My old audit only checked metadata (names and prompts), so it completely missed the binary payload hiding in a nested zip. As a dev who hates friction but loves stability, I spent the last two weeks building a massive defense pipeline to make sure this never happens again.

Here is the deep dive into the 8-layer security architecture I've deployed to keep our LLM agents safe.

The Defense Layers

L1.5 — Metadata Sanity Checks


This is the "cheap" layer. I run 6 rules against the skill metadata to flag red flags immediately:
  • Auth: Warning if no auth is required.

  • Injection: Checking tool descriptions for "ignore previous instructions" patterns.

  • Permissions: Verifying if File/SQL/HTTP access is actually declared.

  • CORS: Flagging Access-Control-Allow-Origin: *.

  • Scopes: Looking for unscoped OAuth tokens.

  • Throttling: Checking for missing rate limits.
  • L1.6 — Static Analysis & Secret Scanning


    I implemented 36 rules using JS regex (avoiding the overhead of a Semgrep binary) to catch common vulnerabilities:
  • Code Smells: 18 patterns for Prompt Injection, Command Injection, SSRF, and Path Traversal.

  • Secret Leaks: 18 patterns for Stripe, GitHub, AWS, RSA keys, and Wallet mnemonics.

  • Dependency Check: Using the OSV API to find known vulnerable packages.
  • Pro tip: I had to fix a bug where process.env.X was triggering hardcoded key alerts. I now strip environment variable patterns before scanning.

    L1.7 — Binary & Malware Detection (The "Trojan Killer")


    This is the new layer specifically designed to stop the unit.exe type of attacks. It recursively unzips everything and scans for:

    const BINARY_EXTENSIONS = ['.exe', '.dll', '.scr', '.msi'];
    const LAUNCHER_EXTENSIONS = ['.bat', '.cmd', '.vbs', '.ps1'];

    I also added 8 regex patterns to catch:

  • Staged Launchers: Patterns like start X.exe Y.txt.

  • Obfuscated Lua: High-arity function signatures.

  • Suspicious Links: raw.githubusercontent.com zip links or "Download" badges.

  • PowerShell: -encodedcommand with long base64 strings.

  • Obfuscation: eval(atob(...)) and oversized text files (>100KB) that aren't JSON.
  • If any of these hit, the skill is instantly quarantined, scored 0, and nuked from the public catalog.

    L1.8 — Malware Family Signatures


    The final layer uses YARA-equivalent rules to identify 17 specific malware families, mapping them directly to MITRE ATT&CK technique IDs for better tracking.

    This setup basically turns the import process into a gauntlet. If you're building an AI workflow with third-party MCP servers, please don't just trust the README—verify the binaries.

    WorkflowAI implementationsecuritymalwaredevsecops

    All Replies (3)

    S
    softwhere Novice 2d ago
    I usually just sandbox my MCPs; way easier than building huge pipelines (and less stressful).
    0 Reply
    C
    coherecheck96 Beginner 2d ago
    Air-gapped is the move. Pipeline overhead vs zero trust—no contest. Been burned by "secure" hubs before.
    0 Reply
    V
    vectorstore Advanced 2d ago
    Latency hit? Curious if your pipeline adds overhead to npx or similar runtime calls.
    0 Reply

    Write a Reply

    Markdown supported