9.7 KiB
Hash Identifier
What this is
A small Python program that looks at a string of weird-looking characters and tells you what kind of cryptographic hash it probably is. You give it something like this:
5f4dcc3b5aa765d61d8327deb882cf99
and it tells you "that's an MD5 hash" with a reason for why it thinks so.
That's the whole job. It does not crack the hash. It does not turn the hash back into a password. It just answers the question "what flavor of hash is this?" — which is the question you have to answer first before any other tool will help you.
Why anyone needs this
The first thing that happens when a real attack succeeds is that the attacker walks out with a database dump full of password hashes. The hashes look like nonsense, but they are not random — every hash carries clues about how it was made. Once you know the algorithm (MD5, SHA-256, bcrypt, Argon2, whatever) you can hand the hash to a cracking tool like hashcat or John the Ripper and start trying to recover the original password.
But here's the thing: hashcat needs you to tell it the algorithm. It has over 400 hash modes, each with a different number. Mode 0 is MD5. Mode 100 is SHA-1. Mode 3200 is bcrypt. If you pick the wrong mode, hashcat will sit there forever and find nothing. So before cracking, you identify. That's this tool.
Real-world moments where you'd reach for this:
- A pentester finds a dump file on a compromised server full of strings like
$2b$12$EixZaYVK1...and needs to know what to feed hashcat. - A CTF challenge hands you a hash and zero hints about what algorithm made it.
- You're reading a breach writeup and want to understand whether the leaked passwords were stored as fast unsalted MD5 (a disaster) or slow salted bcrypt (much better).
- The 2012 LinkedIn breach leaked 6.5 million unsalted SHA-1 hashes. The first thing any researcher had to do before doing anything was confirm "yes, these are SHA-1." Forty-character hex strings. Easy. The tool you're about to read would have told them that in milliseconds.
What you will learn
Security ideas:
- What a cryptographic hash actually is (a function that turns any input into a fixed-length jumble that you can't reverse).
- The three signals every hash leaks about itself: its prefix, its length, and its character set.
- Why modern password hashes (
$2b$...,$argon2id$...) announce themselves on purpose, and why old fast hashes (MD5, SHA-1) don't. - The difference between a fast hash (made for speed, terrible for passwords) and a slow hash (made on purpose to resist cracking).
- Why you can never recover the password from a hash, only guess the password and check if its hash matches.
Python ideas (assuming this is your first time):
- How to read a Python file from top to bottom and understand what it's doing.
- What
importdoes and where the standard library ends and third-party packages begin. - Functions, type hints (
str,int,list[str]), and what-> boolmeans after a function signature. @dataclass— a shortcut for making little record-like objects.frozenset,dict,list,tuple— the core Python containers and when to pick which.- How a command-line tool actually starts running (the
if __name__ == "__main__"line at the bottom). - How a test file works and why every function in the main code has tests next to it.
Tools you'll touch:
uv— the modern Python package manager. Likepipbut ~100× faster.just— a command runner. Instead of memorizing long commands, you typejust testorjust run.rich— the library that prints the pretty colored table at the end.pytest— Python's test runner.ruff+mypy+pylint— the linters that yell at you if your code is wrong, slow, or sloppy.
What you need before starting
Knowledge you should have:
- You've used a terminal at least once (you know what
cdandlsdo). - You vaguely know that "a hash" is a one-way function. If not, 01-CONCEPTS.md will get you there in 10 minutes.
- You can read code, or at least you're willing to. We will explain every Python feature as we hit it.
Knowledge you do NOT need:
- Any prior Python experience. The whole point of the foundations tier is that you start here.
- Any prior cybersecurity experience.
- Any math beyond "counting." There is no math in this project. Cryptography uses math under the hood, but identifying a hash by its shape doesn't.
Software you need installed:
- Python 3.14 or newer.
- The
uvtool (the install script will get this for you if you don't have it). - The
justtool (also handled by the install script). - A terminal. Any terminal. On Mac it's Terminal.app or iTerm2; on Linux it's whatever your distro shipped; on Windows it's WSL2 + Ubuntu (we strongly recommend WSL2 instead of native Windows).
You do not need an IDE — a text editor is fine. We recommend VS Code with the Python extension, but nano, vim, helix, or whatever you already use will work.
Quick start
From inside PROJECTS/foundations/hash-identifier/:
./install.sh
That script will install uv and just if missing, create a virtual environment (an isolated Python sandbox just for this project), install all the dependencies, and verify the tests pass. It prints what it's doing as it goes — read the output, don't just close the terminal.
Then try the tool:
just run -- 5f4dcc3b5aa765d61d8327deb882cf99
You should see a colored table identifying that string as MD5 (with NTLM, MD4, and RIPEMD-128 as less-likely alternatives — all four produce 32 hex characters, so length alone can't separate them).
Try a few more:
# bcrypt — modern password hash, announces itself with the $2b$ prefix
just run -- '$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQNQy.uK4Of2T7G'
# SHA-256 — 64 hex characters
just run -- e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
# A JWT (this is NOT a hash, but the tool will say so politely)
just run -- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U
# Total garbage — the tool will say "no idea" rather than guess
just run -- helloworld
Note on quoting: when a hash starts with $, you must wrap it in single quotes ('$2b$...'). Without quotes, your shell will try to expand $2 as a shell variable and chop the hash up. This is a shell thing, not a Python thing — every Unix shell does it.
Project layout
hash-identifier/
├── hash_identifier.py the whole tool — one file, ~680 lines
├── test_hash_identifier.py tests for every behavior the tool claims to have
├── install.sh one-shot setup script
├── justfile shortcuts for run / test / lint / format
├── pyproject.toml project config: dependencies, linter rules, etc.
├── README.md short pointer to this learn/ folder
├── learn/ you are here
│ ├── 00-OVERVIEW.md quick start (this file)
│ ├── 01-CONCEPTS.md what hashes are and how identification works
│ ├── 02-ARCHITECTURE.md how the code is structured, with diagrams
│ ├── 03-IMPLEMENTATION.md line-by-line walkthrough of the code
│ └── 04-CHALLENGES.md extension ideas if you want to go further
└── assets/ images, screenshots
One file of code is on purpose. The foundations tier is meant to be readable in one sitting. The intermediate and advanced tiers split into many files; foundations does not.
Where to go next
- 01-CONCEPTS.md — understand what a hash is, why identification is the first move, and how prefix/length/charset clues actually work. Read this even if you think you know it; the framing matters.
- 02-ARCHITECTURE.md — see the six-step pipeline the tool uses to make a decision, drawn out as a flow diagram.
- 03-IMPLEMENTATION.md — read
hash_identifier.pywith us, line by line. Every Python feature gets explained when it first appears. - 04-CHALLENGES.md — extensions you can try on your own once you've absorbed the rest.
Common problems
"command not found: just"
The install script should set this up, but if it didn't: curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash. Then close and reopen your terminal so it sees the new tool.
"command not found: uv"
Same idea: curl -LsSf https://astral.sh/uv/install.sh | sh, then reopen your terminal.
just run -- $2b$12$... chops the hash up
You forgot the single quotes around the hash. Re-run with just run -- '$2b$12$...'.
"ModuleNotFoundError: No module named 'rich'"
You ran python hash_identifier.py directly instead of just run. The just run recipe uses the virtual environment that has rich installed. Either use just run, or activate the venv first: source .venv/bin/activate, then python hash_identifier.py <hash>.
Tests fail right after install
Tests should pass on a fresh ./install.sh. If they don't, you probably have an older Python (run python --version; you need 3.14+). On Ubuntu, install it via deadsnakes; on Mac use Homebrew.