Description
Someone's commits seems to be preventing the program from working. Who is it?
Download the challenge zip, unzip it locally, then change into the drop-in directory.
wget https://artifacts.picoctf.net/c_titan/74/challenge.zip && \
unzip challenge.zip && \
cd drop-in/Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
git log over the entire repo. You need to focus on message.py to pinpoint the culprit.Step 1List repository history
ObservationThe description says someone's commits broke the program. Start with an overview of the full history to see what you are dealing with before narrowing anything down.Run git log --oneline --decorate to scan the commit subjects. The repo holds just over 500 commits and almost every one of them carries the same subject, 'important business work', so the log by itself does not single anything out.bashgit log --oneline --decorateSample output (truncated):
faa685a (HEAD -> master) important business work 8670d17 important business work 525c5a7 important business work da9eb7e important business work 4d5ff80 important business work ... (500-plus more, nearly all identical)
Only three distinct subjects exist in the whole history: "create top secret project", the wall of "important business work" padding, and a single "optimize file size of prod code". Finding that one by eye among hundreds of identical lines is the wrong plan, so scope the log to the file that actually broke instead.
What didn't work first
Tried: Scan git log without any flags and read every full commit message looking for the flag
An unfiltered log prints all 500-plus commits with full message bodies, and the padding commits all share the subject 'important business work', so nothing stands out no matter how far you scroll. The path filter in the next step is what cuts the list down.
Tried: Search commit messages with git log --grep='picoCTF' to find the flag directly in the log
The flag sits in the author metadata, not the message body, and --grep only searches the subject and body. It returns nothing. Read the Author line of the suspicious commit instead.
Learn more
git logis your window into a repository's entire history. Every commit records a snapshot of the project along with metadata: who made the change (author name + email), when, and a message describing why.The flags here do specific things:
--onelinecollapses each commit to a single line (hash + message).--decorateshows branch and tag labels so you can see where HEAD points.
In CTFs, repos are often seeded with a long history to hide a single suspicious commit. Scanning with
--onelinelets you spot unusual messages fast.Step 2Focus on the suspicious file
Observationmessage.py is the file that matters, and the padding commits never touch it. Narrowing the log to that path collapses 500-plus commits down to the two that actually changed it.Run git log message.py to narrow the search to the file that broke the flag printer. You're interested in the commit introducing the bogus optimization.bashgit log message.pyExpected output
commit 0fe87f16cbd8129ed5f7cf2f6a06af6688665728 Author: picoCTF{@sk_th3_1nt3rn_ea3...} <ops@picoctf.com> Date: Sat Mar 9 21:09:25 2024 +0000 optimize file size of prod code commit 7e8a2415b6cca7d0d0002ff0293dd384b5cc900d Author: picoCTF <ops@picoctf.com> Date: Sat Mar 9 21:09:25 2024 +0000 create top secret projectWhat didn't work first
Tried: Run git log on the whole repo without specifying a file, then manually read through all commits
The unscoped log is 500-plus padding commits that never touch message.py, burying the one that does. Appending the filename filters to commits that modified that path and cuts the list to exactly two.
Tried: Use git diff HEAD~1 HEAD - message.py to compare the last two commits on the file
git diff compares two specific points, so you see only what changed between them. If the malicious commit is not the most recent one on message.py, you miss it. git log on the file walks its whole history and shows every author who touched it.
Learn more
Passing a filename to
git logfilters the output to only commits that touched that specific file. This is one of git's most useful forensic tools - instead of sifting through every commit in the repo, you see only the history relevant to one path.This technique is called file-level blame tracing and is commonly used in real incident response: if a bug was introduced into a specific module,
git log path/to/file.pyimmediately surfaces who touched it and when.You can go even further with
git log -p message.pyto see the actual diff for every change to that file inline.Step 3Blame the culprit
ObservationThe Author field in that log looks like a picoCTF flag. Run git show on the commit hash to read the full author identity.Once you've identified the offending commit, take note of the author line; that name is the flag. You can double-check by running git show <COMMIT_HASH>, which also shows what the so-called optimization did: it deleted the closing parenthesis from message.py, which is why the program stopped running.bashgit show <COMMIT_HASH>Expected output
commit 0fe87f16cbd8129ed5f7cf2f6a06af6688665728 Author: picoCTF{@sk_th3_1nt3rn_ea3...} <ops@picoctf.com> Date: Sat Mar 9 21:09:25 2024 +0000 optimize file size of prod code diff --git a/message.py b/message.py index 7df869a..326544a 100644 --- a/message.py +++ b/message.py @@ -1 +1 @@ -print("Hello, World!") +print("Hello, World!"What didn't work first
Tried: Run git blame message.py and look at the flag in the diff output rather than the Author line
git blame labels each line with the commit and author that last changed it, but abbreviates the author and drops the full flag value. The flag lives in the commit's Author metadata, which git show or git log prints in full. The diff tells you what changed, not who changed it.
Tried: Copy the Committer name from git show output instead of the Author name
git show prints both an Author and a Committer, and rebases or patch imports make them differ. The flag is in the Author field, the person who originally wrote the change. The Committer value is a different identity entirely.
Learn more
git show <hash>prints everything about a single commit: the author, date, full message, and a unified diff showing exactly what lines were added or removed. It's the most direct way to inspect what a specific commit actually did.The author field records who originally wrote the change. This is distinct from the committer; in workflows involving rebases or patches, these can be different people. In this challenge the author is who you're after.
git blame message.pyannotates every line with the commit hash and author that last changed it. Combined with grep, it gives you an alternative path to the same answer:git blame message.py | grep <suspicious_line>prints the author of just the line you care about.
Interactive tools
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
- Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
Flag
Reveal flag
picoCTF{@sk_th3_1nt3rn_ea3...}
The flag is the author of the suspicious commit (the "optimize file size of prod code" change in message.py). It appears in the git log message.py output once you examine the file history.
Key takeaway
Frequently asked questions
Common questions about the Blame Game solution and the techniques it uses.
Why doesn't plain git log reveal the flag?
git log over the whole repo shows every commit across every file, so the malicious change is buried in noise. The challenge only makes sense when you scope the log to message.py, the file whose optimization comment introduced the bug. Filtering by path is what separates this challenge from Time Machine, which was solvable from the unfiltered log.
What is the difference between git log <file>, git show, and git blame here?
git log message.py lists every commit that touched the file so you can spot the suspicious one. git show <hash> prints that one commit's author, date, message, and diff so you can verify it. git blame message.py annotates each line with the commit and author that last touched it, which is useful if you care about a single line rather than the file's full history. In this challenge the author of the suspicious commit is the flag.
Why is the commit author the flag and not the committer?
In git the author is whoever originally wrote the change, while the committer is whoever applied it (they can differ after rebases or patch imports). picoCTF sets the author to the flag value on the planted commit, so reading the Author line of the malicious commit gives you picoCTF{@sk_th3_1nt3rn_ea3...} directly.