Events (Web)

from flask import Flask, render_template, request, redirect
import sqlite3
import hashlib

app = Flask(__name__)


@app.route("/")
def mainHandler():
    return render_template("index.html")

@app.route("/review")
def reviewHandler():
    con = sqlite3.connect("events.db")
    cur = con.cursor()
    event = request.args.get("event")
    if event == "Flag":
        return ("Blacklisted term detected")
    hash = hashlib.md5(event.encode()).hexdigest()
    result = cur.execute("SELECT * FROM events WHERE id=?", (hash[0:6],))
    try:
        result = result.fetchone()
        name = result[1]
    except:
        return (redirect("/"))
    return render_template("review.html",placeholder=name,desc=result[2],img=result[3])


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

Step 1: Understanding the Code

The /review endpoint takes a GET parameter called event. For example:

/review?event=Cloud

Here’s what happens behind the scenes:

  1. If event == "Flag" → it short-circuits and shows "Blacklisted term detected".

  2. Otherwise, it calculates:

hash = hashlib.md5(event.encode()).hexdigest()
  1. It uses the first 6 characters of that hash:

result = cur.execute("SELECT * FROM events WHERE id=?", (hash[0:6],))
  1. The hash prefix is used to map to an ID in the database.

  2. If found, it renders the review template with data from the DB.

Step 2: Understanding the vulnerability

  • The code is using MD5(event) and uses only 6 hex characters (24 bits).

  • MD5 is fast and known to be vulnerable to collisions.

  • Since the input "Flag" is blacklisted, you can't directly trigger that path — but you can try to find another string whose MD5 hash starts with the same 6 hex digits as MD5("Flag").

This is entirely possible — there are only 16⁶ = ~16 million possible 6-digit hex combinations. With brute force, you’ll likely find a collision in seconds.

Step3: Collision Brute Force Script

import hashlib

target = hashlib.md5(b"Flag").hexdigest()[:6]

i = 0
while True:
    attempt = f"test{i}".encode()
    h = hashlib.md5(attempt).hexdigest()
    if h[:6] == target:
        print(f"Input: {attempt.decode()}")
        break
    i += 1
  • b"Flag": This is the byte-encoded version of the string "Flag", required by hashlib.md5().

  • hashlib.md5(...).hexdigest() computes the full 32-character hex digest of the MD5 hash.

  • [:6]: We're slicing out the first 6 characters of that hex string. This is your collision target.

  • So now target is the 6-char hash prefix you want to collide with.

  • Inside an infinite loop, this line builds a test string like "test1234".

  • .encode() turns it into bytes because hashlib.md5() needs bytes, not strings.

  • Then computes the full MD5 hash of the string and turns it into a hex string.

  • If the first 6 characters of the current test hash match the target, you’ve got a collision!

  • The program prints out the matching input and breaks out of the loop

Step4: Exploiting the Collision

Once you find a string test41093 that has the same MD5 prefix as "Flag", you can simply go to:

/review?event=test7934

This bypasses the blacklist (since it's not "Flag") but still hits the same DB record due to the hash prefix match.

Last updated

Was this helpful?