Description
I accidentally wrote the flag down. Good thing I deleted it!
Download the challenge zip, unzip it locally, then change into the drop-in directory.
wget https://artifacts.picoctf.net/c_titan/77/challenge.zip && \
unzip challenge.zip && \
cd drop-in/Solution
Walk me through itls -a in drop-in directory you can see the .git file which allows you to see the commits. Before looking at prior commits in the log there is a file called "message.txt" with the file contents of "TOP SECRET".- Step 1List commitsgit log walks the commit chain back from HEAD. The history shows a commit labeled "create flag" with hash 3d5ec8a26ee7b092a1760fea18f384c35e435139.bash
git logcommit 9b21a04c... (HEAD -> main) Author: ctf-author Date: ... delete flag commit 3d5ec8a26ee7b092a1760fea18f384c35e435139 Author: ctf-author Date: ... create flagLearn more
This challenge hinges on a fundamental misunderstanding many people have: deleting a file in git does not erase it from history. Git is a content-addressable store. Every version of every file ever committed is permanently recorded under
.git/objects/, even after a follow-up commit deletes the file.You don't even have to switch the working tree to read the deleted contents.
git show <hash>:message.txtprints the file contents from any historical commit without changing what's on disk.git checkoutis the heavier hammer, useful when you need multiple files restored together.This is why you should never commit secrets to a repository, even "temporarily." Removing a secret in a follow-up commit does not protect it. The only safe remediation is to rotate the secret and rewrite history with
git filter-repo(then force-push and coordinate with everyone who has a clone). - Step 2Check out the flag commitRun git checkout <hash> to change the working tree to that exact commit, then cat message.txt to see the flag. You'll be in detached HEAD; that's fine for reading.bash
git checkout 3d5ec8a26ee7b092a1760fea18f384c35e435139 && \ cat message.txtLearn more
git checkout <hash>moves your working tree to the exact state the repository was in at that commit. Files present at that point are restored, and files added later disappear. You end up in "detached HEAD" state, meaning HEAD points directly to a commit rather than a branch name.You can explore freely in this state. To get back to the latest commit on your branch, run
git checkout main(ormaster).Beyond CTFs, this technique drives real debugging. If you know a bug was introduced somewhere in the last 50 commits,
git bisectautomates a binary search through history by checking out midpoints until it isolates the culprit.
Related guides
Linux Command Line Basics for CTF
The git, cat, and ls commands used here are covered in the Linux CLI guide alongside the other shell tools that appear across General Skills challenges.
Flag
picoCTF{s@n1t1z3_30e86...}
How to prevent this
How to prevent this
Deleting a file in a follow-up commit does not remove it from history. The blob stays in .git/objects until pruned.
- Pre-commit secret scanning is the only intervention that prevents the leak. Install
gitleaks,trufflehog, ordetect-secretsas a Git hook plus a CI check. - If a secret slips through, rotate the secret first, then rewrite history with
git filter-repoand force-push. Old clones, mirrors, and forks still have the data; rotation is the only real fix. - Do not store credentials in repos at all. Use a secrets manager (Vercel env vars, AWS Secrets Manager, Doppler, 1Password CLI) and pull at runtime. Then a leak yields nothing useful.