Security Report: Full Stack Trace Disclosure in HTTP Error Responses
1. Vulnerability Summary
| Field |
Value |
| Product |
prometheus/client_java |
| Affected Version |
1.8.0 (main branch); all prior 1.x releases using prometheus-metrics-exporter-httpserver are likely affected |
| Component |
prometheus-metrics-exporter-httpserver |
| Vulnerable File |
prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HttpExchangeAdapter.java |
| Vulnerable Lines |
103-141 |
| CWE |
CWE-209: Generation of Error Message Containing Sensitive Information |
| CVSS 3.1 Score |
5.3 (Medium) |
| CVSS 3.1 Vector |
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N |
| Commit (HEAD) |
a017f809 |
| Discoverer |
Security Research |
2. Vulnerability Description
The HttpExchangeAdapter class in the prometheus-metrics-exporter-httpserver module exposes full Java stack traces to remote, unauthenticated HTTP clients when any exception occurs during a Prometheus metrics scrape. The method sendErrorResponseWithStackTrace() serializes the complete exception stack trace -- including exception type, message, chained causes, class names, method names, file names, and line numbers -- into the HTTP 500 response body and sends it directly to the requesting client.
The /metrics endpoint served by the built-in HTTPServer is accessible without authentication by default. No configuration option exists to suppress stack trace output in error responses. Any exception raised during the scrape cycle (configuration errors, metric collection failures, runtime exceptions from custom collectors) triggers the disclosure.
This is a classic information disclosure vulnerability. Stack traces are considered sensitive information because they reveal internal implementation details that assist attackers in reconnaissance and targeted exploitation.
3. Affected Component Architecture
The vulnerability sits at the intersection of three components:
PrometheusScrapeHandler.handleRequest()
|
| catches IOException / RuntimeException
v
PrometheusHttpExchange.handleException() (interface)
|
| implemented by
v
HttpExchangeAdapter.sendErrorResponseWithStackTrace() <-- VULNERABILITY
|
v
HTTP 500 Response with full stack trace sent to client
Key observation: The servlet-based adapters (prometheus-metrics-exporter-servlet-jakarta and prometheus-metrics-exporter-servlet-javax) are not affected. Their handleException() implementations re-throw the exception to the servlet container rather than serializing stack traces into the response. This vulnerability is specific to the standalone HTTPServer exporter module.
4. Vulnerable Code
4.1 Primary Vulnerable Method
File: prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HttpExchangeAdapter.java
Lines: 103-141
private void sendErrorResponseWithStackTrace(Exception requestHandlerException) {
if (!responseSent) {
responseSent = true;
try {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
printWriter.write("An Exception occurred while scraping metrics: ");
requestHandlerException.printStackTrace(new PrintWriter(printWriter));
byte[] stackTrace = stringWriter.toString().getBytes(StandardCharsets.UTF_8);
httpExchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
httpExchange.sendResponseHeaders(500, stackTrace.length);
httpExchange.getResponseBody().write(stackTrace);
} catch (IOException errorWriterException) {
// ...
}
}
}
The critical issue is the call to requestHandlerException.printStackTrace(new PrintWriter(printWriter)) on line 110, which dumps the entire stack trace into a StringWriter whose contents are then written verbatim to the HTTP response body on line 114.
4.2 Call Sites
The vulnerable method is invoked from the two handleException() overloads (lines 93-101):
@Override
public void handleException(IOException e) throws IOException {
sendErrorResponseWithStackTrace(e);
}
@Override
public void handleException(RuntimeException e) {
sendErrorResponseWithStackTrace(e);
}
4.3 Trigger Point
PrometheusScrapeHandler.handleRequest() (lines 99-102) routes all caught exceptions through the exchange's handleException():
public void handleRequest(PrometheusHttpExchange exchange) throws IOException {
try {
// ... scrape metrics
} catch (IOException e) {
exchange.handleException(e);
} catch (RuntimeException e) {
exchange.handleException(e);
} finally {
exchange.close();
}
}
5. CVSS 3.1 Detailed Breakdown
| Metric |
Value |
Justification |
| Attack Vector (AV) |
Network (N) |
The /metrics HTTP endpoint is network-accessible. The HTTPServer binds to a network socket (default: all interfaces). |
| Attack Complexity (AC) |
Low (L) |
No special conditions required. The attacker only needs network access to the metrics port. An exception must occur during scraping, which can be triggered via environment variable misconfiguration or may happen naturally. |
| Privileges Required (PR) |
None (N) |
The HTTPServer does not require authentication by default. The Authenticator is an optional builder parameter that is null unless explicitly configured. |
| User Interaction (UI) |
None (N) |
No user interaction is needed. The attacker sends an HTTP request directly to the endpoint. |
| Scope (S) |
Unchanged (U) |
The vulnerability affects only the confidentiality of the vulnerable component; it does not cross trust boundaries to impact other components. |
| Confidentiality (C) |
Low (L) |
The disclosed information (stack traces, class names, library versions, configuration values, file paths) provides valuable reconnaissance data but does not directly expose credentials, secrets, or user data. |
| Integrity (I) |
None (N) |
No data modification occurs. |
| Availability (A) |
None (N) |
No denial-of-service impact. |
CVSS 3.1 Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
CVSS 3.1 Base Score: 5.3 (Medium)
6. Information Disclosed in Stack Traces
A stack trace exposed via this vulnerability can reveal the following categories of sensitive information:
| Category |
Example |
| Library version |
Package naming conventions and class structures identify the exact client_java version |
| Internal class names |
io.prometheus.metrics.config.Util, io.prometheus.metrics.config.HistogramProperties, etc. |
| Internal method names |
parseDoubleList, collect, scrape |
| Source file names and line numbers |
Util.java:90, HistogramProperties.java:45, Histogram.java:198 |
| Configuration values |
Exception messages from PrometheusPropertiesException embed the invalid configuration value |
| JVM internals |
Thread stack frames expose JVM version, internal classes, and runtime behavior |
| Application structure |
Custom collector class names, application framework stack frames |
| File system paths |
Absolute paths may appear in exception messages (e.g., properties file locations) |
| Package structure |
Full package hierarchy of the application and its dependencies |
7. Proof of Concept
7.1 Prerequisites
- A Java application using
prometheus-metrics-exporter-httpserver with the built-in HTTPServer
- Network access to the metrics HTTP port
7.2 Reproduction Steps
Step 1: Configure an invalid property to guarantee an exception during scrape.
export IO_PROMETHEUS_METRICS_HISTOGRAM_CLASSIC_UPPER_BOUNDS=invalid
This environment variable sets an invalid value for the histogram upper bounds configuration. When a Histogram metric is collected, HistogramProperties attempts to parse this value, throwing a PrometheusPropertiesException.
Step 2: Start the application with a Prometheus HTTP server.
Any Java application using the HTTPServer builder pattern:
import io.prometheus.metrics.core.metrics.Histogram;
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
import io.prometheus.metrics.model.registry.PrometheusRegistry;
public class VulnerableApp {
public static void main(String[] args) throws Exception {
Histogram histogram = Histogram.builder()
.name("example_histogram")
.help("An example histogram")
.register();
HTTPServer server = HTTPServer.builder()
.port(9400)
.buildAndStart();
System.out.println("Server started on port " + server.getPort());
Thread.currentThread().join();
}
}
Step 3: Request the metrics endpoint.
curl -s http://localhost:9400/metrics
Step 4: Observe the full stack trace in the response.
Expected HTTP 500 response body:
An Exception occurred while scraping metrics: io.prometheus.metrics.config.PrometheusPropertiesException:
io.prometheus.metrics.histogram.classic_upper_bounds=invalid: Expecting comma separated list of double values
at io.prometheus.metrics.config.Util.parseDoubleList(Util.java:90)
at io.prometheus.metrics.config.HistogramProperties.<init>(HistogramProperties.java:45)
at io.prometheus.metrics.core.metrics.Histogram.collect(Histogram.java:198)
at io.prometheus.metrics.model.registry.PrometheusRegistry.scrape(PrometheusRegistry.java:67)
at io.prometheus.metrics.exporter.common.PrometheusScrapeHandler.scrape(PrometheusScrapeHandler.java:143)
at io.prometheus.metrics.exporter.common.PrometheusScrapeHandler.handleRequest(PrometheusScrapeHandler.java:63)
at io.prometheus.metrics.exporter.httpserver.MetricsHandler.handle(MetricsHandler.java:25)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:82)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80)
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:692)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
...
7.3 Attack Scenario Without Misconfiguration
Even without attacker-controlled misconfiguration, stack traces are disclosed when:
- A custom
Collector registered in the PrometheusRegistry throws a RuntimeException during collect() (e.g., NullPointerException, IllegalStateException).
- A transient I/O error occurs during metric serialization.
- An internal consistency error is triggered by concurrent metric updates.
In production environments, these conditions are not uncommon, making the vulnerability exploitable through passive observation as well as active triggering.
8. Impact Analysis
8.1 Direct Impact
- Information Disclosure: Every error during metrics scraping sends implementation details to the network client. In environments where the metrics port is exposed (common in Kubernetes, cloud deployments, and monitoring infrastructure), any network-adjacent attacker can collect this information.
8.2 Reconnaissance Value
The disclosed stack traces provide an attacker with:
- Dependency mapping: Exact library names and versions enable identification of known CVEs in dependencies.
- Code path analysis: Method names and line numbers reveal execution flow, helping identify injection points or logic flaws.
- Configuration inference: Exception messages from
PrometheusPropertiesException and similar classes expose configuration parameters and their values.
- Technology fingerprinting: JVM version, application server type, and framework stack frames identify the technology stack.
8.3 Deployment Context
The HTTPServer component is commonly used in:
- Standalone Java applications without a servlet container
- Java agents for JVM instrumentation
- Microservices exposing metrics alongside application traffic
In these deployments, the metrics port is frequently accessible to broader network segments than intended, amplifying the exposure risk.
9. Scope Clarification
Affected Module
| Module |
Affected |
Reason |
prometheus-metrics-exporter-httpserver |
Yes |
HttpExchangeAdapter.sendErrorResponseWithStackTrace() writes full stack traces to the HTTP response body |
Unaffected Modules
| Module |
Affected |
Reason |
prometheus-metrics-exporter-servlet-jakarta |
No |
handleException() re-throws the exception to the servlet container (throw e;) |
prometheus-metrics-exporter-servlet-javax |
No |
handleException() re-throws the exception to the servlet container (throw e;) |
10. Recommended Remediation
10.1 Immediate Fix
Replace the stack trace serialization in sendErrorResponseWithStackTrace() with a generic error message. Log the full stack trace server-side for debugging purposes.
private void sendErrorResponseWithStackTrace(Exception requestHandlerException) {
if (!responseSent) {
responseSent = true;
try {
// Log the full stack trace server-side only
Logger.getLogger(this.getClass().getName())
.log(Level.SEVERE,
"An Exception occurred while scraping metrics.",
requestHandlerException);
// Send a generic error message to the client
byte[] message = "An internal error occurred while scraping metrics.\n"
.getBytes(StandardCharsets.UTF_8);
httpExchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
httpExchange.sendResponseHeaders(500, message.length);
httpExchange.getResponseBody().write(message);
} catch (IOException errorWriterException) {
// ... existing error handling
}
}
}
10.2 Configurable Error Verbosity
Introduce a configuration property (e.g., io.prometheus.metrics.exporter.httpserver.debug) that controls whether detailed error information is included in HTTP responses. Default to false (generic messages only).
10.3 Defense in Depth
- Document that the metrics HTTP port should be protected by network-level access controls (firewall rules, network policies).
- Encourage use of the
Authenticator builder option when the metrics port is exposed to untrusted networks.
- Consider implementing the same error-handling pattern used by the servlet adapters, which delegate exception handling to the container rather than serializing stack traces.
11. References
Security Report: Full Stack Trace Disclosure in HTTP Error Responses
1. Vulnerability Summary
prometheus-metrics-exporter-httpserverare likely affectedprometheus-metrics-exporter-httpserverprometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HttpExchangeAdapter.javaAV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:Na017f8092. Vulnerability Description
The
HttpExchangeAdapterclass in theprometheus-metrics-exporter-httpservermodule exposes full Java stack traces to remote, unauthenticated HTTP clients when any exception occurs during a Prometheus metrics scrape. The methodsendErrorResponseWithStackTrace()serializes the complete exception stack trace -- including exception type, message, chained causes, class names, method names, file names, and line numbers -- into the HTTP 500 response body and sends it directly to the requesting client.The
/metricsendpoint served by the built-inHTTPServeris accessible without authentication by default. No configuration option exists to suppress stack trace output in error responses. Any exception raised during the scrape cycle (configuration errors, metric collection failures, runtime exceptions from custom collectors) triggers the disclosure.This is a classic information disclosure vulnerability. Stack traces are considered sensitive information because they reveal internal implementation details that assist attackers in reconnaissance and targeted exploitation.
3. Affected Component Architecture
The vulnerability sits at the intersection of three components:
Key observation: The servlet-based adapters (
prometheus-metrics-exporter-servlet-jakartaandprometheus-metrics-exporter-servlet-javax) are not affected. TheirhandleException()implementations re-throw the exception to the servlet container rather than serializing stack traces into the response. This vulnerability is specific to the standaloneHTTPServerexporter module.4. Vulnerable Code
4.1 Primary Vulnerable Method
File:
prometheus-metrics-exporter-httpserver/src/main/java/io/prometheus/metrics/exporter/httpserver/HttpExchangeAdapter.javaLines: 103-141
The critical issue is the call to
requestHandlerException.printStackTrace(new PrintWriter(printWriter))on line 110, which dumps the entire stack trace into aStringWriterwhose contents are then written verbatim to the HTTP response body on line 114.4.2 Call Sites
The vulnerable method is invoked from the two
handleException()overloads (lines 93-101):4.3 Trigger Point
PrometheusScrapeHandler.handleRequest()(lines 99-102) routes all caught exceptions through the exchange'shandleException():5. CVSS 3.1 Detailed Breakdown
/metricsHTTP endpoint is network-accessible. TheHTTPServerbinds to a network socket (default: all interfaces).HTTPServerdoes not require authentication by default. TheAuthenticatoris an optional builder parameter that isnullunless explicitly configured.CVSS 3.1 Vector String:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NCVSS 3.1 Base Score: 5.3 (Medium)
6. Information Disclosed in Stack Traces
A stack trace exposed via this vulnerability can reveal the following categories of sensitive information:
client_javaversionio.prometheus.metrics.config.Util,io.prometheus.metrics.config.HistogramProperties, etc.parseDoubleList,collect,scrapeUtil.java:90,HistogramProperties.java:45,Histogram.java:198PrometheusPropertiesExceptionembed the invalid configuration value7. Proof of Concept
7.1 Prerequisites
prometheus-metrics-exporter-httpserverwith the built-inHTTPServer7.2 Reproduction Steps
Step 1: Configure an invalid property to guarantee an exception during scrape.
export IO_PROMETHEUS_METRICS_HISTOGRAM_CLASSIC_UPPER_BOUNDS=invalidThis environment variable sets an invalid value for the histogram upper bounds configuration. When a Histogram metric is collected,
HistogramPropertiesattempts to parse this value, throwing aPrometheusPropertiesException.Step 2: Start the application with a Prometheus HTTP server.
Any Java application using the
HTTPServerbuilder pattern:Step 3: Request the metrics endpoint.
Step 4: Observe the full stack trace in the response.
Expected HTTP 500 response body:
7.3 Attack Scenario Without Misconfiguration
Even without attacker-controlled misconfiguration, stack traces are disclosed when:
Collectorregistered in thePrometheusRegistrythrows aRuntimeExceptionduringcollect()(e.g.,NullPointerException,IllegalStateException).In production environments, these conditions are not uncommon, making the vulnerability exploitable through passive observation as well as active triggering.
8. Impact Analysis
8.1 Direct Impact
8.2 Reconnaissance Value
The disclosed stack traces provide an attacker with:
PrometheusPropertiesExceptionand similar classes expose configuration parameters and their values.8.3 Deployment Context
The
HTTPServercomponent is commonly used in:In these deployments, the metrics port is frequently accessible to broader network segments than intended, amplifying the exposure risk.
9. Scope Clarification
Affected Module
prometheus-metrics-exporter-httpserverHttpExchangeAdapter.sendErrorResponseWithStackTrace()writes full stack traces to the HTTP response bodyUnaffected Modules
prometheus-metrics-exporter-servlet-jakartahandleException()re-throws the exception to the servlet container (throw e;)prometheus-metrics-exporter-servlet-javaxhandleException()re-throws the exception to the servlet container (throw e;)10. Recommended Remediation
10.1 Immediate Fix
Replace the stack trace serialization in
sendErrorResponseWithStackTrace()with a generic error message. Log the full stack trace server-side for debugging purposes.10.2 Configurable Error Verbosity
Introduce a configuration property (e.g.,
io.prometheus.metrics.exporter.httpserver.debug) that controls whether detailed error information is included in HTTP responses. Default tofalse(generic messages only).10.3 Defense in Depth
Authenticatorbuilder option when the metrics port is exposed to untrusted networks.11. References
HttpExchangeAdapter.java