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:
If
event == "Flag"
→ it short-circuits and shows "Blacklisted term detected".Otherwise, it calculates:
hash = hashlib.md5(event.encode()).hexdigest()
It uses the first 6 characters of that hash:
result = cur.execute("SELECT * FROM events WHERE id=?", (hash[0:6],))
The hash prefix is used to map to an ID in the database.
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 byhashlib.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 becausehashlib.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?