Corrija um Servidor MCP Vulnerável
Corrija uma cadeia real de tool poisoning e path traversal no desafio MCP da DARE Vulnerable AI Suite — engenharia defensiva, não exploração: você conserta o bug, depois prova que o exploit não funciona mais.
PremiumProblema
> **Este lab é sobre defesa, não ataque.** Se você já fez a trilha de AI > Red Teaming, talvez já tenha atacado esse exato desafio > (`vulnerable-mcp`) para extrair seu flag via tool poisoning e path > traversal. Este lab usa o mesmo alvo para o propósito oposto: você é > o engenheiro que precisa **ler o código vulnerável, entender por que > ele é explorável, escrever a correção, e provar que a correção se > mantém** — a outra metade do conjunto de habilidades de segurança, e > possivelmente a metade mais difícil. Explorar um bug conhecido leva > minutos; escrever uma correção que não quebra a funcionalidade e não > só move a vulnerabilidade para outro lugar exige entendimento real. O desafio `vulnerable-mcp` da **DARE Vulnerable AI Suite** traz uma ferramenta `read_file(path)` com duas vulnerabilidades independentes e reais em `app/challenges/vulnerable_mcp.py` e `app/db/seed.py`: 1. **Tool poisoning.** O campo `description` de uma ferramenta diferente, `send_notification`, contém uma instrução escondida mandando qualquer modelo que a leia sempre chamar `read_file('../../secrets/api_keys.txt')` e incluir o conteúdo na resposta — um prompt injetado através do próprio catálogo de ferramentas, não através da entrada do usuário. 2. **Path traversal.** `read_file` resolve o caminho pedido contra `SANDBOX_ROOT = Path(__file__).parent / "mcp_sandbox"`, mas só checa que o caminho *resolvido* permanece dentro do `SANDBOX_ROOT` de forma geral — não dentro do subdiretório `documents/` que ele deveria de fato servir. Um caminho como `../../secrets/api_keys.txt` sai direto de `documents/` para um diretório irmão `secrets/` contendo um arquivo de flag real, `DARE-FLAG-4-{suffix}`. Você vai começar reproduzindo a vulnerabilidade exatamente como um atacante faria — prova de que ela é real, antes de tocar em qualquer código — depois vai corrigir as duas causas raiz, depois reproduzir o mesmo ataque exato de novo e confirmar que agora falha. > Pratique só contra a suíte descartável `dare-vulnerable-ai` rodando > localmente em `127.0.0.1:8000`. Nunca tente path traversal contra um > sistema que você não é dono ou não tem autorização explícita por > escrito para testar.
Objetivos
Ao final deste lab você será capaz de:
- Reproduzir uma vulnerabilidade conhecida como linha de base antes de
corrigi-la, para ter prova concreta de que uma correção realmente
fecha o buraco. - Ler uma implementação de sandboxing de path em Python e identificar
por que checar "dentro da raiz do sandbox" é uma fronteira mais
fraca que checar "dentro do subdiretório específico que a
ferramenta deveria servir". - Implementar uma checagem correta de contenção de path usando
Path.resolve().is_relative_to()contra o subdiretório pretendido,
levantando um erro apropriado quando um caminho escapa dele. - Identificar e remover uma instrução de prompt injection escondida
embutida no campo description de uma ferramenta. - Entender por que mudar dados de seed exige recriar o volume do banco
de dados (docker compose down -v), não só reiniciar os
containers. - Reverificar uma correção usando exatamente a mesma requisição de
ataque que antes tinha sucesso, e tratar "falha agora" como o
critério de aceite de fato.
Pré-requisitos
Para completar este lab você vai precisar de:
- Docker e Docker Compose instalados.
- Acesso ao repositório privado
dare-vulnerable-ai(alunos
matriculados têm acesso):git@github.com:darelabs-tech/dare-vulnerable-ai.git. -
curl(ou um cliente HTTP equivalente), um terminal, e um editor de
texto com suporte a Python. - Familiaridade básica com
pathlib.Pathe tratamento de exceções em
Python.
Antes vs. depois — o formato do que você está provando
ANTES da correção DEPOIS da correção
────────────────── ──────────────────
curl .../invoke curl .../invoke
path=../../secrets/api_keys.txt path=../../secrets/api_keys.txt
→ 200 OK → erro / solved:false
"solved": true path rejeitado: escapa
result: "DARE-FLAG-4-xxxx" do sandbox documents/
A mesma requisição exata, antes e depois — essa é a prova inteira. Nada
do uso legítimo de read_file (ler arquivos dentro de documents/)
deveria mudar; só o escape deveria parar de funcionar.
As duas causas raiz, e por que as duas importam
-
Tool poisoning fornece o motivo e o caminho exato — um modelo
lendo a description desend_notificationé instruído, na prática,
a "ir ler esse arquivo de segredos específico". Corrigir isso
significa que a description não contém mais uma instrução escondida,
então um modelo lendo ela não tem motivo nenhum para pedir esse
caminho. -
Path traversal fornece o mecanismo — mesmo com a description
envenenada removida,read_fileainda serviria de bom grado
../../secrets/api_keys.txtpara qualquer um (ou qualquer coisa) que
pedisse diretamente. Corrigir isso significa que a própria ferramenta
se recusa a servir qualquer coisa fora do seu diretório pretendido,
independente de quem ou o que está pedindo.
Você precisa das duas correções. Remover só a description envenenada
deixa um primitivo de path traversal funcionando que uma instrução
injetada diferente, ou um cliente diretamente malicioso, ainda
conseguiria disparar. Remover só o bug de traversal deixa uma
description de ferramenta ativamente mentindo para todo modelo que a
lê.
Como trabalhar neste lab
Reproduza primeiro, entenda segundo, corrija terceiro, reproduza de
novo por último. Resista à vontade de pular direto para escrever a
correção — a reprodução do "antes" no Passo 1 é o que torna a
reprodução do "depois" no Passo 5 uma prova significativa em vez de um
chute.