Description
From picoCTF:
Can you crack the password to get the flag? Download the password checker here and you'll need the encrypted flag and the hash in the same directory too. There are 7 potential passwords with 1 being correct. You can find these by examining the password checker script.

The Story Behind Solving the Challenge
When I first looked at this challenge, it appeared to be a simple password checker program. The challenge provided three files:
level3.py
level3.flag.txt.enc
level3.hash.binThe goal was clear: find the correct password so the program reveals the flag. However, the password itself was not given directly. Instead, the script contained clues about how the password is validated.
The first step in any CTF challenge like this is reading the source code carefully.
Understanding the Password Checker
Opening level3.py, the program prompts the user to enter a password:
user_pw = input("Please enter correct password for flag: ")The password is then passed to a hashing function:
def hash_pw(pw_str):
pw_bytes = bytearray()
pw_bytes.extend(pw_str.encode())
m = hashlib.md5()
m.update(pw_bytes)
return m.digest()This tells us something important:
The program does not store the password directly. Instead, it stores the MD5 hash of the password.
Next, the script reads a binary file:
correct_pw_hash = open('level3.hash.bin', 'rb').read()Then it compares the hash of the user's input with the stored hash:
if user_pw_hash == correct_pw_hash:If the hashes match, the program decrypts the encrypted flag using an XOR function and prints it.
So the logic becomes:
User Password
↓
MD5 Hash
↓
Compare With Stored Hash
↓
If Match → Decrypt FlagFinding the Possible Passwords
Scrolling further down the script reveals a very useful clue:
pos_pw_list = ["8799", "d3ab", "1ea2", "acaf", "2295", "a9de", "6f3d"]This means the correct password must be one of these seven values.
Instead of brute-forcing millions of passwords, we only need to check seven possibilities.
At this point, there are two ways to solve the challenge:
- Manual hash comparison
- Automated brute-force script
Method 1 — Manual Approach
First, we inspect the stored hash inside the binary file.
xxd level3.hash.binor
hexdump -C level3.hash.binThis shows the raw MD5 digest stored in the file.
Next, we generate the MD5 hash for each candidate password using md5sum.
Important: use -n to avoid adding a newline.
Example:
echo -n "8799" | md5sumWe repeat this for all possible passwords:
echo -n "8799" | md5sum
echo -n "d3ab" | md5sum
echo -n "1ea2" | md5sum
echo -n "acaf" | md5sum
echo -n "2295" | md5sum
echo -n "a9de" | md5sum
echo -n "6f3d" | md5sumBy comparing the generated hashes with the value inside level3.hash.bin, we can identify which password matches.
Once the correct password is found, we run the program:
python3 level3.pyThen enter the discovered password to reveal the flag.
Method 2 — Automated Approach
Instead of checking each password manually, we can automate the process using a short Python script.
The script simply:
- Reads the correct hash from the file
- Computes MD5 hashes for all candidate passwords
- Compares them
import hashlib
with open("level3.hash.bin", "rb") as f:
correct_hash = f.read()
passwords = ["8799", "d3ab", "1ea2", "acaf", "2295", "a9de", "6f3d"]
for pw in passwords:
test_hash = hashlib.md5(pw.encode()).digest()
if test_hash == correct_hash:
print("Password found:", pw)Running the script will immediately reveal the correct password.
Once we have the password, we simply run:
python3 level3.pyand provide the discovered password to decrypt the flag.
What This Challenge Teaches
This challenge highlights several important concepts:
- Reading and understanding source code
- Identifying how password verification works
- Understanding MD5 hashing
- Performing dictionary-based brute force attacks
- Automating repetitive tasks using Python
Even though the challenge is simple, it reflects real-world scenarios where applications store password hashes rather than plain text passwords.
Conclusion
By analyzing the Python script, we discovered that the password validation relies on comparing MD5 hashes. Since the script conveniently provides a list of possible passwords, the task becomes a straightforward hash comparison problem.
Whether solved manually using command-line tools or automatically using a short script, the key idea is the same: find the password whose MD5 hash matches the stored hash.
Once the correct password is entered, the program decrypts the XOR-encrypted flag and completes the challenge.