Commitment Issues picoCTF 2024 Solution

Published: April 3, 2024

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.

bash
wget https://artifacts.picoctf.net/c_titan/77/challenge.zip && \
unzip challenge.zip && \
cd drop-in/
This is a successor to the Time Machine challenge. If you use ls -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".
  1. Step 1List commits
    git log walks the commit chain back from HEAD. The history shows a commit labeled "create flag" with hash 3d5ec8a26ee7b092a1760fea18f384c35e435139.
    bash
    git log
    commit 9b21a04c... (HEAD -> main)
    Author: ctf-author
    Date:   ...
        delete flag
    
    commit 3d5ec8a26ee7b092a1760fea18f384c35e435139
    Author: ctf-author
    Date:   ...
        create flag
    Learn 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.txt prints the file contents from any historical commit without changing what's on disk. git checkout is 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).

  2. Step 2Check out the flag commit
    Run 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.txt
    Learn 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 (or master).

    Beyond CTFs, this technique drives real debugging. If you know a bug was introduced somewhere in the last 50 commits, git bisect automates 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

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, or detect-secrets as a Git hook plus a CI check.
  • If a secret slips through, rotate the secret first, then rewrite history with git filter-repo and 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.

Want more picoCTF 2024 writeups?

Useful tools for General Skills

Related reading

What to try next