Zammad: Stored XSS in der Ticketansicht über Artikel-Metadaten
Ein Kunde speichert Markup in den Metadaten seines Ticket-Artikels, das beim Öffnen des Tickets JavaScript in der Sitzung des Agenten ausführt.
Advisory-ID: TP-2026-115
Produkt: Zammad (webbasiertes Helpdesk- und Ticketsystem)
Schwachstellentyp: Stored Cross-Site Scripting (CWE-79)
CVE: nicht beantragt
CVSS 4.0: 8.5 (Hoch) · CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N
Betroffene Versionen: <= 7.2.0
Hersteller-Advisory: GHSA-fhq9-h46w-68hg
Behoben in: 7.2.1
Gemeldet: 23. August 2026
Zusammenfassung
Zammad ist ein webbasiertes Helpdesk-System, in dem Kunden Tickets anlegen und Agenten sie bearbeiten. Beim Anlegen eines Artikels nimmt der Server vom Kunden ein frei gefülltes Feld preferences an, das eigentlich nur intern erzeugte Metadaten wie Links aus dem Facebook-Kanal oder S/MIME-Ergebnisse trägt. Die Standard-Ticketansicht gibt zwei Unterschlüssel davon, links und security.type, ohne HTML-Maskierung aus. Ein Kunde mit Standardrechten bricht damit aus einem Attribut aus und lädt über ein <iframe srcdoc> ein selbst hochgeladenes Skript aus der Zammad-Herkunft, das die Content-Security-Policy zulässt. Öffnet ein Agent das Ticket, läuft fremder Code mit dessen Sitzung und Rechten.
Ursache
Beim Anlegen eines Artikels übernimmt param_cleanup das Feld preferences aus der Anfrage (app/controllers/concerns/creates_ticket_articles.rb:31-32), und der Zweig für Nicht-Agenten überschreibt nur sender_id, type_id und internal und entfernt origin_by_id, lässt preferences aber unberührt (:35-45). Die Artikel-Assets liefern alle Attribute einschließlich preferences unverändert an jeden Leser aus (app/models/ticket/article/assets.rb:47). Die Ticketansicht übernimmt article.preferences.links (app/assets/javascripts/app/controllers/article_view/item.coffee:104), und die Vorlage gibt link.url und link.target mit <%- %> roh in ein Attribut in doppelten Anführungszeichen aus (app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco:174), ebenso securityPreferences.type hinter einer Bedingung, die der Angreifer selbst erfüllt (:53, :57). Das Ergebnis landet über jQuery .html() im DOM (item.coffee:218). Die CSP script-src 'self' hält das nicht auf, weil derselbe Kunde ein Skript als Anhang hochlädt, das mit dem vom Client gesetzten Content-Type: application/javascript ausgeliefert wird (app/controllers/application_controller/has_download/download_file.rb:40-44).
Proof of Concept
POST /api/v1/ticket_articles (als Kunde)
{"ticket_id": <eigenes Ticket>, "body": "Hallo", "type": "note",
"preferences": {"links": [{"name": "x", "target": "_blank",
"url": "x\"><iframe srcdoc=\"<script src=/api/v1/ticket_attachment/T/A/X></script>\"></iframe><b w=\""}]}}
/api/v1/ticket_attachment/T/A/X ist ein vom selben Kunden hochgeladener Anhang mit Content-Type: application/javascript. Das srcdoc-Dokument erbt die Herkunft und die CSP der Zammad-Seite, deshalb läuft das gleich-herkünftige Skript und greift über window.top auf die Sitzung des Agenten zu, sobald dieser das Ticket öffnet.
Auswirkung
- Lesen aller Tickets, auf die der Agent Zugriff hat, einschließlich anderer Gruppen und interner Notizen.
- Schreiben von Artikeln, internen Notizen und Ticketänderungen im Namen des Agenten.
- Volle Administrationsrechte, wenn der betroffene Agent zugleich Administrator ist.
- In Kombination mit dem anonymen Mass Assignment aus TP-2026-114 ist für das Platzieren der Payload kein vorhandenes Kundenkonto nötig.
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.
