CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-2836
8.40.01%

CVE-2026-2836: Host-Blind Cache Poisoning in Cloudflare Pingora

Alon Barad
Alon Barad
Software Engineer

Mar 5, 2026·6 min read·2 visits

PoC Available

Executive Summary (TL;DR)

Pingora < 0.8.0 used a default cache key that ignored the Host header. Attackers can poison the cache in multi-tenant environments by requesting a path on a controlled domain, causing the proxy to serve that content to victims requesting the same path on a different domain. Fixed in v0.8.0 by removing the insecure default.

A high-severity cache poisoning vulnerability exists in Cloudflare Pingora versions prior to 0.8.0 due to an insecure default implementation of the cache key generation logic. The default `CacheKey` trait implementation constructed cache keys using only the URI path and query string, ignoring the `Host` header and URI scheme. This 'host-blind' behavior allows attackers targeting multi-tenant or shared proxy environments to poison the cache by associating malicious content with a path (e.g., `/lib.js`) that is subsequently served to legitimate users requesting the same path on a different domain.

Vulnerability Overview

Cloudflare Pingora is a Rust-based asynchronous multithreaded framework for constructing HTTP proxies and load balancers. Ideally, a proxy handling traffic for multiple domains (multi-tenant) must differentiate cached resources not just by their path, but also by their origin (Host/Authority) and protocol (Scheme). Failure to do so results in 'cache collisions,' where distinct resources share the same cache key.

CVE-2026-2836 describes a flaw in the pingora-cache module where the default implementation of the CacheKey trait was insufficiently granular. By default, Pingora constructed cache keys solely based on the request URI (path and query). This design flaw meant that the proxy implicitly treated all requests for a specific path—regardless of the requested domain—as identical resources.

In a shared infrastructure environment, such as a Content Delivery Network (CDN) or a centralized API gateway, this vulnerability permits Cross-Tenant Cache Poisoning. An attacker can request a resource from a domain they control, populate the shared cache, and effectively overwrite or preempt the content served for the same path on a victim's domain.

Root Cause Analysis

The vulnerability stems from the CacheKey::default method located in pingora-cache/src/key.rs. In versions prior to 0.8.0, this method provided a convenience implementation for developers who did not wish to define custom caching logic. However, this convenience came at the cost of security context.

The specific failure mechanism lies in the key generation logic:

// Vulnerable implementation in pingora-cache/src/key.rs
impl CacheKey {
    pub fn default(req_header: &ReqHeader) -> Self {
        CacheKey {
            // ...
            // Critical Flaw: Only the URI path is used for the primary key
            primary: format!("{}", req_header.uri).into_bytes(),
            // ...
        }
    }
}

The req_header.uri in this context typically contains the path and query string (e.g., /index.html?id=1) but excludes the Host header or the :authority pseudo-header used in HTTP/2. Consequently, the cache key for http://attacker.com/style.css is identical to the cache key for https://bank.com/style.css. When the proxy performs a cache lookup, it ignores the discrepancy in the Host header, leading to a collision.

Code Analysis: The Fix

The remediation in Pingora v0.8.0 does not merely patch the key generation; it enforces a 'secure by design' philosophy by removing the dangerous default implementation entirely. The maintainers recognized that a 'default' cache key is rarely safe in a generic proxy framework because safety depends heavily on the specific deployment architecture.

Vulnerable Code (Pre-0.8.0):

// Allowing implicit, insecure key generation
let key = CacheKey::default(&session.req_header());

Patched Code (v0.8.0 - Commit 257b59a):

The default implementation was removed, and the ProxyHttp trait now requires the developer to explicitly implement cache_key_callback. If a developer attempts to use caching without defining this callback, the application will panic or fail to compile depending on usage, or hit the unimplemented! macro at runtime if the trait method is not overridden.

// pingora-proxy/src/proxy_trait.rs
fn cache_key_callback(&self, _session: &Session, _ctx: &mut Self::CTX) -> Result<CacheKey> {
    // Forces the developer to make a conscious decision about keying
    unimplemented!("cache_key_callback must be implemented when caching is enabled")
}

This change shifts the responsibility to the developer, ensuring that they must account for the Host header, X-Forwarded-Proto, and other variance headers relevant to their specific application logic.

Exploitation Methodology

Exploiting CVE-2026-2836 requires the attacker to have access to the same Pingora proxy instance as the victim. This is common in SaaS platforms, CDNs, or enterprise Kubernetes ingress controllers using Pingora.

Attack Scenario:

  1. Reconnaissance: The attacker identifies that a target service uses Pingora < 0.8.0 and relies on default caching behavior. They verify this by observing cache headers or timing differences.
  2. Setup: The attacker configures a domain (attacker.com) to resolve to the vulnerable Pingora instance. This is possible if the proxy allows arbitrary host headers or if the attacker is a legitimate tenant on the platform.
  3. Poisoning: The attacker hosts a malicious file (e.g., a JavaScript file containing a payload) at http://attacker.com/common/lib.js. They request this file through the proxy. The proxy caches the response using the key /common/lib.js.
  4. Execution: A victim user visits http://victim-tenant.com/common/lib.js. The proxy checks its cache for /common/lib.js. It finds the entry created in step 3 (ignoring that it came from attacker.com) and serves the malicious JavaScript to the victim.

This attack is particularly effective against static assets (JS, CSS, images) which are frequently cached and shared across different parts of an application ecosystem.

Impact Assessment

The impact of this vulnerability is high, primarily affecting the Integrity and Confidentiality of the application data.

  • Cross-Site Scripting (XSS): By poisoning the cache for JavaScript files, an attacker can achieve persistent Stored XSS against all users of the victim domain who pass through the proxy.
  • Defacement: Attackers can replace images or CSS files, altering the visual appearance of the victim's site.
  • Information Disclosure: In some configurations, an attacker might be able to retrieve sensitive cached data intended for the victim if they can guess the path and the cache does not validate the user's session token against the cached content (though this depends on specific application logic).
  • Denial of Service: An attacker could cache a 404 or 500 error page for a critical resource, rendering the victim application unusable until the cache expires.

The CVSS v4.0 score of 8.4 reflects the high severity of integrity loss (VI:H) and the potential for downstream impacts on the safety of the system (SI:H).

Mitigation and Remediation

The primary mitigation is to upgrade to Pingora v0.8.0 or later. This version enforces the implementation of custom cache key logic, preventing the accidental use of insecure defaults.

Implementation Guidance:

When upgrading, developers must implement the cache_key_callback function. A secure implementation must include the Host header and the URI scheme. Below is a reference implementation:

fn cache_key_callback(&self, session: &Session, _ctx: &mut Self::CTX) -> Result<CacheKey> {
    let req_header = session.req_header();
    
    // robustly extract host/authority
    let host = req_header.headers.get(http::header::HOST)
        .and_then(|v| v.to_str().ok())
        .or_else(|| req_header.uri.authority().map(|a| a.as_str()))
        .unwrap_or("unknown");
        
    // include scheme (http vs https)
    let scheme = req_header.uri.scheme_str().unwrap_or("http");
    
    let path = req_header.uri.path_and_query()
        .map(|pq| pq.as_str()).unwrap_or("/");
 
    // specific variance headers (e.g. Accept-Encoding) should also be considered
    Ok(CacheKey::new("", format!("{}://{}{}", scheme, host, path), ""))
}

If upgrading is not immediately feasible, developers must manually override the cache_key_callback in their existing ProxyHttp implementation to include the host and scheme, mimicking the logic above.

Official Patches

CloudflareCommit removing insecure default CacheKey implementation

Fix Analysis (2)

Technical Appendix

CVSS Score
8.4/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:N/SC:H/SI:H/SA:N
EPSS Probability
0.01%
Top 98% most exploited

Affected Systems

Cloudflare Pingora < 0.8.0Reverse Proxies built on PingoraLoad Balancers built on PingoraAPI Gateways built on Pingora

Affected Versions Detail

Product
Affected Versions
Fixed Version
Pingora
Cloudflare
< 0.8.00.8.0
AttributeDetail
CWE IDCWE-444
Attack VectorNetwork
CVSS v4.08.4 (High)
EPSS Score0.00014
Confidentiality ImpactNone
Integrity ImpactHigh
Exploit StatusPoC Available (Private)

MITRE ATT&CK Mapping

T1557Adversary-in-the-Middle
Credential Access
T1212Exploitation for Credential Access
Credential Access
CWE-444
Inconsistent Interpretation of HTTP Requests

Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')

Vulnerability Timeline

Initial protocol hardening initiated
2025-12-29
Fix committed to main branch
2026-02-19
CVE Published by Cloudflare CNA
2026-03-04
Added to NVD
2026-03-05

References & Sources

  • [1]Pingora Repository
  • [2]CVE-2026-2836 Record

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.