Description
This time there are 100 candidate passwords. One of them hashes to the stored MD5 hash. Add a loop to test all 100.
Setup
Download level4.py -- it contains a list of 100 candidate passwords and a stored MD5 hash.
Solution
- Step 1Read the script structureOpen level4.py. There's a list of 100 candidate passwords and a target hash. The script checks one password at a time -- modify it to loop through all of them.
Learn more
Scaling from 7 to 100 candidates makes manual testing impractical -- at 100 entries, you might still manage it manually, but the pattern clearly demands automation. This is a deliberate pedagogical progression: each pw-crack level increases the candidate pool until manual approaches are clearly infeasible, pushing you toward writing code.
Understanding the script's structure before modifying it is essential. Locate:
- Where the candidate list is defined
- Where the stored hash is defined
- Where the password is compared to the hash
- Where the decryption function is called with the correct password
This structural reading skill -- understanding how code flows before changing it -- is called code comprehension and is fundamental to both software development and security analysis. Never modify code you do not understand.
- Step 2Add a brute-force loopWrite a loop that hashes each candidate with MD5 and compares to the stored hash. When a match is found, pass it to the decryption function to get the flag. The correct password is '607a'.python3 -c "import hashlib; candidates=['ee42','3a5f']; h='stored_hash'; [print(p) for p in candidates if hashlib.md5(p.encode()).hexdigest()==h]"
Learn more
This step introduces the concept of automating repetitive computation. The loop does exactly what you would do manually -- hash a candidate, compare it, move on -- but at computer speed. A Python loop can test 100 MD5 hashes in milliseconds.
The list comprehension form
[print(p) for p in candidates if hashlib.md5(p.encode()).hexdigest() == h]is a compact Python idiom that combines filtering and action in one line. It is functionally equivalent to aforloop with anifcheck inside -- choose whichever form you find more readable.In real-world password auditing, this same loop logic is the core of tools like John the Ripper and hashcat. The difference is that those tools add GPU acceleration, rule-based mutations (appending numbers, capitalizing letters), and support for hundreds of hash algorithms. The fundamental algorithm -- hash candidate, compare to target -- is unchanged.
- Step 3Run the modified scriptWith the correct password found, the XOR decryption function unlocks and prints the flag.python3 level4.py
Learn more
The XOR decryption used in these pw-crack challenges is a simple symmetric cipher: the flag bytes are XORed with a key derived from the password. XOR has the property that applying it twice with the same key returns the original value --
(A XOR K) XOR K = A-- making encryption and decryption the same operation.While XOR is not secure on its own for real encryption (it is trivially breakable with known-plaintext attacks), it appears frequently in CTF challenges and in obfuscated malware as a lightweight way to obscure data. Recognizing XOR-based encoding and knowing how to reverse it is a valuable skill for reverse engineering challenges.
The progression from pw-crack-1 through pw-crack-5 mirrors the real evolution of password security practices: plaintext storage, light obfuscation, small hash lookups, larger hash lookups, and finally full dictionary attacks. Each level requires a slightly more sophisticated approach.
Flag
picoCTF{...}
Testing 100 MD5 hashes takes milliseconds -- the same loop logic scales to millions of entries for real dictionary attacks.