VYPR
Moderate severityNVD Advisory· Published Oct 28, 2025· Updated Apr 17, 2026

Consul's KV endpoint is vulnerable to denial of service

CVE-2025-11374

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.

PackageAffected versionsPatched versions
github.com/hashicorp/consulGo
< 1.22.01.22.0

Affected products

3
  • Hashicorp/Consulllm-fuzzy
    Range: before 1.22.0, 1.21.6, 1.20.8, 1.18.12
  • HashiCorp/Consulv5
    Range: 0
  • HashiCorp/Consul Enterprisev5
    Range: 0

Patches

1
72a358cd0253

SECVULN-29092 DoS handled for kvs_endpoint.go (#22916)

https://github.com/hashicorp/consulManisha KumariOct 14, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.