CryptoNetHub

I caught a trojan in my MCP marketplace. Here's the 8-layer defense I built.

Two weeks ago, a Windows trojan slipped into my MCP (Model Context Protocol) marketplace. The malware wasTrojan:Win64/Lazy. PGPK!MTB, hidden inside a nested zip in a skill package. This is the technical writeup. . .

· 5 min read

I caught a trojan in my MCP marketplace. Here's the 8-layer defense I built.

Two weeks ago, a Windows trojan slipped into my MCP (Model Context Protocol) marketplace. The malware wasTrojan:Win64/Lazy. PGPK!MTB, hidden inside a nested zip in a skill package. This 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. The attackVector:Typosquatting GitHub repositoryA legitimate MCP serverprospector-mcp-email-finderexisted. An attacker created a typosquatting copy atJuanquiFortuny/prospector-mcp-email-finder(now taken down) with an identical README — but with a "Download Latest Release" badge linking to a malicious zip onraw. githubusercontent. com. The payload:Application. cmd → 'start unit. exe package. txt' package. txt → 298 KB of obfuscated Lua bytecode unit. exe → 872 KB PE32+ Windows executableEnter fullscreen modeExit fullscreen modeWindows Defender flaggedunit. exeasTrojan:Win64/Lazy. PGPK!MTB. TheApplication. cmd → unit. exe + package. txtpattern is a classic staged launcher — small CMD wrapper executes the binary with the bytecode payload as input. How it got in:My import script (which fetches MCP servers from GitHub) downloaded the zip and committed it todist/skills/without 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. The defense — 8 layersL1. 5 — Metadata checks (6 rules)Cheapest layer. Runs on every skill's metadata:AUTH: Is auth required? (warning if not)TOOL_DESCRIPTIONS: Prompt injection patterns (ignore previous instructions)INPUT_VALIDATION: File/SQL/HTTP access declared in metadataCORS_ORIGIN:Access-Control-Allow-Origin: *(warning)OAUTH_SCOPES: Unscoped tokensRATE_LIMITING: No rate limit declaredL1. 6 — Semgrep + Secrets + OSV (36 rules)18 Semgrep-equivalent rules implemented as JS regex (no Semgrep binary needed):Prompt injection (6 patterns)Command injection (4 patterns)SSRF (2 patterns)Path traversal (2 patterns)Tool name spoofing (1 pattern)Hardcoded secrets (3 patterns)18 secret detection patterns:Stripe (sk_live_*,sk_test_*)GitHub (ghp_*,gho_*,ghs_*)AWS (AKIA*,aws_secret_access_key)Private keys (RSA/EC/OPENSSH)Wallet mnemonicsJWT, Slack, Discord, Google, Twilio tokensOSV API for known vulnerable dependencies. Bug fix (from peer review):process. env. Xreferences were triggering MCP-SL-001 (hardcoded API key). Fixed by strippingprocess. env. *patterns before running secret detection. L1. 7 — Binary & malware detection (8 patterns) — NEWThis is the layer I built after the trojan incident. It opens the package zip (recursively — zips inside zips) and scans for:constBINARY_EXTENSIONS=['. exe','. dll','. scr','. msi'];constLAUNCHER_EXTENSIONS=['. bat','. cmd','. vbs','. ps1'];Enter fullscreen modeExit fullscreen modePlus 8 regex patterns for:Staged launchers:start X. exe Y. txt(the exact prospector signature)Obfuscated Lua bytecode:function(o,R,F,U,b,p,E,M,Z,W,. . . )(high-arity function signature)External download URLs:raw. githubusercontent. com/. . . /. . . zip"Download Latest Release" badges with external zip linksPowerShell-encodedcommandwith long base64eval(atob(. . . ))obfuscationLua numeric comment markersOversized text files >100KB that aren't valid JSON (bytecode payloads)Quarantine flow:Any critical finding → score 0, risk_levelcritical, statusquarantined, removed from catalog, listed at/api/security?view=quarantine. L1. 8 — Malware family signatures (17 families) — NEWYARA-equivalent rules for specific malware families, each with MITRE ATT&CK technique IDs:FamilyMITREWhat it doesWin64/Lazy. PGPKT1027. 002The trojan that hit usEmotetT1027. 011Banking trojan, Office macro deliveryCobalt StrikeT1071. 001Post-exploitation beaconMimikatzT1003. 001LSASS credential dumperQakBotT1027Banking trojanTrickBotT1027Modular trojan, ransomware precursorAgent TeslaT1056. 001KeyloggerRedLine StealerT1555Browser credential theftVidar StealerT1555Browser + crypto wallet theftRaccoon StealerT1555MaaS stealerLummaC2 StealerT1555Telegram-sold stealerAsyncRATT1071. 001Open-source RATnjRATT1071. 001. NET RATRemcos RATT1071. 001Commercial RATSolarMarkerT1027SEO-poisoned backdoorLokibotT1555Credential stealerDoS toolsT1499hping3, slowloris, goldeneyeAny match → instant quarantine. WAF — Web Application Firewall (40 rules) — NEWInspects every incoming HTTP request:SQLi (7): UNION SELECT, OR 1=1, stacked, time-based, info_schema, hex XSS (7): script, event handlers, javascript:, data:, vbscript:, img/svg Path traversal (5): . . /, encoded, /etc/passwd, /proc/self, Windows SSRF (6): AWS/GCP/Azure metadata, file://, gopher://, dict:// Command injection (5): backticks, $(), chained, pipe, && || NoSQL (3): $where, $ne, $gt Prototype pollution: __proto__, constructor. prototype SSTI: Jinja2 {{ }}, Twig {% %}, JS ${ } Log injection: \n \r header injectionEnter fullscreen modeExit fullscreen modeAuto-banafter 5 WAF hits in 10 minutes (1-hour ban). Honeypot (50+ paths) — NEWFake vulnerable paths that auto-ban scanners for 24 hours:/. env→ serves fake env file with canary tokens/admin→ fake admin login form/wp-admin→ fake WordPress login/. git/config→ fake git config/. aws/credentials→ fake AWS credentials/. ssh/id_rsa→ fake SSH key/phpmyadmin,/backup. sql,/server-status,/. DS_Store, etc. Each hit: IP banned 24h + logged publicly at/api/security?view=honeypot. Threat Intelligence (3 feeds) — NEWReal-time IOC feeds from abuse. ch (free, no API key):URLhaus— last 1000 malicious URLs (5-min cache)MalwareBazaar— last 100 malware sample hashesThreatFox— IOCs from active malware campaigns (7-day window)Used to check skill source URLs and file hashes. Auto-QuarantineIf any layer flags a skill as critical/high:Skill certificate moves to_data/quarantine/Skill removed from public catalogListed publicly at/api/security?view=quarantinefor transparencyPre-import scan in auto-discovery blocks it before enteringResultRe-audited all 7,063 skills with the 8 layers. 0 in quarantine. The catalog was clean except for the one we already removed. ArchitectureRequest → Honeypot check → WAF (40 rules) → Handler ↓ Skill import → L1. 5 (6) → L1. 6 (36) → L1. 7 (8) → L1. 8 (17) → Quarantine? ↓ no Catalog (public) ↓ yes _data/quarantine/ (publicly listed)Enter fullscreen modeExit fullscreen modeStack: Vercel Hobby ($0/mo), GitHub Actions (free), Docker + gVisor for L2 sandbox, abuse. ch feeds. LessonsMetadata audits are not enough. Open the packages. Typosquatting is the #1 threat. "Download Latest Release" badges pointing to external zips. Transparency builds trust. Publish the quarantine list, the audit methodology, the incident history. Free tier is sufficient. $0/month infrastructure, 8 layers, real cryptography. LinksLive:https://marketnow. siteSecurity overview:https://marketnow. site/api/securityTrust page:https://marketnow. site/trustGitHub:https://github. com/edgarfloresguerra2011-a11y/marketnow—Edison Flores, AliceLabs LLC

Source: dev.to

More Layer 2 news