Apr 7, 2026·6 min read·2 visits
A flaw in OpenTelemetry-Go's baggage propagator allows attackers to bypass per-header length limits by sending multiple fragmented headers, causing linear memory allocation growth and eventual application Denial of Service.
OpenTelemetry-Go versions 1.36.0 through 1.40.0 are vulnerable to a resource exhaustion flaw in the W3C Baggage propagation mechanism. Unauthenticated remote attackers can trigger excessive memory and CPU consumption by sending requests with fragmented HTTP baggage headers, resulting in a Denial of Service condition.
OpenTelemetry-Go versions 1.36.0 through 1.40.0 contain a resource exhaustion vulnerability within the W3C Baggage propagation mechanism. The flaw, designated CVE-2026-29181, allows unauthenticated remote attackers to trigger a Denial of Service (DoS) condition via crafted HTTP requests. The vulnerability resides specifically within the go.opentelemetry.io/otel/baggage and go.opentelemetry.io/otel/propagation packages.
The baggage propagator is responsible for parsing and extracting distributed tracing context from incoming HTTP request headers. W3C Baggage specifications permit multiple baggage headers, treating them as semantically equivalent to a single concatenated header. The Go implementation parses these headers to reconstruct the context state for downstream telemetry propagation.
The implementation correctly enforces limits on individual headers but fails to bound the aggregate processing cost across multiple headers. This discrepancy enables a CPU and memory amplification attack. Attackers can intentionally fragment payloads across hundreds of headers to force excessive allocations, leading to degraded service performance or out-of-memory (OOM) crashes.
The vulnerability is classified under CWE-770 (Allocation of Resources Without Limits or Throttling). During request processing, the extractMultiBaggage function in propagation/baggage.go iterates over all instances of the baggage header provided within the TextMapCarrier. For each extracted header string, the function invokes baggage.Parse(bStr) to deserialize the key-value members.
The parsing routine includes a safety constraint restricting individual baggage header values to a maximum of 8192 bytes. However, this check evaluates each string in isolation. The state machine allocates memory and processes text structures for every parsed header, subsequently aggregating the parsed members into a shared slice without tracking the cumulative byte size or member count across the entire request.
Because the maximum total header size is governed only by the underlying HTTP server implementation—typically 1MB in the standard Go net/http package—an attacker can supply hundreds of 8191-byte headers. The baggage.Parse function processes each header sequentially. The CPU cycles consumed and memory allocated scale linearly with the number of headers, bypassing the intended design limits of the W3C specification.
Prior to version 1.41.0, the extractMultiBaggage loop invoked baggage.Parse blindly on every header string. The parsed structures were appended to a running list of members. The lack of an early exit condition meant the server was forced to parse the entirety of the attacker's payload before constructing the final Baggage object.
The developers modified the core parsing logic in commit aa1894e09e3fe66860c7885cb40f98901b35277f to track both the aggregate member count and the cumulative byte size across all header instances. A global budget limits the total extracted members to 64, aligning strictly with W3C recommendations. The extraction loop now breaks immediately when the 8192-byte or 64-member limit is reached.
// Vulnerable implementation concept
for _, bStr := range headers {
b, _ := baggage.Parse(bStr)
members = append(members, b.Members()...)
}
// Patched implementation concept
var totalBytes int
for _, bStr := range headers {
if totalBytes >= 8192 || len(members) >= 64 {
break // Early exit prevents amplification
}
b, _ := baggage.Parse(bStr)
members = append(members, b.Members()...)
totalBytes += len(bStr)
}The patch introduces partial returns, ensuring that valid members parsed before hitting the limit are retained. This implementation safely truncates oversized payloads without failing open or causing a hard operational fault.
Exploitation requires the attacker to identify an internet-facing service that utilizes OpenTelemetry-Go for distributed tracing. The attacker constructs a malicious HTTP request containing a high volume of baggage headers. Each individual header is padded to closely approach the 8192-byte limit, ensuring it passes the initial length validation checks within baggage.Parse.
The attacker transmits this request to the target server over standard HTTP or HTTPS. The Go net/http server accepts the request as long as the total header size remains under the default 1MB threshold. The application then passes the TextMapCarrier to the OpenTelemetry propagator, triggering the vulnerable extractMultiBaggage function.
Proof-of-concept testing demonstrates substantial resource amplification. Processing a baseline request with a single header allocates approximately 133 kilobytes across 480 memory allocations. When subjected to an attack payload containing 80 headers spread across 40 requests, memory allocations spike to over 10.3 megabytes and 16,165 total allocations. This translates to a measurable increase in p95 latency from 0ms to 7ms under modest load.
The primary impact is an application-layer Denial of Service. By continuously sending crafted requests, an attacker forces the Go runtime garbage collector into continuous operation, drastically increasing CPU consumption. Concurrently, the unconstrained allocations apply severe memory pressure to the application heap.
Prolonged exploitation leads to resource starvation for legitimate traffic. In memory-constrained environments such as Kubernetes containers or serverless functions, the operating system's Out-Of-Memory (OOM) killer will terminate the process. This requires orchestration systems to restart the service, causing persistent disruption to availability.
The vulnerability scores a 7.5 (High) on the CVSS v3.1 scale. The vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H reflects the network attack vector and the lack of authentication requirements. The flaw exclusively affects the availability of the system, maintaining the confidentiality and integrity of the underlying data.
Organizations must upgrade the go.opentelemetry.io/otel/baggage and go.opentelemetry.io/otel/propagation packages to version 1.41.0. This release contains the updated parsing logic and strict global limit enforcement. Developers should verify dependency graphs to ensure transitive dependencies are also updated to the patched version.
If immediate patching is not feasible, operators should implement mitigations at the edge network layer. Web Application Firewalls (WAF) or ingress controllers can be configured to drop requests containing more than a predefined number of baggage headers. Alternatively, reverse proxies can enforce strict limits on the total byte size of HTTP headers per request.
Rate limiting and connection shedding provide secondary defense mechanisms against DoS conditions. Implementing strict memory bounds and automated restart policies at the container orchestration layer ensures the service recovers rapidly if an OOM event occurs. These operational controls reduce the systemic impact of resource exhaustion vulnerabilities.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenTelemetry-Go OpenTelemetry | >= 1.36.0, <= 1.40.0 | 1.41.0 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Resource Exhaustion / DoS |
| CWE ID | CWE-770 |
| CVSS Score | 7.5 (High) |
| Attack Vector | Network |
| Privileges Required | None |
| Exploit Maturity | Proof of Concept |
Allocation of Resources Without Limits or Throttling