Description
No candidate list this time -- a large dictionary file is provided. Read each word, hash it with MD5, and compare it to the stored hash to find the password.
Setup
Download level5.py (the checker script) and dictionary.txt (the wordlist).
Solution
- Step 1Understand the scriptOpen level5.py. The stored MD5 hash is visible. The script needs to be extended to read each line from dictionary.txt, strip whitespace, hash it, and compare.
Learn more
This challenge is a full dictionary attack -- the most common real-world technique for cracking hashed passwords. Instead of a curated short list, you now have a wordlist file that must be read line by line and each entry tested against the target hash.
Wordlists (also called dictionaries) are files containing commonly used passwords, words, phrases, and variations. The most famous real-world wordlist is rockyou.txt, which contains over 14 million passwords leaked from the RockYou social gaming breach in 2009. Security professionals use it as a baseline for password auditing -- if a password appears in rockyou.txt, it is considered trivially crackable.
The key difference from pw-crack-4 is that you do not know the candidate list in advance -- you must read it from disk. This is the standard approach for any serious password cracking: maintain a large wordlist file and stream through it programmatically.
- Step 2Implement the dictionary attackStrip each line before hashing -- 'line.strip()' removes the trailing newline that would cause every hash to be wrong. The correct password is '9581'.python3 -c " import hashlib stored = '<hash_from_script>' with open('dictionary.txt') as f: for line in f: pw = line.strip() if hashlib.md5(pw.encode()).hexdigest() == stored: print('Password:', pw) break "
Learn more
The
.strip()call is critical and easy to forget. When you iterate over a file withfor line in f, eachlineincludes the trailing newline character (\n). Hashing"9581\n"produces a completely different MD5 than hashing"9581"-- so every single comparison would fail without stripping.Using
with open(...) as fis the correct Python idiom for file handling. Thewithstatement ensures the file is automatically closed when the block exits, even if an exception occurs. Thebreakstatement after finding the match stops the loop immediately instead of continuing through the rest of the file -- an important optimization for large wordlists.The time complexity of this attack is O(n) where n is the number of words in the dictionary. For a wordlist with 1 million entries, this is still very fast in Python. Professional cracking tools add GPU acceleration and multiple worker threads to push throughput orders of magnitude higher --
hashcaton a modern GPU can test tens of billions of MD5 hashes per second. - Step 3Run with the correct passwordOnce '9581' is identified, use it with the XOR decryption function to print the flag.python3 level5.py
Learn more
Completing this challenge demonstrates a complete understanding of the dictionary attack pipeline:
- Identify the target hash and the hashing algorithm used
- Select or obtain an appropriate wordlist
- Stream the wordlist, hash each candidate, compare to the target
- Use the recovered password to access the protected resource
The real-world defense against dictionary attacks is salting: prepending or appending a unique random value (the salt) to each password before hashing. The salt is stored alongside the hash. Even if two users have the same password, their hashes are different due to different salts. Salting also defeats precomputed hash tables (rainbow tables) because each salt requires its own precomputed table.
Modern password hashing algorithms like bcrypt and Argon2 automatically handle salting and are designed to be computationally expensive, making them far more resistant to dictionary attacks than raw MD5. If you ever design a system that stores user passwords, always use one of these purpose-built algorithms -- never raw MD5 or SHA.
Flag
picoCTF{...}
Always strip whitespace before hashing dictionary words -- the trailing newline from file.readline() changes the hash entirely.