Consul's KV endpoint is vulnerable to denial of service
Description
Consul and Consul Enterprise’s (“Consul”) key/value endpoint is vulnerable to denial of service (DoS) due to incorrect Content Length header validation. This vulnerability, CVE-2025-11374, is fixed in Consul Community Edition 1.22.0 and Consul Enterprise 1.22.0, 1.21.6, 1.20.8 and 1.18.12.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Incorrect Content-Length header validation in the Consul KV endpoint allows remote denial-of-service via oversized request bodies.
Vulnerability
Overview
CVE-2025-11374 is a denial-of-service (DoS) vulnerability in the key/value (KV) endpoint of HashiCorp Consul and Consul Enterprise. The root cause is improper validation of the HTTP Content-Length header, which can lead to resource exhaustion or crashes when a crafted request is sent. The fix improves validation by enforcing a maximum allowed body size and rejecting requests that exceed the configured limit with an appropriate HTTP 413 Payload Too Large response [1][2][3].
Attack
Vector and Prerequisites
An attacker with network access to a Consul agent's HTTP API can exploit this vulnerability by sending a PUT or POST request to the KV endpoint (e.g., /v1/kv/) with a Content-Length header that greatly exceeds the configured KVMaxValueSize limit, or by sending a body that is larger than the limit without a Content-Length header. No authentication is required if the API is exposed without ACL enforcement; otherwise, an attacker would need valid credentials. The vulnerability is triggered over the network and does not require any special privileges beyond network reachability [2][3].
Impact
Successful exploitation results in a denial-of-service condition, potentially causing the Consul agent to become unresponsive or crash. This can disrupt service discovery, health checking, and configuration management within the affected cluster. The impact is limited to a single agent per request, but repeated attacks could degrade or halt cluster operations [1][2].
Mitigation
HashiCorp has released fixes in Consul Community Edition 1.22.0 and Consul Enterprise 1.22.0, 1.21.6, 1.20.8, and 1.18.12. Users are strongly advised to upgrade to a patched version. There is no workaround beyond restricting network access to the API endpoint [1][2].
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/hashicorp/consulGo | < 1.22.0 | 1.22.0 |
Affected products
3- HashiCorp/Consulv5Range: 0
- HashiCorp/Consul Enterprisev5Range: 0
Patches
172a358cd0253SECVULN-29092 DoS handled for kvs_endpoint.go (#22916)
3 files changed · +129 −7
agent/kvs_endpoint.go+34 −7 modified@@ -5,6 +5,7 @@ package agent import ( "bytes" + "errors" "fmt" "io" "net/http" @@ -238,19 +239,45 @@ func (s *HTTPHandlers) KVSPut(resp http.ResponseWriter, req *http.Request, args } // Check the content-length - if req.ContentLength > int64(s.agent.config.KVMaxValueSize) { + maxSize := int64(s.agent.config.KVMaxValueSize) + var buf *bytes.Buffer + + switch { + case req.ContentLength <= 0 && req.Body == nil: + return "Request has no content-length & no body", nil + + case req.ContentLength <= 0 && req.Body != nil: + // LimitReader to limit copy of large requests with no Content-Length + //+1 ensures we can detect if the body is too large + byteReader := http.MaxBytesReader(nil, req.Body, maxSize+1) + buf = new(bytes.Buffer) + if _, err := io.Copy(buf, byteReader); err != nil { + var bodyTooLargeErr *http.MaxBytesError + if errors.As(err, &bodyTooLargeErr) { + return nil, HTTPError{ + StatusCode: http.StatusRequestEntityTooLarge, + Reason: fmt.Sprintf("Request body too large. Max allowed is %d bytes.", maxSize), + } + } + return nil, err + } + + case req.ContentLength > maxSize: + // Throw error if Content-Length is greater than max size return nil, HTTPError{ StatusCode: http.StatusRequestEntityTooLarge, Reason: fmt.Sprintf("Request body(%d bytes) too large, max size: %d bytes. See %s.", - req.ContentLength, s.agent.config.KVMaxValueSize, "https://developer.hashicorp.com/docs/agent/config/config-files#kv_max_value_size"), + req.ContentLength, maxSize, "https://developer.hashicorp.com/docs/agent/config/config-files#kv_max_value_size"), } - } - // Copy the value - buf := bytes.NewBuffer(nil) - if _, err := io.Copy(buf, req.Body); err != nil { - return nil, err + default: + // Copy the value + buf = bytes.NewBuffer(nil) + if _, err := io.Copy(buf, req.Body); err != nil { + return nil, err + } } + applyReq.DirEnt.Value = buf.Bytes() // Make the RPC
agent/kvs_endpoint_test.go+92 −0 modified@@ -6,6 +6,7 @@ package agent import ( "bytes" "fmt" + "io" "net/http" "net/http/httptest" "path" @@ -37,6 +38,7 @@ func TestKVSEndpoint_PUT_GET_DELETE(t *testing.T) { for _, key := range keys { buf := bytes.NewBuffer([]byte("test")) req, _ := http.NewRequest("PUT", "/v1/kv/"+key, buf) + req.ContentLength = int64(buf.Len()) resp := httptest.NewRecorder() obj, err := a.srv.KVSEndpoint(resp, req) if err != nil { @@ -554,6 +556,96 @@ func TestKVSEndpoint_GET(t *testing.T) { } } +func TestKVSPUT_SwitchCases(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode") + } + + t.Parallel() + a := NewTestAgent(t, "") + defer a.Shutdown() + + maxSize := int(a.srv.agent.config.KVMaxValueSize) + + tests := []struct { + name string + body string + contentLength int64 + expectErr bool + expectMsg string + expectHTTPMsg string + }{ + { + name: "Case 2: No Content-Length but Body exists (allowed size)", + body: "small-value", + contentLength: 0, + expectErr: false, + }, + { + name: "Case 2b: No Content-Length but Body exists (too large)", + body: strings.Repeat("x", maxSize+50), + contentLength: 0, + expectErr: true, + expectHTTPMsg: fmt.Sprintf("Request body too large. Max allowed is %d bytes.", maxSize), + }, + { + name: "Case 3: Content-Length greater than max allowed limit", + body: strings.Repeat("x", maxSize+10), + contentLength: int64(maxSize) + 10, + expectErr: true, + expectHTTPMsg: fmt.Sprintf("Request body(%d bytes) too large, max size: %d bytes.", int64(maxSize)+10, maxSize), + }, + { + name: "Case 4: Normal body within allowed limit", + body: "tiny", + contentLength: 4, + expectErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var bodyReader io.Reader + if tt.body != "" { + bodyReader = bytes.NewBufferString(tt.body) + } else { + bodyReader = nil + } + + req := httptest.NewRequest(http.MethodPut, "/v1/kv/switch-test", bodyReader) + req.ContentLength = tt.contentLength + resp := httptest.NewRecorder() + + obj, err := a.srv.KVSEndpoint(resp, req) + + // Expected error cases + if tt.expectErr { + if err == nil { + t.Fatalf("expected error, got nil") + } + httpErr, ok := err.(HTTPError) + if !ok { + t.Fatalf("expected HTTPError, got %T", err) + } + if !strings.Contains(httpErr.Reason, tt.expectHTTPMsg[:20]) { // partial match + t.Fatalf("expected HTTPError reason to contain %q, got %q", tt.expectHTTPMsg, httpErr.Reason) + } + return + } + + // Unexpected error + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // Normal successful PUT result + if res, ok := obj.(bool); !ok || !res { + t.Fatalf("expected successful PUT result, got %v", obj) + } + }) + } +} + func TestKVSEndpoint_DELETE_ConflictingFlags(t *testing.T) { if testing.Short() { t.Skip("too slow for testing.Short")
.changelog/22916.txt+3 −0 added@@ -0,0 +1,3 @@ +```release-note:security +security: Improved validation of the Content-Length header in the Consul KV endpoint to prevent potential denial of service attacks[CVE-2025-11374]() +```
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-7g3r-8c6v-hfmrghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-11374ghsaADVISORY
- discuss.hashicorp.com/t/hcsec-2025-29-consuls-kv-endpoint-is-vulnerable-to-denial-of-service/76724ghsaWEB
- github.com/hashicorp/consul/commit/72a358cd02533477536ad4bd2b781f520fa7fac6ghsaWEB
- github.com/hashicorp/consul/pull/22916ghsaWEB
- github.com/hashicorp/consul/releases/tag/v1.22.0ghsaWEB
- pkg.go.dev/vuln/GO-2025-4081ghsaWEB
News mentions
0No linked articles in our index yet.