Skip to main content

Blame Game picoCTF 2024 Solution

Use Git history and authorship tools to trace back a breaking change and find the flag.

Published: April 3, 2024Updated: August 25, 2026

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 1List repository history
    Observation
    The 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.
    bash
    git log --oneline --decorate

    Sample 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 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 2Focus on the suspicious file
    Observation
    message.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.
    bash
    git log message.py

    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
    
    commit 7e8a2415b6cca7d0d0002ff0293dd384b5cc900d
    Author: picoCTF <ops@picoctf.com>
    Date:   Sat Mar 9 21:09:25 2024 +0000
    
        create top secret project
    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 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 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 3Blame the culprit
    Observation
    The 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.
    bash
    git 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.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.

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

Version control history is forensic evidence: every commit records who changed what, when, and under which identity. log, blame, and show are the same tools incident responders use to date a backdoor or a vulnerable dependency and attribute it. An attacker with write access hides changes behind harmless-looking messages, which is why file-scoped history review and author verification matter in supply-chain 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

Useful tools for General Skills

Where to go next