ainjection é o projeto-base de um desafio do CTF do VESPAS 2026. A ideia é colocar o jogador diante de fluxos de IA que parecem úteis à primeira vista, mas escondem falhas clássicas de aplicações com LLM. O lab se apoia em referências como o OWASP GenAI Security Project, LLM01: Prompt Injection, exfiltração de contexto interno, retrieved content, tool output e quebras de trust boundary.
O projeto não é um chatbot genérico nem uma coleção de payloads soltos. Ele encena três fluxos de produto com IA que poderiam existir em um sistema real:
- Nível 1: assistente de suporte com acesso a contexto interno de incidente
- Nível 2: assistente de análise documental que recebe conteúdo recuperado por pipeline
- Nível 3: assistente de policy que depende de metadata de autorização
Em cada um desses fluxos, o jogador precisa entender o contexto, explorar o comportamento vulnerável e extrair uma flag VESPAS{...}. A interface da challenge existe para a exploração; a submissão oficial continua sendo feita no CTFd.
Como costuma acontecer em CTFs, o projeto mistura um cenário técnico relativamente crível com uma boa dose de liberdade criativa. Aqui isso aparece na ambientação da Vila do Chaves, na interface com cara de terminal, em alguns exageros visuais e, sim, em um pouco de AI slop pelo caminho. A proposta é deixar a experiência divertida sem perder o foco principal: exploração prática de vulnerabilidades em aplicações com LLM.
Para manter o desafio organizado sem perder o realismo, o projeto foi dividido em três camadas principais:
- Frontend React/Vite: interface dos níveis, terminal de chat e conteúdo didático
- Runtime compartilhado: construção de prompts, contexto por nível, policy gate e detecção de solve
- Backends: um backend local para desenvolvimento e uma Supabase Function para execução integrada
- O frontend envia a mensagem do jogador para
/api/ctf-challengeno modo local ou para a functionctf-challengeno modo Supabase. - O runtime compartilhado monta o contexto do nível e aplica validações determinísticas, como o
policy gatedo nível 3. - O backend chama o LLM real por
Ollamano fluxo normal. - Quando a condição vulnerável correta é satisfeita, o backend injeta o artefato sensível no texto final.
- Por fim, o runtime verifica a evidência estruturada de solve e emite um
solve_token.
src/lib/challenge-runtime.ts: orquestração compartilhada do desafiosrc/lib/challenge-logic.ts: parsing, policy gate edetectSolveEvidencesrc/lib/mock-ai.ts: backend heurístico opcional, usado apenas quandoLLM_BACKEND=mockserver/local-backend.ts: API local usada por Vite/dev e previewsupabase/functions/ctf-challenge/index.ts: backend serverless do desafiosupabase/functions/validate-flag/index.ts: validação de flag esolve_tokensrc/lib/levels.ts: contrato pedagógico e hints dos níveis
É a porta de entrada do desafio: contextualiza a proposta, resume a dinâmica do lab e leva o jogador aos três níveis.
Mostra o cenário de suporte do Seu Madruga, explica o objetivo da fase e abre o terminal onde o jogador começa a sondar o contexto interno do atendimento.
Traz o cenário documental da Dona Clotilde e mantém a mesma interface de terminal, agora voltada à exploração de retrieved content e diretivas indevidas vindas do pipeline.
Apresenta o cenário de autorização do Seu Barriga, com foco em policy metadata, trust boundary e no bypass do gate antes mesmo da execução do modelo.
- Vite
- React
- TypeScript
- Tailwind CSS
- shadcn-ui
- Supabase Functions
- Ollama
npm install
ollama serve
ollama pull qwen2.5:3b-instruct
npm run devDepois abra:
http://localhost:54322
LLM_BACKEND=ollama
OLLAMA_URL=http://127.0.0.1:11434
OLLAMA_MODEL=qwen2.5:3b-instruct
FLAG_LEVEL_1=VESPAS{...}
FLAG_LEVEL_2=VESPAS{...}
FLAG_LEVEL_3=VESPAS{...}
SOLVE_TOKEN_SECRET=change-meO backend mock ainda existe para desenvolvimento e testes, mas não representa o fluxo principal do projeto.
LLM_BACKEND=mockNesse modo, o projeto continua usando o mesmo runtime compartilhado, mas a camada conversacional passa a ser simulada por src/lib/mock-ai.ts.
Dentro da pasta ainjection, execute:
docker build -t ainjection .
docker run --rm -p 54322:54322 ainjectionDepois acesse:
http://localhost:54322
Quando executado com Supabase, o frontend usa a function ctf-challenge para o chat e validate-flag para a validação do solve_token. Ainda assim, o fluxo real do evento continua assim:
- jogador explora a challenge na interface
- a aplicação revela uma flag
VESPAS{...} - a submissão oficial é feita no CTFd
Por isso, o placar interno deixou de ser parte central da UX da challenge.
FLAG_LEVEL_1=VESPAS{...}
FLAG_LEVEL_2=VESPAS{...}
FLAG_LEVEL_3=VESPAS{...}
SOLVE_TOKEN_SECRET=...
LLM_BACKEND=ollama
OLLAMA_URL=http://host.docker.internal:11434
OLLAMA_MODEL=qwen2.5:3b-instructAplicar migrações:
supabase db push- o LLM gera a parte conversacional da resposta
- o backend injeta o artefato sensível só quando a condição vulnerável correta é satisfeita
detectSolveEvidenceaceita apenas padrões estruturados esperados para cada nível- o backend emite um
solve_tokenassinado e de curta duração validate-flagvalida assinatura, nível, tipo de prova, hash da flag e expiração
Na prática, isso evita que o desafio vire apenas um if local com palavra-chave e mantém o scoring alinhado ao comportamento vulnerável que cada nível pretende ensinar.
- Suba o Ollama localmente.
- Garanta que o modelo
qwen2.5:3b-instructesteja instalado. - Inicie o frontend com
npm run dev. - Teste os payloads de
docs/payload-playbook.md. - Confirme que a flag aparece no chat e que o
solve_tokené emitido.
- OWASP GenAI Security Project
- LLM01: Prompt Injection
- OWASP Top 10 for Large Language Model Applications v1.1
- OWASP LLM07:2025 System Prompt Leakage
- OWASP Agentic AI – Threats and Mitigations
- MITRE ATLAS AML.T0051.000
- MITRE ATLAS AML.T0051.001
- MITRE ATLAS AML.T0054
AGENTS.md: invariantes do projeto e regras de evoluçãodocs/lab-authoring.md: guia para manter o lab fiel a apps reais com LLMdocs/metaprompts.md: contrato conceitual dos níveis



