Análisis#
La pagina lo único que tiene es un login, si tratamos de ingresar por ejemplo probando admin:admin no nos funcionara nada.
Revisando el código nos damos cuenta de que esta usando una base de datos MongoDB:
from flask import Flask, Blueprint, render_template, redirect, jsonify, request
from flask_bcrypt import Bcrypt
from pymongo import MongoClient
app = Flask(__name__)
app.config.from_object("application.config.Config")
bcrypt = Bcrypt(app)
client = MongoClient(app.config["MONGO_URI"])
db = client[app.config["DB_NAME"]]
users_collection = db["users"]
Además tenemos un archivo llamado migration.py en el cual podemos ver que los correos y las contraseñas se generan aleatoriamente por lo que no pueden ser credenciales debiles.
@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("/")
Este código al usar la entrada del usuario directamente es vulnerable a hacer una inyección NoSQL, un ejemplo del código seguro sería así:
from flask import request, render_template, redirect
from werkzeug.security import check_password_hash
import re
def is_valid_email(email):
return re.match(r"[^@]+@[^@]+\.[^@]+", email) is not None
def login():
email = request.form.get('email')
password = request.form.get('password')
if not isinstance(email, str) or not is_valid_email(email):
return redirect("/")
if not isinstance(password, str) or len(password) == 0:
return redirect("/")
user = users_collection.find_one({"email": email})
if user and check_password_hash(user['password_hash'], password):
return render_template("candy.html", flag=open("flag.txt").read())
else:
return redirect("/")
Exploit#
En este punto cometí un error en su momento no me fije y no me di cuenta que admitía el formato json por lo que lo estaba haciendo con el url-encode por lo que las payloads que estaba pasando no estaban funcionando al final termine cambiando el formato y usando las payloads para json quedando de la siguiente forma:


