May 26, 2026
Intigriti May 2026 Challenge: XSS via Stored Payload & SCA Shield Bypass
Introduction

By Ryuk0x01
3 min read
Introduction
Solving an Intigriti CTF challenge is always a great milestone, but this month's challenge was particularly special for me. It marks my very first official submission on the Intigriti platform! This May (Challenge 0526), the objective was clear: achieve arbitrary JavaScript execution (alert(1)) on the latest version of Google Chrome.
The challenge introduced an interesting corporate-style sandbox implementation called Advanced SCA Shield. As a newcomer to the platform, I wanted to approach this methodically. In this deep dive, I will walk you through the architectural analysis of the application's source code, my fuzzing methodology to map the strict character and signature firewalls, and how I chained native JavaScript evaluation behaviors to achieve a clean, zero-click Stored XSS exploit on my very first try.
1. Source Code Review & Sink Identification
The target application features a dynamic portal where users can customize their public profile and view a community testimonials feed. I initiated my analysis by reviewing the JavaScript source responsible for rendering the testimonial components on the client-side DOM.
While inspecting the dynamic DOM construction within loadTestimonials(), I spotted an immediate inconsistency in input handling:
// Client-side rendering logic
let contentDiv = document.createElement('div');
contentDiv.innerHTML = DOMPurify.sanitize(t.content); // Protected
let nameDiv = document.createElement('div');
nameDiv.className = 'user-name';
nameDiv.innerHTML = t.user_name; // <--- Unsanitized Sink// Client-side rendering logic
let contentDiv = document.createElement('div');
contentDiv.innerHTML = DOMPurify.sanitize(t.content); // Protected
let nameDiv = document.createElement('div');
nameDiv.className = 'user-name';
nameDiv.innerHTML = t.user_name; // <--- Unsanitized SinkThe template rendering engine explicitly secures the testimonial description via DOMPurify, but completely trusts the user's profile display name (t.user_name), feeding it directly into an innerHTML sink. This established the Display Name input field as our primary Stored XSS vector.
2. Reverse Engineering the Advanced SCA Shield (Fuzzing)
Submitting a standard XSS vector like <img src=x onerror=alert(1)> immediately triggered an alert from the back-end infrastructure:
SCA Shield: Malicious characters detected! Quotes, parenthesis, dots, commas, and semicolons are strictly forbidden.
The application enforces a rigorous server-side character restriction engine. Let's list the forbidden characters:
- Blocked Tokens: " , ' , ( , ) , . , , , ;
Tactical Adjustments for Character Restrictions:
- HTML Attribute Quoting: Under the HTML5 specification, attribute values do not require encapsulation within single or double quotes as long as the value string contains no white spaces (e.g.,
id=target). - Executing Functions Without Parentheses: In modern ECMAScript (ES6+), Template Literals (backticks) can be utilized to execute functions. Writing
alert1`` instructs the JavaScript engine to call the globalalertfunction, passing the string as a template argument, effectively bypassing the bracket restriction.
Encountering the Signature Firewall
Once the payload structure was modified to obey the character limitations, the filter threw a secondary defensive alert:
SCA Shield: Malicious payload signature detected!
To profile the regex rules of this signature engine, I performed a structured fuzzing phase on specific HTML strings:
- The
srcBlacklist: Submitting standalone tags like<imgpassed safely, but any inclusion of thesrctoken (such as<src) was instantly flagged. - The Namespace Blacklist: The signature engine dropped any payload referencing the global scope object identifier explicitly (the word
windowwas banned entirely).
3. Engineering the Evasion Chain
To build a viable exploit payload, the vector had to fulfill three strict design requirements:
- It must execute code without invoking the
srcattribute. - It must execute automatically without requiring a click or scroll event (Zero-click constraint).
- It must reference the global execution context without explicitly writing the keyword
windoworalert.
Overcoming the src Constraint via HTML5 <details>
Standard elements like <img> or <iframe> rely heavily on the src or onload/onerror handlers. To bypass the src filter cleanly, I leveraged the HTML5 <details> tag engineered with open and ontoggle attributes. The open attribute instructs the browser to expand the interactive element immediately upon DOM insertion. The sudden state transition forces the browser to fire the native ontoggle event handler autonomously, satisfying the zero-interaction requirement.
Object Context Aliasing & Signature Obfuscation
To evade the keyword blacklist for window, we can access the global execution scope using alternative root object references natively mapped by the browser's DOM environment, such as top, self, or parent. I selected top.
Since the word alert is explicitly monitored by the signature engine, it had to be obfuscated. The base64 string representation of alert is YWxlcnQ=. By invoking the browser's native decoding utility atob via bracket array notation on the top object, we can construct the function reference dynamically at runtime.
Piecing the entire architecture together cleanly with template literals, we get the following structure:
<details open ontoggle=top[atob`YWxlcnQ=`]`1`><details open ontoggle=top[atob`YWxlcnQ=`]`1`>Exploit Vector Breakdown:
- SCA Character Compliance: The string contains no single/double quotes, parenthesis, commas, dots, or semicolons.
- SCA Signature Compliance: The
srctoken is missing,windowis bypassed viatop, andalertis safely stored as an opaque base64 block.
4. Full Proof of Concept (PoC) & Exploitation
- Access the application's profile management interface (
#profile). - Update the Display Name field with our crafted evasion payload:
<details open ontoggle=top[atobYWxlcnQ=]1> - Click save. The back-end SCA Shield processes and saves the string successfully because it matches no malicious signatures or character blocks.
- Navigate to the community testimonials tab and publish a dummy comment.
- As soon as the page reloads, the script engine attempts to populate the username via
innerHTML. The browser parses the<details>tag, expands it due to theopenstate, triggers theontoggleevent, decodes the base64 sequence, and firesalert(1)automatically on Chrome.
Root Cause & Remediation
Blacklist-based string filters and regular expressions offer poor security coverage against advanced client-side exploitation. The fundamental fix requires removing unsafe sinks from the rendering engine. The engineering team should modify the testimonial generation routine to treat user inputs purely as literal data strings by replacing innerHTML with textContent or innerText:
// Secure Implementation
nameDiv.textContent = t.user_name;// Secure Implementation
nameDiv.textContent = t.user_name;That's all for this challenge! Thanks for stopping by, and feel free to follow my profile to catch my upcoming CTF walkthroughs and security research logs. Stay secure!