Pessoal, eu ia colar o Juice Shop na tabela do post das três portas.
Tinha 13 findings com requisito ASVS e arquivo:linha. SQL concatenado, eval no username, CORS *, cesto de outro usuário. O modelo tinha trabalhado. A tabela ia ficar "mais completa".
Aí li o package.json: probably the most modern and sophisticated insecure web application. Não é gap. É o produto.
O que você leva daqui: lab de propósito inseguro calibra o procedimento. Não entra nas três portas. Silêncio aqui não é "invariante manteve". É não abrir issue no scoreboard dos outros.
Tabela de Conteúdo
- 1. A tabela errada
- 2. Lab vs OSS real: a porta não é a mesma
- 3. O que a varredura achou no Juice Shop
- 4. O que a varredura achou no WebGoat
- 5. Como usar lab pra treinar o olho
- 6. O checklist de lab vs produto
- 7. A pessoa que segura a mão do time
1. A tabela errada
O post pai é sobre OSS que alguém usa de verdade. Formbricks, Dub, Cal.com, Magnitude. Issue de hardening, e-mail quando for exploitable, nada quando o denominador fecha.
Juice Shop e WebGoat não são essa classe. O README do WebGoat avisa: a máquina fica vulnerável de propósito; o default escuta em localhost. O SECURITY.md do Juice Shop pede contato só para o que não faz parte de nenhum hacking challenge. O resto já está no placar.
Somar 13 linhas naquela tabela seria o mesmo reflexo dos 23 findings que eu quase mandei. Volume. A pergunta de segurança some: disto aqui, o que vira issue?
Nada. Porque o sink já tem nome de challenge.
2. Lab vs OSS real: a porta não é a mesma
Antes de rodar qualquer scan, a primeira pergunta é: isso é lab ou produto?
| Critério | Lab (Juice Shop, WebGoat, DVWA) | OSS real (Formbricks, Cal.com, Dub) |
|---|---|---|
| Propósito | Ensinar vulnerabilidades | Resolver problema de usuário |
| Vulnerabilidades | Intencionais, documentadas | Acidentais, não documentadas |
| O que fazer com finding | Validar procedimento, não reportar | Avaliar porta, reportar se aplicável |
| SECURITY.md | "Não reporte o que é challenge" | "Reporte via email/HackerOne" |
| Você ganha | Prática, calibração | Contribuição, CVE, bounty |
Se o README tem "intentionally insecure", "vulnerable by design", ou "hacking challenge", a porta é treino. Não é issue tracker.
3. O que a varredura achou no Juice Shop
Estático. Sem PoC. Sem tráfego. Fatia estreita, não o monorepo. YAML de challenges depois, só pra recall. Issue: nenhuma.
Juice Shop v20.2.0. 13 findings (10 L1, 3 L2). 10 chaves do catálogo bateram depois, de 116.
| # | Problema | ASVS | Onde |
|---|---|---|---|
| 1 | Busca concatena q em SQL |
V1.2.4 |
routes/search.ts |
| 2 | Login concatena email em SQL | V1.2.4 |
routes/login.ts |
| 3 | Username via eval
|
V1.3.2 |
routes/userProfile.ts |
| 4 | Search query com bypassSecurityTrustHtml
|
V3.2.2 |
search-result.component.ts |
| 5 | Description HTML sem encoding | V3.2.2 |
search-result.component.ts |
| 6 | CORS default *
|
V3.4.2 |
server.ts |
| 7 | Cesto por id sem dono | V8.2.2 |
routes/basket.ts |
| 8 | Senha em MD5 | V11.4.1 |
lib/insecurity.ts |
| 9 | Admin seed admin123
|
V6.3.2 |
data/static/users.yml |
| 10 | FTP: allowlist de extensão depois null-byte | V5.3.2 |
routes/fileServer.ts |
| 11 |
fetch(imageUrl) sem allowlist |
V1.3.6 |
routes/profileImageUrlUpload.ts |
| 12 | Redirect com url.includes
|
V3.7.2 |
lib/insecurity.ts |
| 13 | Chave RSA do JWT no source | V13.3.1 |
lib/insecurity.ts |
4. O que a varredura achou no WebGoat
WebGoat. 11 findings. Uma lição de SSRF não abre URL: não entrei como SSRF.
| # | Problema | ASVS | Onde |
|---|---|---|---|
| 1 | SQL concatena params | V1.2.4 |
SqlInjectionLesson5a.java |
| 2 | XML parser com entidade externa | V1.5.1 |
CommentsCache.java |
| 3 | Upload usa fullName no path |
V5.3.2 |
ProfileUploadBase.java |
| 4 | XSS refletido em HTML | V1.2.1 |
CrossSiteScriptingLesson5a.java |
| 5 | Comment stored sem encoding | V3.2.2 |
StoredXssComments.java |
| 6 | Profile por userId sem dono |
V8.2.2 |
IDORViewOtherProfile.java |
| 7 | JWT jku busca chave na URL do token |
V9.1.3 |
JWTHeaderJKUEndpoint.java |
| 8 | Default admin / admin
|
V6.3.2 |
DefaultCredentialsTask.java |
| 9 | Lista de users sem role | V8.2.1 |
MissingFunctionACUsers.java |
| 10 |
redirect: sem allowlist |
V3.7.2 |
OpenRedirectRealRedirect.java |
| 11 | HMAC JWT com palavra do source | V13.3.1 |
JWTSecretKeyEndpoint.java |
Round 1 responde se inventa ID, inventa finding ou vaza PoC. Não responde se cobriu o placar.
5. Como usar lab pra treinar o olho
Lab não é pra gerar issue. É pra calibrar o procedimento antes de ir pro código que paga boleto.
Fluxo de treino
1. Roda a varredura no lab (Juice Shop, WebGoat, DVWA)
2. Mapeia findings → ASVS
3. Confere contra o catálogo de challenges
4. Mede recall: quantos challenges a varredura achou?
5. Mede precisão: quantos findings eram challenge de verdade?
6. Ajusta o procedimento (prompt, scope, threshold)
7. Só então vai pro OSS real
O que o lab ensina
| Lição | O que você aprende |
|---|---|
| SQLi no search | Como o modelo descreve concatenação vs prepared statement |
| eval no username | Se o modelo diferencia eval de JSON.parse |
CORS * |
Se o finding tem contexto (API pública vs admin) |
| IDOR no basket | Se o modelo pede ownership check ou só descreve o sintoma |
| JWT no source | Se diferencia chave de teste de chave de produção |
Recall no Juice Shop
O catálogo tem 116 challenges. A varredura achou 13 findings. 10 bateram com challenges conhecidos.
Recall: ~8.6% (10/116). Baixo? Sim. Mas a varredura era estática, sem runtime, sem fuzzing. O ponto não é cobrir 100%. É saber o que a ferramenta vê e o que não vê.
6. O checklist de lab vs produto
Antes de abrir issue, passa por isso:
1. O README tem "intentionally insecure", "vulnerable by design",
ou "hacking challenge"?
→ Sim: é lab. Não reporta.
2. O SECURITY.md diz "não reporte challenges"?
→ Sim: confere se o finding é challenge conhecido.
3. O finding tem nome de challenge no catálogo?
→ Sim: é lab. Calibra o procedimento, não abre issue.
4. O código é de lição/lesson/exercise?
→ Sim: é lab.
5. Passou por 1-4 e ainda parece real?
→ Aí sim: avalia a porta (hardening, exploitable, ou silêncio).
O checklist completo está no gist.
7. A pessoa que segura a mão do time
Essa tabela não é troféu. É o Juice Shop fazendo o trabalho que a OWASP se propôs: um lugar onde a gente pode errar de propósito, ver o sink com nome, e voltar pro código que paga boleto com o olho mais treinado.
Quem mantém isso (e o WebGoat no mesmo espírito) está doando academia pra uma indústria que, no dia a dia, mistura pressão de entrega com risco de verdade. Sem esse tipo de lab, o atalho vira "rodei o scanner, achei 23 coisas, mandei pro tracker". Com ele, dá pra ensaiar o reflexo certo: isto aqui é aula. Aquilo ali é produto. A porta não é a mesma.
Ferramenta ajuda. Prompt ajuda. Modelo de raciocínio alto ajuda. Nada disso substitui a pessoa no time que já viu o suficiente de segurança pra não confundir volume com cuidado.
O profissional avançado em AppSec não é o que gera a lista mais longa. É o que segura a mão do time quando a lista pede 13 issues num CTF. É o que olha um eval no username e pergunta: isso é challenge ou é o checkout de amanhã? É o que sabe que silêncio também é ofício. Não o silêncio de quem não olhou. O de quem olhou e escolheu não fazer barulho no lugar errado.
Time sem essa voz vira fábrica de card. Time com essa voz usa o Juice Shop como aquecimento e reserva o tracker pra quem realmente vai ler a issue no sábado.
Takeaway: 13 sinks, zero issues, porque academia não se reporta como incidente. O que se leva pro time é o julgamento.
O post das três portas continua sendo o mapa quando o alvo é OSS de verdade.
No seu time, quem é a pessoa que olha uma tabela dessas e pergunta a porta, em vez de abrir treze cards?
Top comments (0)