May 5, 2026·6 min read·5 visits
A stored XSS vulnerability in the Prometheus legacy web UI allows attackers to execute arbitrary JavaScript via maliciously crafted histogram bucket labels, affecting versions prior to 3.11.3.
Prometheus versions prior to 3.11.3 contain a Stored Cross-Site Scripting (XSS) vulnerability in the legacy web UI's heatmap visualization component. An attacker can inject arbitrary JavaScript by providing malicious `le` (less-than-or-equal) bucket labels within scraped metrics. When an administrator views the heatmap in the legacy UI, the payload executes within their browser context, potentially leading to unauthorized configuration access or actions performed on behalf of the user.
Prometheus is a widely deployed systems monitoring and alerting toolkit that collects and stores metrics as time-series data. The platform exposes a web-based user interface for querying and visualizing this data. A legacy version of this interface, often referred to as the 'Old UI', remains accessible in many deployments and contains a vulnerability within its heatmap visualization feature.
The vulnerability is classified as Stored Cross-Site Scripting (CWE-79). It specifically affects the rendering of histogram metrics. In the Prometheus data model, histograms track distributions of events using buckets, which are defined by an le (less-than-or-equal) label. The legacy UI processes these le labels to generate Y-axis ticks on heatmap charts.
An attacker can exploit this vulnerability by introducing a metric with a crafted le label into the Prometheus time-series database. When a victim subsequently queries this metric and generates a heatmap using the legacy UI, the malicious label is rendered without sanitization. This execution context allows the attacker's JavaScript to interact with the Prometheus web interface using the session privileges of the victim.
The root cause of this vulnerability lies in the improper neutralization of user-controllable input within the legacy UI's React components. Specifically, the flaw exists in the tickFormatter function for the Y-axis, located in web/ui/react-app/src/pages/graph/Graph.tsx. This function extracts the le label from the underlying metric data and returns it as a string to be rendered on the chart.
The legacy UI utilizes the Flot plotting library, a jQuery-based visualization tool. Unlike modern React rendering paradigms that inherently escape HTML entities, Flot often processes axis labels by directly injecting them into the Document Object Model (DOM) via methods such as .html(). This architectural choice shifts the responsibility of output encoding to the implementer.
The vulnerable implementation directly returns the raw string value of the le label. If the underlying time-series data contains HTML tags or JavaScript event handlers within this label, Flot parses and executes them during the chart rendering phase. The application implicitly trusts the structure and content of ingested metrics, failing to enforce a boundary between data and executable code at the presentation layer.
An analysis of web/ui/react-app/src/pages/graph/Graph.tsx reveals the exact mechanism of the flaw and its remediation. The vulnerable implementation defines the tickFormatter inline within the chart configuration object. It retrieves the label array from the metric data structure and accesses the le key directly.
// Vulnerable Code Snippet in Graph.tsx
options.yaxis.tickFormatter = (val) => `${val ? data[val - 1].labels.le : ''}`;The fix, introduced in commit 38f23b9075ced1de2b82d2dad8b2bebb1ecd5b7d, introduces an explicit output encoding step. The maintainers imported an existing escapeHTML utility function and wrapped the label extraction logic. This function converts characters with special meaning in HTML into their corresponding safe entities.
// Patched Code Snippet in Graph.tsx
import { escapeHTML } from '../../utils';
// ...
options.yaxis.tickFormatter = (val) => `${val ? escapeHTML(data[val - 1].labels.le) : ''}`;By ensuring that characters such as < and > are converted to < and >, the Flot library is forced to treat the label strictly as text content rather than markup. This change comprehensively addresses the vulnerability within the heatmap component by neutralizing the payload immediately prior to presentation.
Exploitation requires the attacker to inject a malicious metric into the Prometheus database. This can be achieved through two primary vectors: exposing the payload on an endpoint scraped by Prometheus, or pushing the metric directly via the Prometheus Remote Write API. The attacker does not require direct access to the Prometheus web UI to stage the payload.
A viable proof-of-concept payload targets the histogram metric format. The attacker exposes a metric where the le label contains an image tag with an onerror event handler. The event handler executes the arbitrary JavaScript.
http_request_duration_seconds_bucket{le="<img src=x onerror=alert('XSS')>", method="GET"} 1Once the metric is ingested, the vulnerability lies dormant until a user interacts with the data. An administrator or user must navigate to the 'Graph' page in the Old UI and execute a query that returns the malicious histogram bucket. As the UI attempts to draw the heatmap Y-axis ticks, the browser processes the <img> tag, fails to load the nonexistent source x, and fires the onerror event, triggering the payload.
The impact of this Stored XSS vulnerability is contingent upon the privileges of the victim and the network configuration of the Prometheus deployment. Because the payload executes in the context of the victim's session, the attacker inherits the victim's authentication state and interaction capabilities with the Prometheus interface.
Arbitrary JavaScript execution allows the attacker to silently issue API requests to the Prometheus server. This enables the exfiltration of sensitive monitoring data, including alerting rules, target configurations, and internal infrastructure topology exposed via metric labels. If the Prometheus instance is configured with administrative APIs enabled (e.g., the lifecycle or remote write administration endpoints), the attacker could manipulate server state.
Furthermore, if the victim is an infrastructure administrator accessing Prometheus from an internal, trusted network segment, the compromised browser context can serve as a pivot point. The attacker's script could initiate requests to other internal services accessible from the victim's machine, expanding the scope of the breach beyond the monitoring infrastructure.
The primary remediation for this vulnerability is to upgrade the Prometheus server to version 3.11.3 or later. This release includes the necessary output encoding modifications in the legacy UI components. Organizations utilizing older release branches should check for backported fixes, specifically around the 3.5.3 release line, as noted in vendor advisories.
If immediate patching is not feasible, organizations can mitigate the risk by modifying their operational procedures to strictly avoid the Old UI. Administrators should instruct all personnel to utilize the modern React-based UI included in newer Prometheus versions, which is not susceptible to this specific flaw due to its native component rendering behavior. Additionally, external visualization platforms like Grafana securely handle metric label sanitization and can be used as a safe alternative.
As a defense-in-depth measure, administrators can employ metric relabeling rules within the Prometheus scrape configuration to sanitize or drop incoming metrics containing suspicious characters in label values. Dropping metrics where the le label contains HTML bracket characters (< or >) effectively prevents the payload from entering the time-series database entirely.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Prometheus Prometheus Project | < 3.11.3 | 3.11.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network / Stored Ingestion |
| CVSS Score | 6.5 (Moderate) |
| Impact | Session Hijacking, Configuration Theft |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.