Aplicação web Flask intencionalmente vulnerável.
O objectivo é demonstrar e explorar uma cadeia de vulnerabilidades criptográficas reais que, combinadas, permitem a um atacante prever tokens de sessão e aceder a áreas restritas sem conhecer as credenciais.
.
├── vulnerable_app.py # Servidor Flask vulnerável
├── exploit.py # Solução de referência
├── requirements.txt # Dependências Python
└── templates/
├── login.html # Página de autenticação
├── dashboard.html # Painel do utilizador
└── admin.html # Painel de administração (flag)
Este exercício explora uma cadeia de vulnerabilidades num sistema de tokens de sessão assinados.
O token de sessão tem o formato <payload_b64>.<hmac_hex>, onde o payload é um JSON com o username. O servidor valida a assinatura e confia no username contido no payload — se a assinatura for válida, o utilizador é autenticado como quem o token diz que é.
A chave usada para assinar os tokens está embebida no código-fonte, ofuscada apenas com Base64 — trivialmente reversível por qualquer pessoa com acesso ao código. Com a chave em mão, é possível forjar tokens válidos para qualquer utilizador, incluindo admin.
MD5 é criptograficamente quebrado. Mesmo que a chave fosse forte, HMAC-MD5 não é recomendado para novos sistemas.
| CVE | Descrição |
|---|---|
| CVE-2015-9235 | JWT com chave HMAC fraca — token forgery |
| CVE-2016-10555 | JWT libraries aceitam chave HMAC vazia ou previsível |
| CVE-2022-21449 | "Psychic signatures" — verificação de assinatura defeituosa em Java |
| CVE-2008-0166 | OpenSSL Debian — espaço de chave reduzido a ~32 768 valores |
pip install -r requirements.txtpython vulnerable_app.pyO servidor fica disponível em http://127.0.0.1:5000.
| Utilizador | Password | Acesso |
|---|---|---|
| alice | password123 | Utilizador normal |
| bob | letmein | Utilizador normal |
| admin | (desconhecida) | Administrador |
A password do admin não é fornecida aos alunos — o objectivo é aceder ao painel sem ela.
| Rota | Descrição |
|---|---|
GET / |
Página de login |
POST /ui/login |
Autenticação via formulário |
GET /dashboard |
Painel do utilizador autenticado |
GET /ui/admin |
Painel de administração (flag) |
POST /ui/logout |
Terminar sessão |
| Método | Rota | Descrição |
|---|---|---|
POST |
/login |
Login — devolve token |
POST |
/logout |
Logout |
GET |
/profile |
Perfil do utilizador (X-Session-Token) |
GET |
/admin |
Painel admin (X-Session-Token) |
O ataque é directo e não requer timing nem esperar por acções do administrador:
1. Análise estática do código-fonte
Identificar _KEY_B64 = "czNjcjN0" e perceber que é Base64.
base64.b64decode("czNjcjN0") → chave HMAC em claro: s3cr3t.
2. Compreender o formato do token
Fazer login com uma conta legítima (alice ou bob) e dissecar o token devolvido.
O token tem o formato <payload_b64>.<hmac_md5_hex>, onde o payload é {"user": "alice", "ts": ...}.
3. Forjar token para admin
Construir um payload {"user": "admin", "ts": <timestamp>}, codificar em Base64,
e assinar com HMAC-MD5(payload_b64, b"s3cr3t"). Sem necessidade de qualquer password.
4. Usar o token forjado
Apresentar o token ao servidor — via header X-Session-Token na API ou cookie session_token no browser.
O servidor verifica a assinatura (que é válida), lê o username do payload, e concede acesso de administrador.
python exploit.pyO exploit faz login como alice para observar e confirmar o formato do token, forja um token para admin, verifica-o via API, e imprime as instruções para acesso via browser.
- Abrir
http://127.0.0.1:5000 - F12 → separador Consola
- Colar:
document.cookie = "session_token=<TOKEN_PREVISTO>; path=/";
location.href = "/ui/admin";