Blame Game picoCTF 2024 Solution

Published: April 3, 2024

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.

bash
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.

Walk me through it
Unlike the Time Machine challenge, the answer isn't visible from a plain git log over the entire repo. You need to focus on message.py to pinpoint the culprit.
  1. Step 1
    List repository history
    Observation
    I noticed the challenge description said someone's commits were preventing the program from working, which suggested starting with an overview of the full commit history to spot any suspicious or anomalous entries.
    Run git log --oneline --decorate to scan the commit subjects. Look for messages that don't match real engineering work (vague performance claims, generic refactors, anything dropped in by 'the intern').
    bash
    git log --oneline --decorate

    Sample output (truncated):

    a3f1d20 (HEAD -> main) update README
    2b7c84e fix typo in greeting
    9e44c11 optimize file size of prod code
    4d11baf refactor message printer
    12c0abb initial commit

    The "optimize file size of prod code" subject is the suspicious one. Real engineering changes name what changed, not vague performance claims.

    What didn't work first

    Tried: Scan git log without any flags and read every full commit message looking for the flag

    The unfiltered log prints every commit across all files with full message bodies, which produces dozens of entries. The planted commit's message is intentionally generic ('optimize file size of prod code'), so it blends in. The --oneline flag collapses all of that to one line per commit, making the anomalous message stand out at a glance.

    Tried: Search commit messages with git log --grep='picoCTF' to find the flag directly in the log

    The flag is embedded in the author metadata field, not in the commit message body. git log --grep searches only the message subject and body, so it returns zero results here. You need to look at the Author line of the suspicious commit rather than its message text.

    Learn more

    git log is 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:

    • --oneline collapses each commit to a single line (hash + message).
    • --decorate shows 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 --oneline lets you spot unusual messages fast.

  2. Step 2
    Focus on the suspicious file
    Observation
    I noticed the solutionIntro specifically called out message.py as the file to focus on, and the suspicious commit message 'optimize file size of prod code' appeared in the full log without a clear file connection, which suggested narrowing the log to message.py to isolate who actually touched 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.
    bash
    git log message.py

    Expected output

    commit 9e44c11abc123def456789012345678901234567
    Author: picoCTF{@sk_th3_1nt3rn_ea3...} <intern@example.com>
    Date:   Mon Jan 15 14:32:07 2024 -0500
    
        optimize file size of prod code
    What 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 mixes commits touching README, config, tests, and other files, so the one suspicious commit touching message.py is diluted by noise. Appending the filename filters the output to only commits that modified that path, shrinking a long list to just the few commits you care about.

    Tried: Use git diff HEAD~1 HEAD - message.py to compare the last two commits on the file

    git diff compares two specific points in history, so you only see the change between those two commits. If the malicious commit is not the most recent one touching message.py, this command misses it entirely. git log message.py walks the full history for that file, guaranteeing you see every author who ever modified it.

    Learn more

    Passing a filename to git log filters 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.py immediately surfaces who touched it and when.

    You can go even further with git log -p message.py to see the actual diff for every change to that file inline.

  3. Step 3
    Blame the culprit
    Observation
    I noticed the Author field in the git log message.py output contained a string in picoCTF flag format, which suggested running git show on that commit hash to confirm the full author identity is the flag.
    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>.
    bash
    git show <COMMIT_HASH>

    Expected output

    commit 9e44c11abc123def456789012345678901234567
    Author: picoCTF{@sk_th3_1nt3rn_ea3...} <intern@example.com>
    Date:   Mon Jan 15 14:32:07 2024 -0500
    
        optimize file size of prod code
    
    diff --git a/message.py b/message.py
    index 1a2b3c4..5d6e7f8 100644
    --- a/message.py
    +++ b/message.py
    @@ -1,5 +1,5 @@
     def get_message():
    -    return "Correct answer!"
    +    return "Not quite!"
    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 annotates each line of the current file with the commit hash and author who last changed it, but it shows the author in abbreviated form without the full flag value. The flag is embedded in the Author metadata field of the commit itself, which you read from git show or git log. Reading only the diff lines shows what changed in the code, not who made the change.

    Tried: Copy the Committer name from git show output instead of the Author name

    git show prints both an Author and a Committer field. In workflows involving rebases or patch imports these can differ. The flag is placed in the Author field of the planted commit - the person who originally wrote the change - not the Committer field. Using the Committer value would give a different identity that is not the flag.

    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.py annotates 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.

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

Version control history is forensic evidence: every commit permanently records who changed what, when, and under what identity. Git log, git blame, and git show are the same tools used in real incident response to trace when a backdoor or vulnerable dependency was introduced and attribute it to a specific author. Attackers who gain write access to a repository can hide malicious changes behind innocuous commit messages, making file-scoped history review and author verification essential skills for supply-chain security audits.

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.

Related reading

Want more picoCTF 2024 writeups?

Useful tools for General Skills

Do these first

What to try next