
Minimaler Python-PoC für CVE-2026-40179: injiziert einen bösartigen Metriknamen über unauthentifiziertes Prometheus-remote_write, um gespeichertes XSS in der Web-UI auszulösen. Keine Protobuf-Stubs erforderlich.
remote_writeEin minimaler, dependency-armer Proof of Concept für CVE-2026-40179 (GHSA-vffh-x6r8-xx99): gespeichertes Cross-Site-Scripting in der Prometheus-Web-UI, ausgeliefert über einen nicht authentifizierten remote_write-Endpunkt.
Prometheus 3.x rendert Metriknamen und Label-Werte in der Web-UI über innerHTML, ohne sie zu escapen. Da Prometheus v3 die Label-Validierung gelockert hat, um beliebiges UTF-8 zu erlauben — einschließlich <, > und " — kann ein Metrikname ein HTML-Payload tragen, das ausgeführt wird, wenn jemand die UI durchsucht.
| Betroffen | Prometheus 3.0.0 – 3.5.1 |
| Behoben in | 3.5.2 |
| Auswirkung | Gespeichertes XSS, wird im Browser jedes Benutzers ausgeführt, der die betroffene Metrik ansieht |
| Verwundbare Oberflächen | Graph-Tooltips, Metrics Explorer |
Das XSS selbst benötigt einen Weg, um eine präparierte Serie in die TSDB zu bringen. Jede der folgenden Möglichkeiten funktioniert:
remote_write-Endpunkt (was dieser PoC nutzt),Ein im Internet oder LAN exponiertes Prometheus ohne vorgeschaltete Authentifizierung bietet all das kostenlos an — genau das macht dies praktisch ausnutzbar statt nur theoretisch.
Sendet ein einzelnes POST /api/v1/write mit einer Zeitreihe, deren __name__ lautet:
pentest_poc_cve_2026_40179
Die Protobuf-WriteRequest ist in ~40 Zeilen handkodiert, daher gibt es keine protobuf-, prometheus_pb2- oder promtool-Abhängigkeit — nur python-snappy für die Wire-Kompression und requests für den POST. Das macht es einfach, es auf einem Jump-Host abzulegen, auf dem man keine generierten Protobuf-Stubs bauen kann.
python3 -m pip install python-snappy requests
python-snappy benötigt die Snappy-C-Bibliothek:
# Debian/Ubuntu
sudo apt install libsnappy-dev
# macOS
brew install snappy
python3 cve-2026-40179-poc.py <target_ip>
Das Skript geht von HTTPS auf Port 9090 mit deaktivierter Zertifikatsprüfung aus (typisch für eine interne Instanz mit selbstsigniertem Zertifikat). Wenn dein Ziel einfaches HTTP oder ein anderer Port ist, bearbeite die url-Zeile in main().
Eine erfolgreiche Injektion liefert HTTP 204 zurück:
POST https://192.0.2.10:9090/api/v1/write -> 204
Frag die Serie in der Prometheus-UI oder -API ab — sie sollte sofort zurückkommen:
{__name__=~"pentest_poc.*"}
Die Tabellenansicht escaped sie korrekt und zeigt das Payload als Klartext. Das ist erwartet und nicht die verwundbare Oberfläche.
Öffne den Metrics Explorer (den Metrik-Browser neben dem Query-Feld), suche nach pentest_poc und fahre mit der Maus über den aufgelisteten Eintrag.
In Feldtests gegen 3.2.1 feuerte der Metrics-Explorer-Pfad zuverlässig, während der Graph-Tab-Tooltip nicht reproduzierbar war — die injizierte Stichprobe (Wert 1.0) wurde auf dem dargestellten Diagramm nicht sichtbar gerendert, und auch das Überfahren der Legende löste es nicht aus. Beide Oberflächen sind im Advisory genannt; wenn eine auf deiner Zielversion nicht feuert, versuche die andere, bevor du schlussfolgerst, dass sie nicht verwundbar ist.
Das Standard-Payload verwendet console.log(), damit es niemanden unterbricht, der nicht danach sucht. Für eine sichtbare Demo — etwa einen Screenshot für einen Bericht — ändere METRIC_NAME so, dass alert() verwendet wird:
METRIC_NAME = ('pentest_poc_cve_2026_40179'
'')
Die injizierte Serie ist persistent. Einmal geschrieben, bleibt sie in der TSDB, bis die Aufbewahrungsfrist sie entfernt. Zum Entfernen ist die Admin-API erforderlich, die standardmäßig deaktiviert ist:
# Funktioniert nur, wenn Prometheus mit --web.enable-admin-api gestartet wurde
curl -X POST -g 'https://<target>:9090/api/v1/admin/tsdb/delete_series?match[]={__name__=~"pentest_poc.*"}'
curl -X POST 'https://<target>:9090/api/v1/admin/tsdb/clean_tombstones'
Wenn die Admin-API deaktiviert ist, gibt es keinen Remote-Bereinigungspfad. Die einzige Alternative ist promtool tsdb, lokal auf dem Host ausgeführt — worauf du wahrscheinlich keinen Zugriff hast.
Praktische Konsequenzen:
job="pentest_poc"), sodass er leicht zu finden und offensichtlich ein Testartefakt ist.Zur Reproduktion gegen eine Wegwerf-Instanz statt gegen etwas Live-System:
docker run --rm -p 9090:9090 prom/prometheus:v3.2.1 \
--config.file=/etc/prometheus/prometheus.yml \
--web.enable-remote-write-receiver \
--web.enable-admin-api
Richte das Skript dann auf 127.0.0.1 (ändere das URL-Schema in main() auf http://). Der Start mit --web.enable-admin-api bedeutet, dass du die Serie danach tatsächlich löschen kannst.
--web.config.file mit basic_auth_users.--web.enable-remote-write-receiver nur, wenn du es brauchst, und setze es niemals unauthentifiziert ein.Beachte, dass (1) allein das XSS behebt, aber ein unauthentifiziertes Prometheus für sich genommen eine erhebliche Informationsoffenlegungsfläche bleibt: Die vollständige Metriknamenliste, das Scrape-Target-Inventar und interne Hostnamen sind alle ohne Anmeldedaten lesbar.
Dies wird für Verteidiger, Forscher und Tester veröffentlicht, die unter Autorisierung arbeiten. Die Schwachstelle ist öffentlich offengelegt und upstream gepatcht.
Die Ausführung gegen ein System schreibt persistente Daten darauf. Richte es nicht gegen Infrastruktur, die du nicht besitzt oder für die du keine ausdrückliche schriftliche Genehmigung zum Testen hast.
MIT — siehe LICENSE.