PICOCTF 2026 • FORENSICS • GIT INTERNALS & REFLOG CARVING

Forensics Git 1: Tracing Deleted Commits & Git Object Carving

By Abdo•7 min read•PicoCTF 2026
TARGET IMAGEdisk.img (1 GB)
INITIAL COMMIT177789af... (Add flag)
FLAG BLOBInode 65695 (flag.txt)
FLAGpicoCTF{g17_r3...}
Git Internal Object Carving and Reflog Forensics
FIGURE 1: Forensic Lab Workbench & Git Internal DAG ReconstructionCommit → Tree → Blob • zlib Inflate
Forensic Scenario

Investigating a raw 1 GB ext4 disk image (disk.img). In this scenario, checking the latest commit message yields Remove flag — the author attempted to sanitize and delete sensitive evidence. However, Git is an immutable append-only object database. By reading Git reflogs (logs/HEAD) and following the internal object chain (Commit → Tree → Blob), we extract the deleted file object from .git/objects/ and inflate the zlib stream to recover the flag.

01. Spotting the Deletion & Inspecting the Reflog

After locating the repository at Sector offset 1140736, reading the latest commit message via COMMIT_EDITMSG (Inode 65693) shows:

POWERSHELL • CHECK COMMIT_EDITMSG
icat.exe -o 1140736 .\disk.img 65693
Remove flag

To discover previous commits where the flag was originally added, we read Git's reflog history via logs/HEAD (Inode 65704):

POWERSHELL • DUMP GIT REFLOG
icat.exe -o 1140736 .\disk.img 65704
// Git Reflog History:
000000... 177789af0b300e043ea8f54ea57d6cee352291ae commit (initial): Add flag
177789... 5fb8194539c770a830b8ba089a50778c07072b03 commit: Remove flag

02. Git Internal Architecture: Commit → Tree → Blob

In Git, every object is stored in .git/objects/xx/yyyy..., where xx is the first 2 hex characters of its 40-character SHA-1 hash. Objects are compressed using zlib.

1. Decompressing the Commit Object (Inode 65700, folder "17"):
python -c "import subprocess, zlib; print(zlib.decompress(subprocess.check_output(['icat.exe', '-o', '1140736', '.\disk.img', '65700'])))"
b'commit 179\x00tree a62340e078686778969b9a555fc722147cf14e5a\nauthor... Add flag\n'
2. Decompressing the Tree Object (Inode 65698, folder "a6"):
python -c "import subprocess, zlib; print(zlib.decompress(subprocess.check_output(['icat.exe', '-o', '1140736', '.\disk.img', '65698'])))"
b'tree 36\x00100644 flag.txt\x00\xf1P\xf4z]\xab\xfbC... (Hash: f150f47a5dabfb...)'
3. Decompressing the Flag Blob (Inode 65695, folder "f1"):
python -c "import subprocess, zlib; print(zlib.decompress(subprocess.check_output(['icat.exe', '-o', '1140736', '.\disk.img', '65695'])))"
b'blob 31\x00picoCTF{g17_r3m3mb3r5_d4ddf904}'

03. Alternative Extraction Methods (Zero Code)

Method A: CyberChef (GUI)

Dump the object to disk via icat ... 65695 > blob.bin, open CyberChef, and apply the Zlib Inflate recipe to view the flag without programming.

Method B: Git Native CLI

Git provides its own decompression tool. Running pretty-print auto-inflates any object:

git cat-file -p f150f47a5dabfb4397706aa18905df936595a86e
OFFICIAL CAPTURED FLAGpicoCTF{g17_r3m3mb3r5_d4ddf904}