Every hacker knows the feeling. You are staring at a web application, throwing payloads into the dark, waiting for a callback that never comes. The recent Intigriti 0426 CTF challenge was exactly that — a grueling training arc that tested my patience, forced me to pivot, and reminded me why we never trust client-side routing.

If you are new to bug bounty or pentesting, complex exploit chains can look like magic. Today, I am going to break down exactly how I bypassed a heavy defense mechanism (DOMPurify), chained an API vulnerability with a routing flaw, and hit the admin bot with a devastating Stored XSS finishing move.

Let's dissect the jutsu.

The Target & The Defenses

The target was Northstar Notes, a markdown-based note-taking app. The Objective: Execute Cross-Site Scripting (XSS) against the admin bot and steal the flag hidden in its localStorage.

None

The core defense mechanism was DOMPurify, a library designed to aggressively strip out malicious HTML and JavaScript. If you just type <script>alert(1)</script> into a note, DOMPurify destroys it instantly. We needed a way to drop its shields.

Arc 1: Reading the Opponent's Code (The Sink)

To defeat a defense, you have to understand how it's configured. Diving into the frontend app.js source code revealed a crack in the armor.

The application fetches a manifest.json file to determine how to render the notes. I noticed that if this manifest contained a specific configuration ("renderMode": "full"), DOMPurify would unlock, allowing rich HTML elements like <form> and custom JavaScript widgets to execute.

Even better, I found the exact execution point (the "sink"). If the manifest also contained "widgetSink": "script", the app would take data from a specific HTML <div> and append it directly to the page as an executable <script> tag.

None
None

The Problem: The backend dynamically generated this manifest based on the user's settings. I needed a way to forge my own configuration.

Arc 2: Forging the Blade (Mass Assignment)

"A skilled swordsman knows his weapon inside and out."

To inject my configuration, I needed a Mass Assignment vulnerability. Noob Translation: Mass Assignment happens when a backend API blindly accepts a block of data from a user and saves it directly to the database without checking if the user is actually allowed to modify those specific fields.

The app's settings page allowed users to update basic preferences like theme and fontSize. But the backend API (/api/account/preferences) lacked strict input validation.

By intercepting the save request in Burp Suite, I bypassed the UI entirely and injected a deeply nested, malicious preset named exploit directly into my account database

None
UI
None
Preferences Api_Burp_Repeater
None
Mass Assignment

The backend swallowed it whole. The weapon was forged. Now, I just needed to force the admin bot to load my exploit preset instead of its own safe settings.

Arc 3: The Filler Episode (Plot Armor & Network Traps)

I staged my XSS payload in a note, designed to steal the bot's cookies and send them to my free Ngrok server. To trigger my forged settings, I used a Path Traversal payload in the URL.

I hit the URL myself, and it worked perfectly! I got my own cookies in my Ngrok terminal. I sent the exact same URL to the admin bot. I waited. Silence.

Why did the bot tank it?

Turns out, the bot had Plot Armor. Free Ngrok accounts serve an HTML "Visit Site" warning page to automated headless browsers to prevent phishing. When the bot tried to fetch my server, it choked on that HTML warning page. The exploit died silently in the console. I had walked right into a network trap.

The Lesson: Always ensure your exfiltration servers are silent and return exactly what the bot expects (like raw JSON or a 200 OK).

Arc 4: Bankai — The Final Kill Shot

"When your primary jutsu fails, you don't retreat. You adapt."

I ditched Ngrok and spun up a clean, interstitial-free listener on Webhook.site. I staged a fresh note with the ultimate exfiltration payload, designed to evade the application's regex filters:

None
Payload Input in Note

To force the bot to load my malicious preset, I used Client-Side Path Traversal. The application routed layouts based on the URL path: /note/<note_id>/<panel>. It took the <panel> and shoved it directly into a fetch() request without cleaning it.

By URL-encoding directory traversal characters (%2f is /), I could trick the bot's browser into escaping the notes directory and hitting the API directly!

The execution URL looked like this: https://challenge-0426.intigriti.io/note/<MY_NOTE_ID>/..%2f..%2fapi%2faccount%2fpreferences%2freader-presets%2fexploit

The Local Test: Before sending it to the admin, I needed to make sure my new jutsu actually worked. I pasted that exact URL into my own browser and hit enter. Instantly, I was redirected, and my Webhook dashboard lit up with my own session cookie and local storage data. The exploit was flawless.

None
Response at Webhook page

Now it was time to point the weapon at the real target. I fired this exact URL to the bot's "Request Review" endpoint.

The Chain Reaction:

  1. The bot loads the page. The app sets the panel to ../../api/account/preferences/reader-presets/exploit.
  2. The browser executes fetch('/note/<id>/../../api/.../manifest.json').
  3. The browser normalizes the path, escaping the notes directory, and queries the backend API.
  4. The backend identifies me as the note's author, pulls my Mass-Assigned exploit preset, and serves it to the bot.
  5. DOMPurify unlocks. The widgetSink fires, turning my data-cfg payload into an executable <script> tag.
  6. The bot is violently redirected to my Webhook, dropping its cookies and localStorage directly into my logs.
None
Bot Reponse [ Flag ]

A few seconds later, my Webhook dashboard lit up again. This time, inside the localStorage array, was the admin's private Note ID. I navigated to it and claimed the flag.

The Aftermath:

This CTF was a masterclass in why you can never look at vulnerabilities in a vacuum. A Mass Assignment bug on the backend is useless if you can't force the target to load it. A client-side Path Traversal is harmless unless it points to a loaded gun. And an XSS sink requires the exact right environmental conditions to fire.

But chain them together? That is an Avengers-level threat.

Shoutouts & Respect (The Architects)

A massive shoutout to Intigriti for consistently providing the arena for these monthly training arcs. Their challenges bridge the gap between abstract theory and real-world bug bounty hunting.

Keep your payloads sharp, watch out for network-layer traps, and never trust client-side routing. Until the next battle.