Lion Cipher Lab
Rebuild Git objects by hand, and see why a deleted secret stays in history
- Foundations
- Intermediate
- ~75 min
- Free · no account required
Why it matters
Almost every security tool you will ever install comes from a Git repository, and almost every leaked API key you will ever read about was committed to one. Most people use Git as a black box: add, commit, push. That is fine until someone commits a password, deletes it in the next commit and says 'fixed'. It is not fixed, and this lab shows you why at the level of bytes. First you open the box: you compute Git's object ids yourself in Python, decompress the files in .git/objects, and build a commit without ever running git commit. Once you have seen that Git is a store of compressed,…
What you'll learn
- computing a Git object id by hand: sha1 of 'blob 19', a NUL byte and the content gave 5c325a65... in Python, the exact id git hash-object printed
- zlib-decompressing the loose files in .git/objects and reading blob, tree and commit objects raw, including the binary tree format (mode, name, NUL, 20 raw id bytes)
- proving an object id IS the hash of its bytes: rebuilding a tree in Python and re-hashing a raw commit both matched, and changing one letter gave a different id
- building a commit with plumbing only (hash-object -w, mktree, commit-tree, update-ref) and watching git log and git fsck --strict accept it
- explaining SHA-1 vs the SHA-256 object format with a measured 40- vs 64-character id from git init --object-format=sha256, and why the two do not interoperate today
- measuring where a deleted secret still lives after git rm, commit --amend, git gc, reflog expire and gc --prune=now (only the last one emptied the object store; a plain gc moved it into a cruft pack)
- rewriting pushed history with git filter-repo --sensitive-data-removal, force-pushing, and proving a colleague's clone still printed the token afterwards
- writing a 25-line pre-commit secret scan and measuring its misses: base64 slipped through, --no-verify skipped it, and a fresh clone had no hook at all
Steps in this lab
- What you are building, and what Git really stores
- Set up: Git, Python, git-filter-repo, and a short lab folder
- Compute a blob id by hand and match git hash-object
- Read blob, tree and commit objects raw with zlib
- Build a commit with plumbing only, and let git log read it
- SHA-1 vs the SHA-256 object format
- Commit a fake secret, delete it, and find it again
- Amend before pushing, then measure what gc, reflog expire and --prune=now each remove
- Honest negative: once it is pushed, no local cleanup reaches the copies
- Prevent it: a pre-commit scan, and what it misses
- Do it better: the order a professional follows, then clean up