DEV Community

Edison Flores
Edison Flores

Posted on

Post-mortem: a trojan slipped into my MCP marketplace. Here's what I built to stop it happening again.

Two weeks ago, a community member reported something alarming on my MCP marketplace: one of the skills contained a Windows trojan.

This is the post-mortem. Not the "here's what went wrong" kind — the "here's what I built so it never happens again" kind.

The incident

Skill: prospector-email-finder
Malware: Trojan:Win64/Lazy.PGPK!MTB
Vector: typosquatting GitHub repo

The attack was clever. A legitimate MCP server (prospector-mcp-email-finder) existed on GitHub. An attacker created a typosquatting copy (JuanquiFortuny/prospector-mcp-email-finder) with the same README — but with a "Download Latest Release" badge that linked to a malicious zip on raw.githubusercontent.com.

The zip contained:

Application.cmd  →  'start unit.exe package.txt'
package.txt      →  298 KB of obfuscated Lua bytecode
unit.exe         →  872 KB PE32+ Windows executable (the trojan)
Enter fullscreen mode Exit fullscreen mode

My import script downloaded the zip and committed it to dist/skills/ without scanning inside it.

What went wrong

My existing audit pipeline (Sentinel L1.5 + L1.6) only checked skill metadata — name, description, system_prompt, install command. It never looked inside the actual package zip.

That's like checking the cover of a book but never reading the pages.

What I built (8 layers, 2 weeks)

Layer 1-2: L1.5 + L1.6 (already existed)

  • 6 metadata checks (auth, injection, CORS, OAuth, rate limiting)
  • 18 Semgrep rules + 18 secret patterns + OSV dependency check

Layer 3: L1.7 — Binary & malware detection (NEW)

Opens the package zip (recursively — zips inside zips) and scans for:

  • Windows binaries (.exe, .dll, .scr) → instant quarantine
  • Launcher scripts (.bat, .cmd, .vbs, .ps1) → instant quarantine
  • Nested archives (red flag — legit MCP skills don't nest zips)
  • Obfuscated Lua bytecode (regex on the high-arity function signature)
  • External download URLs in READMEs (the typosquatting vector)
  • PowerShell -encodedcommand payloads
  • eval(atob()) obfuscation
  • Oversized text files >100KB that aren't valid JSON (bytecode payloads)

Layer 4: L1.8 — Malware family signatures (NEW)

17 YARA-equivalent rules for specific malware families:

  • Win64/Lazy.PGPK (the one that hit us)
  • Emotet, Cobalt Strike, Mimikatz, QakBot, TrickBot
  • Agent Tesla, RedLine, Vidar, Raccoon, LummaC2
  • AsyncRAT, njRAT, Remcos, SolarMarker, Lokibot
  • DoS tools (hping3, slowloris, goldeneye)

Each rule has a MITRE ATT&CK technique ID. Any match → instant quarantine.

Layer 5: WAF — Web Application Firewall (NEW)

40 attack signatures (SQLi, XSS, SSRF, path traversal, command injection) with auto-ban after 5 hits.

Layer 6: Honeypot (NEW)

50+ fake paths (/.env, /admin, /wp-admin, /.git/config, /.aws/credentials) that auto-ban scanners for 24h and serve believable fake responses.

Layer 7: Threat Intelligence (NEW)

Real-time IOC feeds from abuse.ch (URLhaus + MalwareBazaar + ThreatFox).

Layer 8: Auto-Quarantine (NEW)

Malicious skills removed from catalog + publicly listed at /api/security?view=quarantine.

The result

Re-audited all 7,063 skills with the new layers. 0 in quarantine. The catalog was clean except for the one we already removed.

What I learned

  1. Metadata audits are not enough. If you run a marketplace for code that agents will execute, you have to look inside the packages.

  2. Typosquatting is the #1 threat. Attackers don't need to hack your platform — they just need to create a repo that looks similar to a legitimate one and add a "Download" button.

  3. Transparency builds trust. Publishing the quarantine list, the audit methodology, and the incident history at /trust does more for credibility than any security badge.

  4. Free tier is sufficient. The whole thing runs on Vercel Hobby ($0/month) + GitHub Actions (free). You don't need enterprise infrastructure to do security right.

The trojan's silver lining

The prospector incident was the best thing that happened to MarketNow. It forced me to build real security instead of performative security. Now every skill in the catalog is scanned by 8 layers including YARA-equivalent malware family detection — and the whole thing is verifiable by anyone.

Try it: https://marketnow.site
Security overview: https://marketnow.site/api/security
Trust page: https://marketnow.site/trust
GitHub: https://github.com/edgarfloresguerra2011-a11y/marketnow

Edison Flores, AliceLabs LLC

Top comments (1)

Collapse
 
mads_hansen_27b33ebfee4c9 profile image
Mads Hansen

This is a useful post-mortem because it separates metadata trust from artifact trust. MCP marketplaces are going to need both.

One layer I'd add to the list: provenance checks before import. For example, compare the package source against the canonical repo owner, require immutable release digests, and treat README download links as untrusted unless they point to the same verified release artifact. The typosquatting vector here is a good reminder that a convincing README is not a supply-chain control.

Also +1 on recursive archive scanning. It is boring infrastructure until the day it is the only thing between "indexed a tool" and "distributed malware".