# Montamos um Caça-Níquel Viciado e Desafiamos o Hacker Summer Camp a Quebrá-lo

> Na DEF CON, na Black Hat e na BSides Las Vegas rodamos um caça-níquel que nunca paga — a não ser que você saiba ler a máquina. Quatro bugs web plantados, um ranking e até três meses de Tandera em jogo. Veja como funcionava.

A banca sempre ganha. Era essa a proposta na tela do nosso estande durante o Hacker Summer Camp — DEF CON, Black Hat e BSides Las Vegas. Escolha um apelido — sem e-mail, sem cadastro — ganhe cinco créditos e puxe a alavanca de um caça-níquel "777" em neon. Os rolos giram, param numa caveira, e o feed do crupiê imprime `paid=false`. Puxe de novo. Caveira. De novo. Caveira.

Dez prêmios por evento, quatro deles reservados para quem quebrasse o bug mais difícil, e um contador no topo caindo ao vivo. O e-mail só era pedido _depois_ que alguém ganhava de verdade.

A máquina é viciada. A gente avisou, ali no rodapé: _esta máquina é viciada de propósito. É esse o ponto._ O cliente nunca ganha. Mas a máquina está _conversando_ com um servidor, e o servidor é desleixado. Leia a máquina, quebre a máquina, e o prêmio de verdade eram até três meses de Tandera, de graça.

![A tela inicial do 777: Jackpot or Jail](/assets/hsc/hero.jpg)

Por baixo do neon era um CTF web de quatro flags, cada uma um bug que a gente vê no mundo real — em especial no canto pouco regulado dos jogos de "cassino social" online. Aqui vai o tour, agora que os códigos já foram resgatados.

## Flag 1 — o cliente é mentiroso

Cada giro envia um pedacinho de telemetria ao servidor: `client_outcome`, os rolos que o navegador acha que mostrou. O cliente honesto reporta o que sorteou. O detalhe é que o servidor _decide_ o resultado e ignora o seu relato por completo — os rolos nunca são seus para declarar.

E se você declarar mesmo assim? Abra a aba de rede, repita o giro com `client_outcome` valendo uma trinca vencedora, e o servidor responde:

> nice try — the client cannot authorize a payout. the house signs its own outcomes.

![O feed do crupiê com o giro adulterado e a captura da flag 1](/assets/hsc/flag1-client-liar.jpg)

Essa é a flag um, valendo 100 pontos de consolação e nenhum prêmio. A lição é a mais antiga da segurança web: **o cliente é controlado pelo atacante.** Um número assustador de front-ends reais de apostas decide a vitória em JavaScript e simplesmente confia que o navegador vai reportá-la de volta. O nosso não confia — mas te entrega uma flag por provar que você entende por que isso importa.

## Flag 2 — XOR não é criptografia

Quando você senta, `/api/hsc/start` devolve um `vault`: um blob base64 que a máquina chama de "criptografado". Não é. É um JSON passado por um XOR de chave curta repetida — ofuscação disfarçada de cadeado.

XOR cai com um crib de texto conhecido. Você sabe que o blob é JSON, então sabe que ele começa com `{"flag":"`. Faça o XOR do texto cifrado contra esse prefixo conhecido e a chave repetida aparece na hora; aplique no resto e o vault volta em texto claro, incluindo uma chave de assinatura por sessão e, de brinde, a receita de como usá-la. Envie a chave recuperada e você leva a flag dois: 250 pontos e um mês de Tandera.

![Enviar o token recuperado do vault leva a flag 2](/assets/hsc/flag2-vault-cracked.jpg)

O ponto não é que o XOR é fraco — é que **um segredo que você entrega ao cliente não é um segredo.** Toda chave que chega ao navegador pode ser recuperada do navegador. A gente já abriu respostas de API "criptografadas" exatamente assim em engajamentos reais, e a criptografia era exatamente fina assim.

## Flag 3 — o pagamento em dobro

Existe um botão de "resgatar bônus" que vale cinco créditos grátis, uma vez por sessão. O servidor verifica se você já resgatou e então concede os créditos. Leia de novo devagar: ele _verifica_ e depois _age_, e não há nada de atômico entre as duas coisas.

Essa brecha é um TOCTOU — time-of-check to time-of-use. Clique no botão e você recebe seus cinco créditos como um bom cidadão. Um duplo-clique preguiçoso não resolve; dispare uma rajada de verdade _ao mesmo tempo_ e várias delas leem "ainda não resgatado" antes de qualquer uma terminar de escrever "resgatado". O bônus é pago de novo e de novo. O servidor percebe que pagou várias vezes e te entrega a flag três — 500 pontos, dois meses.

![A rajada dispensa o bônus várias vezes e entrega a flag 3](/assets/hsc/flag3-race-won.jpg)

Essa é a corrida clássica por trás de todo incidente de "saquei meu saldo duas vezes": uma trava correta isoladamente e inútil sob concorrência. Um servidor de dev serializado esconde isso por completo; a produção, com workers realmente paralelos batendo numa única linha compartilhada, não.

## Flag 4 — provavelmente injusto

O grande prêmio se esconde atrás do recurso de que a máquina mais se orgulha: ela é _provably fair_ (comprovadamente justa). Quando você senta, o `/start` se compromete com uma server seed publicando o hash dela. O resultado de cada giro é `v = int(sha256(round_seed : client_seed : nonce)[:8], 16)`, e um giro é jackpot quando `v % 20000 == 7770` — cerca de um em vinte mil, então você nunca vai comprar seu caminho até lá com cinco créditos. Cada giro ainda te devolve o próprio `round_seed` para você conferir que a máquina não te trapaceou.

Essa transparência é o bug. As round seeds são uma cadeia _para frente_ — `round_seed[n+1] = sha256(round_seed[n])` — e a máquina revela cada uma conforme você joga. Revele uma única seed e você consegue avançar o hash para computar toda rodada futura: cada seed, cada resultado, até onde quiser olhar. Então você gira uma vez, pega a seed revelada e mói sha256 para frente até achar o nonce cujo resultado dá o jackpot. Aí você reivindica — `POST /api/hsc/jackpot {nonce, seed}` — nomeando a rodada exata em que a máquina vai pagar, e anexando a seed daquela rodada para provar que sabe computá-la. Chute uma seed errada e a reivindicação é rejeitada; não há o que forçar. Você cantou o jackpot antes de ele acontecer. Essa é a flag quatro — 1000 pontos e os três meses completos.

Cassinos _provably fair_ de verdade se comprometem com a seed e a revelam _depois_, na direção segura. Erre a direção e a prova de justiça vira uma bola de cristal.

![Cantar a rodada do jackpot antes de acontecer leva a flag 4](/assets/hsc/flag4-jackpot-chain.jpg)

![As quatro flags capturadas, com o prêmio pronto para resgatar](/assets/hsc/machine.jpg)

![O ranking High Rollers](/assets/hsc/leaderboard.jpg)

## Por que um cassino?

Porque o código de cassino é onde esses bugs sustentam o negócio e, com frequência demais, estão em produção. Resultados decididos no cliente, "criptografia" caseira, atualizações de saldo não-atômicas e esquemas provably-fair que vazam o futuro não são exóticos — são os achados do dia a dia de teste de aplicação web, e o dinheiro os torna caros. Envolvê-los num caça-níquel só deixou o risco legível.

E é para isso que a Tandera existe. Construímos a plataforma que times de segurança ofensiva usam para encontrar exatamente esses problemas e transformá-los em relatórios limpos, padronizados e white-label — da descoberta de ativos aos findings até a entrega. O caça-níquel era um brinquedo. A metodologia por trás de achar seus quatro bugs é o trabalho de verdade.

A todos que leram a máquina em vez de puxar a alavanca: o ranking era de vocês, e os meses também. Até o próximo camp.

---

Canonical: https://tandera.io/pt-br/blog/777-jackpot-or-jail
This page as markdown: https://tandera.io/pt-br/blog/777-jackpot-or-jail.md
Index for agents: https://tandera.io/llms.txt

Every page here is also available as markdown: append `.md` to the path (e.g. `/recon.md`, `/index.md` for this homepage, `/blog/<slug>.md`), or request the canonical path with `Accept: text/markdown`.
