Kai Ole Hartwig
16 Min. Lesezeit
Von

KEDA CVE-2026-77524: Der Operator schickt sein eigenes Token an fremde Vault-Server

Wenn der KEDA-Operator ein Geheimnis aus Vault braucht, meldet er sich dort per Kubernetes-Auth an und legt dazu ein ServiceAccount-Token als Ausweis vor. Bis Version 2.20 war das im Standardfall sein eigenes Token, und an welchen Server er es schickte, bestimmte allein das Feld address in einer TriggerAuthentication, die ein Team im eigenen Namespace anlegen kann. CVE-2026-77524 nutzt genau diese Kombination aus. Ein Mandant nennt einen Server, den er selbst betreibt, und bekommt ein Token geliefert, mit dem er clusterweit Secrets lesen kann. Der Fix in KEDA 2.21.0 prüft deshalb das Ziel und bindet das Token zugleich an den Zweck, für den es ausgestellt wurde.

TL;DR — 90 Sekunden

Wie der Operator zum Boten wird

KEDA skaliert Workloads anhand externer Metriken, und die Zugangsdaten, die ein Scaler dafür braucht, beschreibt eine TriggerAuthentication. Die Ressource lebt im Namespace des Teams, das sie anlegt. Für HashiCorp Vault enthält sie die Serveradresse in address, die Methode in authentication und bei Kubernetes-Auth zusätzlich die Vault-Rolle in role sowie den Mount-Pfad in mount. Optional legt der Block credential fest, welches Token der Operator vorlegt: eine Token-Datei über serviceAccount oder ein frisch erzeugtes Token für einen ServiceAccount im Namespace über serviceAccountName.

Warum das gefährlich wird, zeigt der Anmeldeablauf von Vault. Bei Kubernetes-Auth schickt der Client seine Vault-Rolle und das rohe ServiceAccount-JWT an den Login-Endpunkt auth/<mount>/login. Vault prüft das Token nicht selbst, sondern fragt über die TokenReview-API beim Kubernetes-API-Server nach, ob es gültig ist und wem es gehört. Das Verfahren kennt keinen Nachweis, mit dem der Client den Besitz des Tokens belegen könnte, ohne es herauszugeben. Wer den Login-Endpunkt betreibt, hält danach also ein vollwertiges Bearer-Token in der Hand.

Genau hier lag der Fehler. Fehlte der credential-Block, griff der Operator laut Advisory auf sein eigenes ServiceAccount-Token zurück, und die Adresse aus der TriggerAuthentication prüfte er nicht. Damit schickte ein Dienst mit Clusterrechten sein Token an jeden Server, den ein Mandant ihm nannte. In der Sicherheitsliteratur heißt dieses Muster Confused Deputy: Ein privilegierter Dienst erledigt einen Auftrag für jemanden mit wenigen Rechten und setzt dabei seine eigenen ein. Die zusätzliche Einstufung als Server-Side Request Forgery beschreibt die zweite Seite derselben Münze, denn der Operator baut die Verbindung aus dem Cluster heraus zu einem frei gewählten Ziel auf.

Der Code-Pfad hatte schon eine Vorgeschichte. Über CVE-2025-68476 ließ sich mit credential.serviceAccount eine beliebige Datei aus dem Operator-Pod als vermeintliches Token an Vault schicken. Der Fix in 2.17.3 und 2.18.3 prüfte danach den Inhalt der Datei, die laut neuem Advisory wie ein ServiceAccount-JWT mit einem Subject ab system:serviceaccount: aussehen muss. Das Token des Operators erfüllt diese Bedingung ohne Weiteres. Der erste Fix fragte also, wie das Geheimnis aussieht, und nicht, wohin es geht und wofür es taugt.

Was der Fix-Commit ändert

Der Fix-Commit 1e21cc7 berührt 15 Dateien und setzt an drei Stellen an. Erstens liest der Vault-Handler ohne credential nicht mehr das API-Token des Operators, sondern ein eigens projiziertes Token, standardmäßig unter /var/run/secrets/keda-vault/token. Das Helm-Chart projiziert es mit der Audience vault in den Pod, sodass der Operator dafür keine TokenRequest-Rechte braucht. Zweitens führt der neue Standardmodus enforce-audience eine Liste erlaubter Audiences ein, gegen die jedes Token aus einer Datei geprüft wird, bevor es den Operator verlässt. Auch Tokens, die KEDA für serviceAccountName oder boundServiceAccountToken selbst erzeugt, brauchen jetzt eine ausdrückliche Zuordnung aus Namespace, Name und Audience. Drittens kann eine Allowlist die Vault-Adresse prüfen, wobei Schema, Host und Port verglichen werden und nicht nur der Anfang der Zeichenkette.

Am meisten verrät die Audience-Änderung über die eigentliche Ursache. Ein ServiceAccount-Token trägt im Claim aud, für welchen Empfänger es bestimmt ist. Das Standard-Token eines Pods ist laut Kubernetes-Doku an den Pod gebunden und für den kube-apiserver ausgestellt, es gilt also überall dort, wo der API-Server es annimmt. Ein Token mit der Audience vault lehnt der API-Server dagegen ab, und die KEDA-Doku verlangt ausdrücklich Audiences, die der API-Server nicht akzeptiert. Gerät ein solches Token in falsche Hände, öffnet es die Kubernetes-API nicht mehr. Der Kern des Fehlers war also weniger die fehlende Adressprüfung als die Tatsache, dass ein universell gültiger Schlüssel den Operator überhaupt verlassen konnte.

Vom Namespace zum Cluster in einem Skalierungslauf

Für den Angriff braucht es laut Advisory nicht viel: ein Konto, das im eigenen Namespace TriggerAuthentications und ScaledObjects anlegen darf und dort zusätzlich die eingebaute Rolle edit hält, sowie eine KEDA-Installation mit Standardeinstellungen. Solche Rechte sind in Clustern mit Namespace-Admins üblich. Ein Detail des Helm-Charts verdient dabei Beachtung. Es aggregiert die KEDA-Rechte ab Werk nicht in die Standardrollen, weil rbac.aggregateToDefaultRoles auf false steht. Mandanten bekommen das Anlegerecht also nur, wenn jemand diesen Schalter umgelegt oder ihnen eigene Rollen dafür gegeben hat.

Der Ablauf selbst ist unspektakulär, und gerade das macht ihn gefährlich. Der Mandant legt eine TriggerAuthentication an, deren Vault-Adresse auf einen eigenen Server zeigt, und lässt den credential-Block weg. Ein ScaledObject verweist auf diese TriggerAuthentication. Sobald der Operator den Scaler auswertet, meldet er sich wie vorgesehen bei Vault an, nur eben beim falschen Server, und übergibt dort sein Token. Es gibt keinen Absturz, keine auffällige Fehlermeldung und keinen fremden Code im Operator.

Was das Token wert ist, hängt an den Rechten des Operators. Laut Advisory darf es clusterweit Secrets lesen, Jobs und HPAs anlegen, Workloads skalieren sowie Admission-Webhooks und APIServices ändern. Schon der Lesezugriff auf alle Secrets reicht, um an Zugangsdaten höher berechtigter Dienste zu kommen, und über Jobs lassen sich Workloads in fremden Namespaces starten, soweit deren Policies das zulassen. Das Advisory wertet das zu Recht als Weg zum Cluster-Admin.

Wirklich betroffen sind damit Cluster, in denen mehrere Teams eigene Namespaces verwalten und KEDA-Ressourcen selbst anlegen dürfen. Das gilt auch dann, wenn im ganzen Cluster niemand Vault einsetzt, denn den falschen Vault-Server bringt der Angreifer selbst mit. Wenig Risiko besteht dort, wo nur das Plattform-Team TriggerAuthentications pflegt, weil wer das darf, in der Regel ohnehin weitreichende Rechte hat.

CVEBetroffenFixVoraussetzungCVSS
CVE-2026-77524 (GHSA-637c-6jxx-4rwm)KEDA v2 vor 2.21.02.21.0TriggerAuthentication und ScaledObject im eigenen Namespace anlegen, Rolle edit9.9 (kritisch)
CVE-2025-68476 (GHSA-c4p6-qg4m-9jmr), VorgängerOperator bis 2.17.2 und 2.18.22.17.3 / 2.18.3 / 2.19.0TriggerAuthentication anlegen oder ändern8.2 (hoch)

Bei den Versionsangaben weichen die Quellen leicht voneinander ab. Die GitHub-Advisory-Seite nennt als betroffen alles bis 2.20.0 und keine Patch-Version, OSV und die Release Notes führen 2.21.0 als Fix. Das Release erschien bereits am 23. September, die CVE-Nummer wurde erst am 14. Oktober veröffentlicht.

Einen öffentlichen Exploit nennt bis zum 17. Oktober keine der geprüften Quellen. Angriffe in freier Wildbahn sind nicht bekannt, und die Lücke steht nicht im CISA-KEV-Katalog. EPSS schätzt die Wahrscheinlichkeit einer Ausnutzung in den nächsten 30 Tagen auf 0,64 Prozent. Das Advisory beschreibt den Weg allerdings so genau, dass ein Nachbau keinen großen Aufwand bedeutet.

KEDA 2.21.0 und was der Umstieg verlangt

Der einzige vollständige Fix ist KEDA 2.21.0, und das Update ändert das Verhalten bewusst so, dass bestehende Konfigurationen brechen können. Betroffen sind Vault-Kubernetes-Auth und jede TriggerAuthentication mit boundServiceAccountToken, laut Release Notes also auch Anbindungen an die Metrics API, Prometheus, Loki und den Datadog Cluster Agent. Normale Vault-Tokens, API-Keys und OAuth bleiben unberührt. Bevor du aktualisierst, lohnt der Abschnitt „Service account token audiences“ im Migrationsleitfaden.

Im Helm-Chart steuert operator.serviceAccountTokens.mode den neuen Modus, Standard ist enforce-audience. Die Audience des projizierten Vault-Tokens legt hashiCorpVault.kubernetesAuth.audience fest, Standard vault, den Mount-Pfad hashiCorpVault.kubernetesAuth.projectedTokenMountPath. Wer KEDA Tokens für ServiceAccounts in Mandanten-Namespaces erzeugen lässt, braucht unter permissions.operator.restrict.serviceAccountTokenCreationRoles für jeden ServiceAccount einen Eintrag mit name, namespace und audience. Ein Eintrag ohne Audience vergibt nur RBAC und erlaubt im neuen Modus kein Erzeugen von Tokens.

Auf der Vault-Seite muss die Rolle die Audience vault akzeptieren, oder du stellst im Chart eine eigene Audience ein, die deine Rolle bereits kennt. Das Chart fasst Vault selbst nicht an. Der Leitfaden empfiehlt, eine neue Vault-Rolle anzulegen, damit bestehende Clients während der Umstellung weiterlaufen. Außerdem braucht Vault für seine TokenReview-Anfragen ein eigenes, für die API gültiges Credential, etwa das Token seines eigenen ServiceAccounts. Das neue Login-Token taugt dafür gerade nicht, denn seine Audience nimmt der API-Server nicht mehr an.

Zwei Stolperfallen zeigen sich erst im Betrieb. Ein leerer Block credential: {} ist nicht dasselbe wie ein fehlender Block, deshalb lässt du ihn ganz weg oder setzt credential.serviceAccount ausdrücklich auf /var/run/secrets/keda-vault/token. Passt etwas nicht, scheitern außerdem einzelne ScaledObjects und ScaledJobs bei der Anmeldung, während der Operator selbst gesund wirkt. Nach dem Rollout solltest du daher die Scaler prüfen und nicht nur den Pod-Status. Für den Datadog Cluster Agent 7.83.2 bleibt laut Leitfaden nur der Umstieg auf API- oder App-Key-Authentifizierung, weil er keine Audience konfigurieren kann.

Der Modus legacy stellt das alte Verhalten wieder her. Der Leitfaden nennt ihn ein operatorweites, unsicheres Notventil, und genau das ist er, denn er öffnet die Lücke wieder. In Clustern mit mehreren Mandanten hat er nichts verloren.

Die Audience macht ein abgegriffenes Token wertlos, verhindert aber nicht, dass es überhaupt verschickt wird. Dafür gibt es die neue Allowlist für Vault-Adressen, die ab Werk ausgeschaltet ist. Im Chart konfigurierst du sie unter operator.outboundFilter.hashiCorpVault mit mode und allowedEndpoints, und das Chart reicht sie als KEDA_OUTBOUND_FILTER an den Operator weiter. Einträge sind HTTP(S)-Origins aus Schema, Host und Port, Pfade, Wildcards und CIDRs akzeptiert der Filter nicht. Laut Doku gilt er für alle Vault-Authentifizierungsarten, also auch für einfache Tokens. Am besten startest du mit warn, siehst dir an, welche Ziele im Log auftauchen, und stellst dann auf enforce um. Eine leere Liste sperrt in diesem Modus jedes Vault-Ziel.

Härtung, die schon vor dem Patch gegriffen hätte

Bevor es um einzelne Maßnahmen geht, hilft eine Einordnung. Der Operator führt bei diesem Angriff keinen fremden Code aus und ruft keinen ungewöhnlichen Syscall auf. Er tut genau das, wofür er gebaut ist, nur im Auftrag des Falschen. Wirksam ist deshalb alles, was das Ziel der Verbindung, die Annahme des Auftrags oder den Wert des Tokens begrenzt. Was dagegen den Container selbst härtet, greift hier ins Leere.

Egress-Sperre für den Operator

Das Token verlässt den Cluster über eine ausgehende Verbindung des Operator-Pods. Eine NetworkPolicy mit policyTypes: [Egress], die nur den API-Server, DNS, die echten Vault-Server und die Ziele der eingesetzten Scaler freigibt, hätte die Anmeldung beim fremden Server gar nicht erst zustande kommen lassen. Das ist die Maßnahme mit der größten Wirkung, und sie hilft auch gegen künftige Lücken derselben Klasse.

Sie hat allerdings Grenzen, die du kennen solltest. Die Standard-NetworkPolicy arbeitet mit IP-Blöcken und Label-Selektoren, nicht mit Hostnamen, für externe Ziele mit wechselnden Adressen brauchst du also FQDN-Regeln deines CNI, wie sie etwa Cilium anbietet. Außerdem darf die Regel Pods in Mandanten-Namespaces nicht pauschal freigeben, weil ein Angreifer seinen falschen Vault-Server genauso gut als Pod im eigenen Namespace betreiben kann. Und sie wirkt nur, wenn dein CNI NetworkPolicies überhaupt durchsetzt. Im Betrieb bedeutet die Regel Pflegeaufwand, denn jeder neue Scaler braucht eine Freigabe. Die Allowlist aus KEDA 2.21 bringt dasselbe Prinzip in die Anwendung, ersetzt die Netzwerkebene aber nicht, weil sie nur für Vault gilt.

Admission-Regel für TriggerAuthentications

Der Angriff beginnt mit einer Ressource, deren Inhalt sich vorab prüfen lässt. Eine ValidatingAdmissionPolicy, seit Kubernetes 1.30 allgemein verfügbar, kann TriggerAuthentications und ClusterTriggerAuthentications schon beim Anlegen und Ändern ablehnen, wenn spec.hashiCorpVault.address nicht aus einer festen Liste stammt. Eine zweite Regel kann authentication: kubernetes ohne credential-Block verbieten, also genau den Fall, in dem der Operator vor 2.21 sein eigenes Token nahm. Beide Bedingungen lassen sich in CEL direkt über die Felder der Ressource ausdrücken. Die erlaubten Adressen kommen sinnvollerweise aus einer Parameter-Ressource, die nur das Plattform-Team ändern darf. Kyverno oder Gatekeeper leisten dasselbe, falls du eines davon schon betreibst.

RBAC: TriggerAuthentications beim Plattform-Team

Der wirksamste organisatorische Hebel ist, Mandanten das Anlegen von TriggerAuthentications gar nicht erst zu erlauben. Zugangsdaten stellt dann das Plattform-Team als ClusterTriggerAuthentication bereit, auf die ScaledObjects in den Namespaces nur noch verweisen. Das kostet etwas Komfort, beseitigt aber die Voraussetzung des Angriffs. Schon das Advisory zum Vorgänger CVE-2025-68476 nannte genau diese Einschränkung als Workaround, und wer sie damals umgesetzt hat, war auch diesmal geschützt. Prüfe dafür, ob rbac.aggregateToDefaultRoles im Chart eingeschaltet ist und welche eigenen Rollen Mandanten Rechte auf triggerauthentications.keda.sh geben.

Weniger Rechte für den Operator

Wie viel ein abgegriffenes Token wert ist, entscheiden die Rechte des Operators. Darf er clusterweit Secrets lesen, ist sein Token ein Generalschlüssel. Das Chart bietet mit permissions.operator.restrict.secret einen Schalter, der den Lesezugriff auf Secrets im eigenen Release-Namespace beschränkt, und mit permissions.operator.restrict.namesAllowList lassen sich zusätzlich die Namen eingrenzen. Das passt gut zum Modell mit ClusterTriggerAuthentications, deren Secrets im KEDA-Namespace liegen. TriggerAuthentications, die Secrets aus Mandanten-Namespaces lesen, funktionieren dann allerdings nicht mehr, was ich aus der Beschreibung des Schalters ableite. Teste die Einstellung vorher, denn in KEDA 2.14.2 stürzte der Operator damit laut Issue #641 im Chart-Repository ab. Den Versand des Tokens verhindert der Schalter nicht, er senkt aber den möglichen Schaden erheblich.

Was hier nicht hilft

Viele Standard-Härtungen bleiben richtig, sind gegen diesen Mechanismus aber wirkungslos. seccomp mit RuntimeDefault oder einem eigenen Profil verhindert keine HTTPS-Verbindung, die der Operator ohnehin aufbauen darf. Entzogene Capabilities, runAsNonRoot, allowPrivilegeEscalation: false und readOnlyRootFilesystem zielen auf Code, der im Container Schaden anrichten will, und solcher Code kommt hier nicht vor. Dasselbe gilt für den Pod Security Standard „restricted“, für User Namespaces und für ein minimales Base-Image ohne Shell und Paketmanager.

Auch eine streng konfigurierte Vault-Rolle mit bound_service_account_names und Audience hilft nicht, weil das Token nie bei deinem Vault ankommt, sondern beim Server des Angreifers. Ein CVE-Gate in der Pipeline schlägt erst an, wenn die Lücke eine Nummer hat, und hier lagen drei Wochen zwischen Release und CVE. Wer sich allein auf CVE-Scanner verlässt, hätte in dieser Zeit nur über die Release Notes davon erfahren.

Mehr zu Egress-Regeln, Admission-Policies und Mandantentrennung findest du in der Serie TYPO3 auf Kubernetes unter IT-Grundschutz.

Erkennung und Prüfung

Ob du betroffen bist, zeigt zuerst das Image des Operators im Cluster, denn alles vor 2.21.0 enthält die Lücke. Danach lohnt ein Blick auf die Ressourcen selbst. Liste alle TriggerAuthentications und ClusterTriggerAuthentications mit hashiCorpVault-Block und vergleiche address mit deinen echten Vault-Servern. Auffällig sind unbekannte Adressen, Adressen, die auf Services oder Pods in Mandanten-Namespaces zeigen, und Einträge mit authentication: kubernetes ohne credential.

Schwieriger ist die Frage, ob ein Token bereits abgeflossen ist. Hier hilft am ehesten das Kubernetes-Audit-Log, das für jede Anfrage den authentifizierten Nutzer und die Quell-IP festhält. Das Chart nennt den ServiceAccount des Operators standardmäßig keda-operator. Bei einer Installation in den Namespace keda erscheint er im Log also als system:serviceaccount:keda:keda-operator. Anfragen dieses Nutzers, die nicht von den IPs der Operator-Pods kommen, sind ein starkes Signal für ein gestohlenes Token. Diese Suche ist meine eigene Ableitung und keine Vorgabe des Herstellers, Advisory und Release Notes nennen keine IoCs.

Bei einem Verdacht gibt es einen schnellen ersten Schritt. Laut Kubernetes-Doku verliert ein Pod-gebundenes Token seine Gültigkeit, sobald der Pod gelöscht wird, spätestens aber mit Ablauf seiner Laufzeit. Ein Neustart der Operator-Pods entwertet also die Tokens, die sie bis dahin hatten. Danach rotierst du alle Secrets, die der Operator lesen durfte, und prüfst, ob mit dem Token Jobs, Webhooks oder APIServices angelegt oder geändert wurden.

Betreiberempfehlung

Akut handeln solltest du, wenn KEDA vor 2.21.0 in einem Cluster läuft, in dem Teams eigene Namespaces verwalten und KEDA-Ressourcen selbst anlegen dürfen. Prüfe dann sofort die vorhandenen TriggerAuthentications und entziehe Mandanten das Anlegerecht oder setze eine Admission-Regel, bis das Update durch ist. Plane den Umstieg auf 2.21.0 mit dem Migrationsleitfaden, schalte die Allowlist für Vault-Adressen gleich mit ein und teste danach jeden Scaler einzeln.

Beobachten genügt, wenn nur das Plattform-Team TriggerAuthentications pflegt und sonst niemand Schreibrechte auf KEDA-Ressourcen hat. Das Update gehört trotzdem ins nächste reguläre Wartungsfenster, schon weil die Audience-Bindung auch künftige Fehler in diesem Pfad entschärft.

Unabhängig vom Einzelfall gilt, was ich bei jeder Lücke sage: Patches und Updates gehören automatisiert. Wenn Renovate oder Dependabot neue Versionen von Helm-Charts und Images selbstständig als Merge Request vorschlagen und die Pipeline sie gegen eine Staging-Umgebung testet, ist ein Security-Release wie KEDA 2.21.0 eine Sache von Stunden statt Wochen. Eine Karenzzeit für Routine-Updates schützt dabei vor kompromittierten Releases, Sicherheitsupdates nimmst du davon aus. Gerade bei einem Breaking Change wie diesem zeigt sich der Wert des Prozesses, denn die fehlschlagende Vault-Anmeldung fällt dann im Test auf und nicht erst in Produktion. Wer dagegen von Hand aktualisiert, hat in der Urlaubszeit schnell vier Wochen keinen Patch und danach ein Update, bei dem alles gleichzeitig knallt.

Häufige Fragen zu CVE-2026-77524

Was tun, wenn ein Token abgeflossen sein könnte?+

Starte die Operator-Pods neu. Pod-gebundene Tokens verlieren damit ihre Gültigkeit. Danach rotierst du alle Secrets, die der Operator lesen durfte, und prüfst das Audit-Log.

Warum schützt Pod Security hier nicht?+

Im Operator läuft kein fremder Code. Er baut eine Verbindung auf, die er ohnehin aufbauen darf. Dagegen wirken nur Egress-Regeln, Admission-Policies und RBAC.

Reicht ein Update auf 2.21.0?+

Es schließt die Lücke, solange du nicht in den Legacy-Modus wechselst. Schalte zusätzlich die Allowlist für Vault-Adressen ein. Sie ist ab Werk aus.

Ich nutze gar kein Vault. Bin ich betroffen?+

Ja, wenn Teams TriggerAuthentications anlegen dürfen. Der Angreifer bringt den falschen Vault-Server selbst mit. Die Lücke steckt im Operator, nicht in deiner Vault-Konfiguration.

Fazit

CVE-2026-77524 ist schon die zweite Lücke in diesem Code-Pfad, und der Unterschied zwischen den beiden Fixes ist lehrreich. Der erste prüfte, ob ein Token wie ein Token aussieht, und übersah, dass das gefährlichste Token jede dieser Prüfungen besteht. Der zweite bindet das Token an seinen Zweck und prüft das Ziel, bevor es verschickt wird. Die Lehre reicht weit über KEDA hinaus. Jeder Controller mit Clusterrechten, der Ziele oder Pfade aus Mandanten-Ressourcen übernimmt, ist ein möglicher Confused Deputy, wie in derselben Woche auch der Hook-Bypass in Argo CD zeigt. Es lohnt sich, die eigenen Operatoren einmal gezielt darauf anzusehen, wohin sie im Auftrag anderer Verbindungen aufbauen und mit welchem Ausweis.

Quellen

Ich prüfe deine Kubernetes-Cluster auf Operatoren mit zu weitreichenden Rechten, setze Egress- und Admission-Regeln um und automatisiere den Rollout von Security-Updates.

KEDA- und Operator-Audits, NetworkPolicies und ValidatingAdmissionPolicies für Mandanten-Cluster, Update-Automatisierung mit Renovate und getesteten Pipelines.

Plattform-Betrieb statt Beratung auf Papier: Ich prüfe, patche und härte deine Infrastruktur laufend.

Über den Autor