Skip to main content
Hash challenges in CTFs come in two distinct flavours. The first is pure cracking: you’re given a hash digest and you need to find the original input, usually a password or short string. The second is exploitation: the application uses a hash as a security primitive — a MAC, a signature, or an authentication token — and there’s a structural flaw in how it was constructed. Both categories appear constantly, and your approach to each is completely different. This walkthrough covers the full spectrum, from firing up hashcat to crafting a length extension payload against a server-side MAC.

Step 1: Identify the Hash Algorithm

The first thing you do with any unknown hash is figure out what algorithm produced it. Hash length and character set narrow it down quickly, and hashid confirms it.
1

Check the Hash Length and Format

Most common hash algorithms produce output of a fixed, recognizable length:If the hash starts with $2b$, $2a$, or $2y$, it’s bcrypt. If it’s 32 hex characters, it could be MD5 or NTLM — context usually disambiguates.
2

Run hashid

hashid analyses the hash format and returns a ranked list of likely algorithms. Give it the hash directly or point it at a file.
The -m flag prints the hashcat mode alongside each algorithm name, which saves you from looking it up separately.
3

Try Online Lookups First

Before spending GPU time cracking, check crackstation.net and hashes.com. These sites maintain rainbow tables of billions of pre-computed hashes. Many CTF flags and challenge passwords appear immediately.

Step 2: Crack with hashcat

hashcat is the fastest hash cracking tool available and handles virtually every algorithm you’ll encounter in a CTF. It runs on the GPU by default, which makes it orders of magnitude faster than CPU-based tools for most algorithms.

hashcat Mode Reference

Run hashcat --help | grep -i sha to search for a specific algorithm by name.
Use the --show flag after a session completes to print all cracked hashes without re-running the attack: hashcat -m 0 hash.txt --show. Results are saved to hashcat.potfile automatically.

Step 3: Crack with John the Ripper

John the Ripper (john) is more flexible than hashcat in some scenarios — it auto-detects many hash formats, handles complex file formats (ZIP, PDF, SSH keys) out of the box, and works well on CPU when a GPU isn’t available.

Step 4: Hash Length Extension Attack

This is the most technically interesting hash attack you’ll encounter in CTFs. Many applications compute a MAC like this:
Where H is a Merkle–Damgård hash function (MD5, SHA-1, or SHA-256), secret is a server-side key, and message is attacker-controlled. The flaw: you can append extra data to message and compute a valid MAC for the extended message — without knowing secret.

Why It Works

Merkle–Damgård hashes process input in blocks. After all blocks are processed, the internal state is the hash output. If you resume hashing from that state, you continue as if you were still hashing the same stream — including the secret you never saw.
1

Confirm the Vulnerability

Look for a server endpoint that accepts a (message, mac) pair and validates it as mac == H(secret || message). The hash algorithm must be MD5, SHA-1, or SHA-256 (all Merkle–Damgård constructions). SHA-3 and HMAC are not vulnerable.
2

Automate with hash_extender

hash_extender handles all the padding calculation and state manipulation for you. You need: the original message, the known valid MAC, the data you want to append, and (if known) the secret length.
The tool outputs the new MAC and the new (URL-encoded) message to submit to the server.
3

Implement It in Python (Manual Approach)

If hash_extender isn’t available, you can implement length extension manually using a custom hash state loader.
HMAC (H(key || H(key || message))) is specifically designed to prevent length extension. If the server uses hmac.new(secret, message, hashlib.md5).hexdigest(), this attack does not work. Length extension only applies to the naive H(secret || message) construction.

Step 5: Hash Comparison Bypasses

PHP and other loosely typed languages introduce their own class of hash vulnerabilities that have nothing to do with cracking.
In PHP, the == operator performs type coercion. If a hash string looks like a float in scientific notation (0e...), PHP converts it to 0 before comparing. Two different inputs that produce hashes starting with 0e followed only by digits will compare as equal.
MD5 and SHA-1 are both collision-broken. You can find two different inputs that produce the same hash. In CTF challenges, this sometimes means you upload two different files with the same hash to bypass an integrity check.
For SHA-1, use the SHAttered collision PDFs or sha1collisiondetection. Generating novel SHA-1 collisions requires significant compute, so CTF challenges that use SHA-1 collisions typically provide the collision pair and ask you to exploit the application.
Some older implementations pass hashes through C-string functions that stop at null bytes (\x00). If you can include a null byte in your input, everything after it may be ignored.
This is rare in modern challenges but appears in binary exploitation scenarios where a hash comparison lives inside native code.

Rainbow Table Lookups

For unsalted MD5 and SHA-1 hashes of common strings, rainbow tables give you instant results. Use these resources before running hashcat:
Salted hashes completely defeat rainbow tables. If the hash was computed as H(salt || password) with a unique per-user salt, you must crack it directly with hashcat or John using the salt as part of the hash format. Always check whether the challenge provides a salt alongside the hash.

Quick Reference

Last modified on July 23, 2026