{"slug": "i-caught-a-trojan-in-my-mcp-marketplace-here-s-the-8-layer-defense-i-built", "title": "I caught a trojan in my MCP marketplace. Here's the 8-layer defense I built.", "summary": "A developer discovered a Windows trojan, Trojan:Win64/Lazy.PGPK!MTB, hidden in a typosquatting MCP server package on their marketplace. In response, they built an 8-layer defense pipeline that includes recursive zip scanning, YARA rules for 17 malware families, and HTTP request inspection to prevent future incidents.", "body_md": "Two weeks ago, a Windows trojan slipped into my MCP (Model Context Protocol) marketplace. The malware was `Trojan:Win64/Lazy.PGPK!MTB`\n\n, hidden inside a nested zip in a skill package.\n\nThis is the technical writeup of what happened, what I built to prevent it, and the architecture of the 8-layer defense pipeline now running in production.\n\n**Vector:** Typosquatting GitHub repository\n\nA legitimate MCP server `prospector-mcp-email-finder`\n\nexisted. An attacker created a typosquatting copy at `JuanquiFortuny/prospector-mcp-email-finder`\n\n(now taken down) with an identical README — but with a \"Download Latest Release\" badge linking to a malicious zip on `raw.githubusercontent.com`\n\n.\n\n**The payload:**\n\n```\nApplication.cmd  →  'start unit.exe package.txt'\npackage.txt      →  298 KB of obfuscated Lua bytecode\nunit.exe         →  872 KB PE32+ Windows executable\n```\n\nWindows Defender flagged `unit.exe`\n\nas `Trojan:Win64/Lazy.PGPK!MTB`\n\n. The `Application.cmd → unit.exe + package.txt`\n\npattern is a classic staged launcher — small CMD wrapper executes the binary with the bytecode payload as input.\n\n**How it got in:** My import script (which fetches MCP servers from GitHub) downloaded the zip and committed it to `dist/skills/`\n\nwithout scanning inside it. My existing audit pipeline (Sentinel L1.5 + L1.6) only checked skill metadata — name, description, system_prompt. It never looked inside the actual package.\n\nCheapest layer. Runs on every skill's metadata:\n\n`ignore previous instructions`\n\n)`Access-Control-Allow-Origin: *`\n\n(warning)18 Semgrep-equivalent rules implemented as JS regex (no Semgrep binary needed):\n\n18 secret detection patterns:\n\n`sk_live_*`\n\n, `sk_test_*`\n\n)`ghp_*`\n\n, `gho_*`\n\n, `ghs_*`\n\n)`AKIA*`\n\n, `aws_secret_access_key`\n\n)OSV API for known vulnerable dependencies.\n\n**Bug fix (from peer review):** `process.env.X`\n\nreferences were triggering MCP-SL-001 (hardcoded API key). Fixed by stripping `process.env.*`\n\npatterns before running secret detection.\n\nThis is the layer I built after the trojan incident. It opens the package zip (recursively — zips inside zips) and scans for:\n\n``` js\nconst BINARY_EXTENSIONS = ['.exe', '.dll', '.scr', '.msi'];\nconst LAUNCHER_EXTENSIONS = ['.bat', '.cmd', '.vbs', '.ps1'];\n```\n\nPlus 8 regex patterns for:\n\n`start X.exe Y.txt`\n\n(the exact prospector signature)`function(o,R,F,U,b,p,E,M,Z,W,...)`\n\n(high-arity function signature)`raw.githubusercontent.com/.../...zip`\n\n`-encodedcommand`\n\nwith long base64`eval(atob(...))`\n\nobfuscation**Quarantine flow:** Any critical finding → score 0, risk_level `critical`\n\n, status `quarantined`\n\n, removed from catalog, listed at `/api/security?view=quarantine`\n\n.\n\nYARA-equivalent rules for specific malware families, each with MITRE ATT&CK technique IDs:\n\n| Family | MITRE | What it does |\n|---|---|---|\n| Win64/Lazy.PGPK | T1027.002 | The trojan that hit us |\n| Emotet | T1027.011 | Banking trojan, Office macro delivery |\n| Cobalt Strike | T1071.001 | Post-exploitation beacon |\n| Mimikatz | T1003.001 | LSASS credential dumper |\n| QakBot | T1027 | Banking trojan |\n| TrickBot | T1027 | Modular trojan, ransomware precursor |\n| Agent Tesla | T1056.001 | Keylogger |\n| RedLine Stealer | T1555 | Browser credential theft |\n| Vidar Stealer | T1555 | Browser + crypto wallet theft |\n| Raccoon Stealer | T1555 | MaaS stealer |\n| LummaC2 Stealer | T1555 | Telegram-sold stealer |\n| AsyncRAT | T1071.001 | Open-source RAT |\n| njRAT | T1071.001 | .NET RAT |\n| Remcos RAT | T1071.001 | Commercial RAT |\n| SolarMarker | T1027 | SEO-poisoned backdoor |\n| Lokibot | T1555 | Credential stealer |\n| DoS tools | T1499 | hping3, slowloris, goldeneye |\n\nAny match → instant quarantine.\n\nInspects every incoming HTTP request:\n\n```\nSQLi (7): UNION SELECT, OR 1=1, stacked, time-based, info_schema, hex\nXSS (7): script, event handlers, javascript:, data:, vbscript:, img/svg\nPath traversal (5): ../, encoded, /etc/passwd, /proc/self, Windows\nSSRF (6): AWS/GCP/Azure metadata, file://, gopher://, dict://\nCommand injection (5): backticks, $(), chained, pipe, && ||\nNoSQL (3): $where, $ne, $gt\nPrototype pollution: __proto__, constructor.prototype\nSSTI: Jinja2 {{ }}, Twig {% %}, JS ${ }\nLog injection: \\n \\r header injection\n```\n\n**Auto-ban** after 5 WAF hits in 10 minutes (1-hour ban).\n\nFake vulnerable paths that auto-ban scanners for 24 hours:\n\n`/.env`\n\n→ serves fake env file with canary tokens`/admin`\n\n→ fake admin login form`/wp-admin`\n\n→ fake WordPress login`/.git/config`\n\n→ fake git config`/.aws/credentials`\n\n→ fake AWS credentials`/.ssh/id_rsa`\n\n→ fake SSH key`/phpmyadmin`\n\n, `/backup.sql`\n\n, `/server-status`\n\n, `/.DS_Store`\n\n, etc.Each hit: IP banned 24h + logged publicly at `/api/security?view=honeypot`\n\n.\n\nReal-time IOC feeds from abuse.ch (free, no API key):\n\nUsed to check skill source URLs and file hashes.\n\nIf any layer flags a skill as critical/high:\n\n`_data/quarantine/`\n\n`/api/security?view=quarantine`\n\nfor transparencyRe-audited all 7,063 skills with the 8 layers. **0 in quarantine.** The catalog was clean except for the one we already removed.\n\n```\nRequest → Honeypot check → WAF (40 rules) → Handler\n                                          ↓\nSkill import → L1.5 (6) → L1.6 (36) → L1.7 (8) → L1.8 (17) → Quarantine?\n                                                                ↓ no\n                                                          Catalog (public)\n                                                                ↓ yes\n                                                          _data/quarantine/\n                                                          (publicly listed)\n```\n\nStack: Vercel Hobby ($0/mo), GitHub Actions (free), Docker + gVisor for L2 sandbox, abuse.ch feeds.\n\n— *Edison Flores, AliceLabs LLC*", "url": "https://wpnews.pro/news/i-caught-a-trojan-in-my-mcp-marketplace-here-s-the-8-layer-defense-i-built", "canonical_source": "https://dev.to/edison_flores_6d2cd381b13/i-caught-a-trojan-in-my-mcp-marketplace-heres-the-8-layer-defense-i-built-gk4", "published_at": "2026-07-18 03:27:03+00:00", "updated_at": "2026-07-18 04:28:06.317710+00:00", "lang": "en", "topics": ["ai-safety", "developer-tools", "ai-infrastructure"], "entities": ["MCP marketplace", "Trojan:Win64/Lazy.PGPK!MTB", "GitHub", "Windows Defender", "YARA", "MITRE ATT&CK"], "alternates": {"html": "https://wpnews.pro/news/i-caught-a-trojan-in-my-mcp-marketplace-here-s-the-8-layer-defense-i-built", "markdown": "https://wpnews.pro/news/i-caught-a-trojan-in-my-mcp-marketplace-here-s-the-8-layer-defense-i-built.md", "text": "https://wpnews.pro/news/i-caught-a-trojan-in-my-mcp-marketplace-here-s-the-8-layer-defense-i-built.txt", "jsonld": "https://wpnews.pro/news/i-caught-a-trojan-in-my-mcp-marketplace-here-s-the-8-layer-defense-i-built.jsonld"}}