CVE-2025-23061 Overview
CVE-2025-23061 is a search injection vulnerability in Mongoose, the popular MongoDB object modeling library for Node.js. Versions before 8.9.5 improperly handle a nested $where filter inside a populate() match. Attackers can bypass the existing $where block by nesting the operator one level deeper, achieving code injection [CWE-94] through MongoDB server-side JavaScript evaluation. The flaw exists because the original remediation for CVE-2024-53900 only validated top-level and array-level $where keys, missing nested structures. Applications that pass user-controlled input into populate() match conditions are exposed to remote, unauthenticated exploitation.
Critical Impact
Unauthenticated attackers can inject arbitrary JavaScript expressions evaluated by MongoDB through populate() match, leading to data exfiltration, manipulation, and potential service disruption.
Affected Products
- Mongoose for Node.js, all versions prior to 8.9.5
- Applications using populate() with user-controlled match filters
- Node.js services backed by MongoDB through the mongoosejs/mongoose ODM
Discovery Timeline
- 2025-01-15 - CVE-2025-23061 published to NVD
- 2025-10-31 - Last updated in NVD database
Technical Details for CVE-2025-23061
Vulnerability Analysis
Mongoose's populate() method resolves references between MongoDB documents and accepts a match option to filter populated results. The $where operator in MongoDB executes a JavaScript expression on the server during query evaluation. When user input reaches a match object containing $where, an attacker can supply arbitrary JavaScript that runs inside the database engine.
The earlier fix for CVE-2024-53900 rejected match.$where and iterated arrays for item.$where. It did not recurse into nested objects or logical operators such as $and, $or, or sub-document filters. Crafted payloads place $where inside a deeper key, evading the guard and reaching MongoDB unfiltered. This is an incomplete fix issue, which is why CVE-2025-23061 was assigned as a distinct vulnerability.
Root Cause
The root cause is incomplete input validation in lib/helpers/populate/getModelsMapForPopulate.js. The previous logic only inspected the immediate match object and array entries. Attackers can hide $where inside nested filter clauses, bypassing the shallow check and injecting JavaScript that MongoDB executes server-side.
Attack Vector
Exploitation requires the application to pass attacker-influenced data into populate({ match: ... }). A request that nests $where under another operator triggers code evaluation when Mongoose builds the populate query. No authentication or user interaction is required when the vulnerable endpoint is reachable over the network.
// Security patch in lib/helpers/populate/getModelsMapForPopulate.js
// Replaces shallow checks with a recursive helper that rejects $where anywhere in the match tree.
if (hasMatchFunction) {
match = match.call(doc, doc);
}
- if (Array.isArray(match)) {
- for (const item of match) {
- if (item != null && item.$where) {
- throw new MongooseError('Cannot use $where filter with populate() match');
- }
- }
- } else if (match != null && match.$where != null) {
- throw new MongooseError('Cannot use $where filter with populate() match');
- }
+ throwOn$where(match);
data.match = match;
data.hasMatchFunction = hasMatchFunction;
data.isRefPath = isRefPath;
// Source: https://github.com/Automattic/mongoose/commit/64a9f9706f2428c49e0cfb8e223065acc645f7bc
Detection Methods for CVE-2025-23061
Indicators of Compromise
- HTTP request bodies or query parameters containing $where keys nested inside other filter operators.
- MongoDB server logs showing unexpected JavaScript evaluation or queries with $where expressions originating from application traffic.
- Anomalous database response times or CPU spikes on MongoDB nodes coinciding with populate() queries.
Detection Strategies
- Inventory Node.js projects and audit package-lock.json or yarn.lock for mongoose versions below 8.9.5.
- Perform static analysis on application code to locate calls to populate() where the match argument is derived from request input.
- Inspect web application firewall logs for JSON payloads containing the substring $where in nested positions.
Monitoring Recommendations
- Enable MongoDB profiler or audit logging to capture queries that include $where and alert on their frequency.
- Forward Node.js application logs and database query telemetry to a centralized analytics platform for correlation.
- Track outbound connections and file access from MongoDB hosts to detect post-exploitation activity.
How to Mitigate CVE-2025-23061
Immediate Actions Required
- Upgrade Mongoose to version 8.9.5 or later across all Node.js services.
- Reject any user-supplied filter objects containing $where at the application layer before invoking populate().
- Disable server-side JavaScript in MongoDB where business logic does not require it by setting security.javascriptEnabled: false.
Patch Information
The fix is delivered in Mongoose 8.9.5. The repaired code introduces a recursive throwOn$where helper, as shown in the GitHub commit details. Refer to the GitHub Mongoose Changelog and NPM Mongoose Package Versions for release details.
Workarounds
- Sanitize incoming JSON by recursively stripping keys that begin with $ before passing data to Mongoose query builders.
- Wrap populate() calls in a helper that accepts only an allow-list of filter fields and operators.
- Restrict MongoDB user privileges so that the application account cannot execute commands requiring server-side JavaScript.
# Update Mongoose to a patched version
npm install mongoose@^8.9.5
# Verify the installed version
npm ls mongoose
# Disable server-side JavaScript in mongod.conf as a defense-in-depth measure
# security:
# javascriptEnabled: false
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


