← Back to Writeups
CUSTOMMediumMisc

Phantom

XESXOR9/2/20263 min read
#misc#custom#medium#base64_blob_decoding#github_api_commit_enumeration#github_phantom_commit_recovery#git_tree_traversal#linux

Phantom

Platform: tamuctf | Category: Misc | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-03-21 | Status: Solved Techniques: base64_blob_decoding, github_api_commit_enumeration, github_phantom_commit_recovery, git_tree_traversal

Summary

Task: GitHub repo with single empty commit, flag hidden in phantom/orphaned commit. Solution: Used GitHub API /comments endpoint to discover unreachable commit SHA, then traversed git tree via Git Data API to extract flag from blob.

Phantom — TAMU CTF 2026

Description

The flag is somewhere around here or something idk

A GitHub repository link was provided: https://github.com/tamuctf/phantom

The repository appeared nearly empty with just a single commit on main branch containing a bare README.md with only # phantom.

Analysis

Initial Reconnaissance

Cloning the repository revealed minimal content:

  • Single commit ("Initial commit") on main branch
  • README.md containing only # phantom
  • No other branches, tags, notes, stash, or dangling objects locally ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Standard git forensics techniques (reflog, fsck, stash) yielded nothing — the interesting data wasn't in the clone.

GitHub API Exploration

Since local inspection was fruitless, the GitHub REST API was investigated:

  1. Issues/PRs (/repos/tamuctf/phantom/issues?state=all) — Found 3 issues/PRs, all decoys from other CTF participants
  2. Refs (/repos/tamuctf/phantom/git/refs) — Only refs/heads/main and a PR ref
  3. Events (/repos/tamuctf/phantom/events) — Found CommitCommentEvent entries referencing commit SHAs that didn't exist on any branch ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Discovery: Commit Comments Endpoint

The breakthrough came from the commit comments endpoint:

GET /repos/tamuctf/phantom/comments

This revealed comments on commits with SHAs not reachable from any branch. These are phantom commits — commits that exist in GitHub's object store but have no branch/tag reference pointing to them.

Critical commit found:

  • SHA: b365313472870cbf887a42a7be75df741b60c8d3
  • Author: Noah Mustoe (cobradev4, the repo owner)
  • Message: "Add flag"
  • Date: 2026-03-16 ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Step 1: Identify Phantom Commits via Comments API

bash
curl -s "https://api.github.com/repos/tamuctf/phantom/comments" | jq '.[] | {commit_id, user: .user.login, body}'

Found reference to commit b365313472870cbf887a42a7be75df741b60c8d3.

Step 2: Fetch Phantom Commit Details

bash
curl -s "https://api.github.com/repos/tamuctf/phantom/git/commits/b365313472870cbf887a42a7be75df741b60c8d3"

Response included:

  • message: "Add flag"
  • tree.sha: f2d3d0cec7a11ef4df615ebbdbd270e03673180a ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 3: Traverse the Tree

bash
curl -s "https://api.github.com/repos/tamuctf/phantom/git/trees/f2d3d0cec7a11ef4df615ebbdbd270e03673180a"

Found README.md with blob SHA: 86f49cf229723174e9bc1ac822fb33617c9ada1f

Step 4: Extract the Flag from Blob

bash
curl -s "https://api.github.com/repos/tamuctf/phantom/git/blobs/86f49cf229723174e9bc1ac822fb33617c9ada1f" | jq -r '.content' | base64 -d

Output:

# phantom
gigem{REDACTED}

Complete Solve Script

python
#!/usr/bin/env python3
import requests
import base64
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

REPO = "tamuctf/phantom"
API = f"https://api.github.com/repos/{REPO}"

# Step 1: Find phantom commits via comments
comments = requests.get(f"{API}/comments").json()
for c in comments:
    print(f"Comment on commit {c['commit_id']} by {c['user']['login']}")

# Step 2: Fetch the phantom commit with "Add flag" message
phantom_sha = "b365313472870cbf887a42a7be75df741b60c8d3"
commit = requests.get(f"{API}/git/commits/{phantom_sha}").json()
print(f"Commit message: {commit['message']}")
tree_sha = commit['tree']['sha']
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Step 3: Get tree contents
tree = requests.get(f"{API}/git/trees/{tree_sha}").json()
readme_sha = next(f['sha'] for f in tree['tree'] if f['path'] == 'README.md')

# Step 4: Decode blob
blob = requests.get(f"{API}/git/blobs/{readme_sha}").json()
content = base64.b64decode(blob['content']).decode()
print(content)

Notes

Why phantom commits exist: When commits are pushed to GitHub and then dereferenced (branch deleted, force-pushed over), GitHub retains the commit objects in its database. They're not garbage-collected immediately and remain accessible via the Git Data API if you know the SHA. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

How to find them:

  1. /repos/{owner}/{repo}/comments — Comments may reference orphaned commits
  2. /repos/{owner}/{repo}/events — Push/commit events may log old SHAs
  3. /repos/{owner}/{repo}/git/commits/{sha} — Direct access if SHA is known ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍