Forensics Git 2 picoCTF 2026 Solution

Published: March 20, 2026

Description

The agents interrupted the perpetrator's disk deletion routine. Can you recover this git repo?

Download and decompress the disk image.

Mount the image and inspect the filesystem for a damaged git repository.

bash
gunzip disk.img.gz
bash
sudo mount -o loop disk.img /mnt/disk
  1. Step 1Decompress and quick strings check
    Extract the disk image. The deletion routine was interrupted, so git objects may still be present. Run strings first as a fast check.
    bash
    gunzip disk.img.gz
    bash
    strings disk.img | grep picoCTF
    Learn more

    When a deletion process is interrupted, the filesystem may be in an inconsistent state: some files are deleted (their directory entries removed) but their data blocks have not yet been overwritten. The raw bytes of the deleted content remain on disk until the operating system reuses those blocks for new data. strings | grep picoCTF scans the raw image and finds these bytes even though the filesystem no longer has a path to them.

    This is the principle behind file carving: recovering files by their content patterns rather than their filesystem metadata. Known file types have recognizable headers (magic bytes): for example, JPEG files start with FF D8 FF, ZIP files with 50 4B 03 04, and git objects are zlib-compressed with a characteristic header. Carving tools like Foremost and PhotoRec scan raw disk images looking for these magic bytes and extract complete files even from unallocated space.

  2. Step 2Detect partition layout and mount
    Use mmls to identify the partition offset, then mount the filesystem.
    bash
    mmls disk.img
    bash
    sudo mount -o loop,offset=$((512*<start_sector>)) disk.img /mnt/disk
    bash
    ls /mnt/disk
    Learn more

    Even when files are deleted, the partition table and filesystem superblock are usually intact because they are written early in the deletion process and are often the last to be cleared. mmls reads the partition table to locate the data partition, and Linux can still mount and read the filesystem structure even if individual file inodes have been cleared.

    Linux ext4 filesystems mark deleted inodes as free in the inode bitmap but do not immediately zero the inode's block pointers. This means the Sleuth Kit's ils (inode list) and ifind tools can still see deleted inodes and their data block addresses for a period after deletion: long enough to recover recently deleted files. The fls -r -d command from TSK lists deleted files and directories by scanning for inodes marked as unallocated.

  3. Step 3If .git is fully gone, recover with TSK first
    When the .git directory itself was unlinked, normal find/ls won't see it. Use The Sleuth Kit on the raw image (no mount needed) to enumerate deleted inodes and pull anything git-shaped back out before trying any git commands.
    bash
    fls -r -d disk.img | grep -iE 'git|HEAD|objects|refs'
    bash
    # For each interesting deleted inode:
    bash
    icat disk.img <inode> > /tmp/recovered/<name>
    bash
    file /tmp/recovered/<name>   # 'zlib compressed data' = loose git object
    bash
    # Decompress a zlib loose object to see its raw blob/commit/tree:
    python
    python3 -c 'import sys, zlib; sys.stdout.buffer.write(zlib.decompress(open(sys.argv[1],"rb").read()))' /tmp/recovered/<name>
    Learn more

    If the entire .git directory was unlinked before the deletion was interrupted, mounting the filesystem normally won't reveal it - the directory entries are gone. TSK bypasses the live filesystem and reads metadata structures directly from the image, so it sees deleted inodes that the kernel has marked as unallocated. This makes TSK the right starting point in this scenario, not the fallback.

    icat output for a recovered git object can be one of two things. Files that lived under .git/objects/XX/YY... are zlib-compressed loose objects, so file will report "zlib compressed data"; pipe them through python3 -m zlib or python3 -c '...zlib.decompress...' to get the raw blob/commit/tree text. Files in .git/lost-found/other/ from a previous git fsck --lost-found run are already plain text blobs, so cat works directly. file tells the two apart in one shot.

  4. Step 4Find surviving .git directory and HEAD
    If TSK turned up nothing - or once you've staged recovered objects - check whether any of the standard git plumbing survived intact.
    bash
    find /mnt/disk -name '.git' -type d 2>/dev/null
    bash
    find /mnt/disk -name 'HEAD' 2>/dev/null
    bash
    find /mnt/disk -path '*/objects/*/*' 2>/dev/null | head
    Learn more

    Git loose object files are stored in .git/objects/XX/YYYYYYYY... where XX is the first two hex characters of the object's SHA-1 hash. Each file is zlib-compressed and contains either a blob (file content), tree (directory snapshot), commit (history entry), or tag. Even if the .git directory itself was deleted, individual object files that haven't been overwritten can be recovered and reassembled.

    The HEAD file in a git repository contains a ref pointer like ref: refs/heads/main or a raw SHA-1 hash (detached HEAD). Finding an intact HEAD file tells you the repository's root and whether the ref structure survived. If HEAD is gone but object files remain, you can still reconstruct history by running git fsck in a directory containing those object files.

    Git also supports pack files (.git/objects/pack/*.pack) which bundle many objects into a single compressed archive with a corresponding index file. Pack files are more space-efficient than loose objects and are created by git gc or when fetching from a remote. If pack files survived, they may contain all the repository's history even if loose objects were deleted.

  5. Step 5Recover the repository
    Copy any surviving git directory or objects to a writable path and run git fsck to reconstruct reachable history.
    bash
    mkdir /tmp/recovered && cp -r /mnt/disk/<path>/.git /tmp/recovered/.git
    bash
    cd /tmp/recovered && git fsck --unreachable
    bash
    git log --all --oneline 2>/dev/null
    bash
    git fsck --lost-found && ls .git/lost-found/other/
    Learn more

    git fsck (filesystem check) verifies the integrity of the object database. It traverses all refs and objects, checks SHA-1 hashes for corruption, and reports dangling (unreachable) objects. Running it after recovering partial object files tells you which parts of the history survived and whether any objects are corrupt (truncated or overwritten blocks).

    If git log reports errors about missing objects, you can sometimes reconstruct the missing pieces by finding their raw bytes in the disk image using grep for the partial SHA-1 hash, or by using git hash-object -w to re-add a found blob. The recovery process is iterative: find surviving objects, identify which are missing, search for them in unallocated space, and repeat until the history is complete enough to read the flag.

  6. Step 6Extract the flag
    Read flag content from whichever commit, blob, or recovered file contains it.
    bash
    git cat-file -p <blob-hash>
    bash
    git show <commit>
    bash
    cat .git/lost-found/other/<hash>
    Learn more

    After recovery, git cat-file -p and git show let you read the content of any recovered object. The .git/lost-found/other/ directory (created by git fsck --lost-found) contains the dangling blob objects already decompressed and named by their SHA-1 hash, so plain cat works directly. Loose objects under .git/objects/XX/YY... are still zlib-compressed; use git cat-file -p for those, or decompress manually with python3 -m zlib < .git/objects/XX/YY... .

    This challenge is a microcosm of real incident response: a threat actor attempted to destroy evidence by deleting a repository, the deletion was interrupted, and forensic investigators recover what they can from the surviving artifacts. The same skills (mounting disk images, using TSK, recovering git objects) are used in real forensic investigations of developer workstations, source code repositories, and cloud storage buckets.

    For more on the surrounding shell workflow, see Linux CLI for CTF.

Flag

picoCTF{...}

The repository was partially deleted. Surviving git objects or deleted inodes on the disk hold the flag. Use git fsck --lost-found and TSK icat to recover them.

How to prevent this

If an attacker (or insider) reaches a developer machine, file deletion is reversible. Plan for full-disk compromise.

  • Encrypt developer disks at rest (FileVault, BitLocker, LUKS). Recovered inodes from an encrypted volume are useless without the key.
  • Don't store production credentials in repos at all, even briefly. Use a secrets manager (AWS Secrets Manager, Doppler, 1Password CLI, Vercel env vars) and pull at runtime. Then disk recovery yields nothing useful.
  • For high-value repos, enforce secure_erase / shred on decommission and full-disk wipe (NIST 800-88 purge) on hardware retirement. rm -rf alone leaves recoverable inodes for weeks.

Want more picoCTF 2026 writeups?

Useful tools for Forensics

Related reading

What to try next