
Capturei um trojan no meu marketplace MCP. Aqui está a defesa em 8 camadas que construí.
Duas semanas atrás, um trojan do Windows infiltrou-se no meu marketplace MCP (Modelo de Protocolo de Contexto). O malware era Trojan:Win64/Lazy.PGPK!MTB, escondido dentro de um zip aninhado em um pacote de habilidades.
Este é o relatório técnico do que aconteceu, o que eu construí para preveni-lo e a arquitetura do pipeline de defesa em 8 camadas que agora está em produção.
O ataque
Vetor: Repositório do GitHub com typosquatting
Um servidor MCP legítimo prospector-mcp-email-finder existia. Um atacante criou uma cópia com typosquatting em JuanquiFortuny/prospector-mcp-email-finder (agora removido) com um README idêntico — mas com um badge "Baixar Última Versão" que linkava para um zip malicioso em raw.githubusercontent.com.
O payload:
Application.cmd → 'start unit.exe package.txt'
package.txt → 298 KB de bytecode Lua ofuscado
unit.exe → 872 KB executável PE32+ para Windows
O Windows Defender sinalizou unit.exe como Trojan:Win64/Lazy.PGPK!MTB. O padrão Application.cmd → unit.exe + package.txt é um lançador em camadas clássico — um pequeno wrapper CMD executa o binário com o payload de bytecode como entrada.
Como entrou: Meu script de importação (que busca servidores MCP no GitHub) baixou o zip e o cometeu em dist/skills/ sem escanear seu conteúdo. Meu pipeline de auditoria existente (Sentinel L1.5 + L1.6) apenas verificava os metadados da habilidade — nome, descrição, system_prompt. Ele nunca olhou dentro do pacote real.
A defesa — 8 camadas
L1.5 — Verificações de metadados (6 regras)
Camada mais barata. Executa em todos os metadados da habilidade:
- AUTH: A autenticação é necessária? (aviso se não)
- TOOL_DESCRIPTIONS: Padrões de injeção de prompt (
ignore previous instructions) - INPUT_VALIDATION: Acesso a arquivos/SQL/HTTP declarado nos metadados
- CORS_ORIGIN:
Access-Control-Allow-Origin: *(aviso) - OAUTH_SCOPES: Tokens não escopados
- RATE_LIMITING: Nenhum limite de taxa declarado
L1.6 — Semgrep + Secrets + OSV (36 regras)
18 regras equivalentes ao Semgrep implementadas como regex JS (nenhum binário Semgrep necessário):
- Injeção de prompt (6 padrões)
- Injeção de comando (4 padrões)
- SSRF (2 padrões)
- Traversal de caminho (2 padrões)
- Falsificação de nome de ferramenta (1 padrão)
- Segredos codificados (3 padrões)
18 padrões de detecção de segredos:
- Stripe (
sk_live_*,sk_test_*) - GitHub (
ghp_*,gho_*,ghs_*) - AWS (
AKIA*,aws_secret_access_key) - Chaves privadas (RSA/EC/OPENSSH)
- Mnemonicos de carteira
- Tokens JWT, Slack, Discord, Google, Twilio
API OSV para dependências vulneráveis conhecidas.
Correção de bug (da revisão por pares): referências a process.env.X estavam acionando MCP-SL-001 (chave de API codificada). Corrigido removendo padrões process.env.* antes de executar a detecção de segredos.
L1.7 — Detecção de binários & malware (8 padrões) — NOVO
Esta é a camada que construí após o incidente do trojan. Ela abre o zip do pacote (recursivamente — zips dentro de zips) e escaneia por:
const BINARY_EXTENSIONS = ['.exe', '.dll', '.scr', '.msi'];
const LAUNCHER_EXTENSIONS = ['.bat', '.cmd', '.vbs', '.ps1'];
Além de 8 padrões regex para:
- Lançadores em camadas:
start X.exe Y.txt(a assinatura exata do prospector) - Bytecode Lua ofuscado:
function(o,R,F,U,b,p,E,M,Z,W,...)(assinatura de função de alta aridade) - URLs de download externo:
raw.githubusercontent.com/.../...zip - Badges "Baixar Última Versão" com links para zips externos
- PowerShell
-encodedcommandcom long base64 eval(atob(...))ofuscação- Marcadores numéricos Lua
- Arquivos de texto excessivamente grandes >100KB que não são JSON válidos (payloads de bytecode)
Fluxo de quarentena: Qualquer descoberta crítica → pontuação 0, nível de risco crítico, status quarentenado, removido do catálogo, listado em /api/security?view=quarantine.
L1.8 — Assinaturas de famílias de malware (17 famílias) — NOVO
Regras equivalentes ao YARA para famílias de malware específicas, cada uma com IDs de técnica MITRE ATT&CK:
| Família | MITRE | O que faz |
|---|---|---|
| Win64/Lazy.PGPK | T1027.002 | O trojan que nos atingiu |
| Emotet | T1027.011 | Trojan bancário, entrega de macro do Office |
| Cobalt Strike | T1071.001 | Beacon pós-exploração |
| Mimikatz | T1003.001 | Dumpador de credenciais LSASS |
| QakBot | T1027 | Trojan bancário |
| TrickBot | T1027 | Trojan modular, precursor de ransomware |
| Agent Tesla | T1056.001 | Keylogger |
| RedLine Stealer | T1555 | Roubo de credenciais do navegador |
| Vidar Stealer | T1555 | Roubo de navegador + carteira de criptomoedas |
| Raccoon Stealer | T1555 | Stealer MaaS |
| LummaC2 Stealer | T1555 | Stealer vendido no Telegram |
| AsyncRAT | T1071.001 | RAT de código aberto |
| njRAT | T1071.001 | RAT .NET |
| Remcos RAT | T1071.001 | RAT comercial |
| SolarMarker | T1027 | Backdoor envenenada por SEO |
| Lokibot | T1555 | Stealer de credenciais |
| Ferramentas DoS | T1499 | hping3, slowloris, goldeneye |
Qualquer correspondência → quarentena instantânea.
WAF — Firewall de Aplicação Web (40 regras) — NOVO
Inspeciona cada requisição HTTP recebida:
SQLi (7): UNION SELECT, OR 1=1, empilhado, baseado em tempo, info_schema, hex
XSS (7): script, manipuladores de eventos, javascript:, data:, vbscript:, img/svg
Traversal de caminho (5): ../, codificado, /etc/passwd, /proc/self, Windows
SSRF (6): metadados AWS/GCP/Azure, file://, gopher://, dict://
Injeção de comando (5): crase, $(), encadeado, pipe, && ||
NoSQL (3): $where, $ne, $gt
Poluição de protótipo: __proto__, constructor.prototype
SSTI: Jinja2 {{ }}, Twig {% %}, JS ${ }
Injeção de log: \n \r injeção de cabeçalho
O incidente destaca a vulnerabilidade dos marketplaces MCP e a necessidade de medidas de segurança robustas. Empresas brasileiras que utilizam MCP devem considerar a implementação de defesas semelhantes para proteger seus dados e usuários. A adoção de práticas de segurança pode mitigar riscos associados a malware e ataques cibernéticos.

