Description
Can you find the flag in this disk image?
Setup
Download and decompress the disk image.
Mount the image and explore the git repository inside.
gunzip disk.img.gzsudo mount -o loop disk.img /mnt/diskSolution
- Step 1Decompress and quick strings checkExtract the disk image, then run strings as a fast pre-check for an obvious flag before mounting.
gunzip disk.img.gzstrings disk.img | grep picoCTFLearn more
stringsscans a binary file for sequences of printable ASCII characters of a minimum length (default 4). It is the fastest possible triage tool for a disk image: if the flag is stored in plaintext anywhere in the image — in a file, in git object data, or even in slack space —strings | grep picoCTFfinds it immediately without mounting or parsing the filesystem.Disk images are raw byte-for-byte copies of storage devices. A gzip-compressed image (
.img.gz) must be decompressed first because the compression transforms the bytes, makingstringsuseless on the compressed file. After decompression, the image contains the same layout as the original disk, including partition tables, filesystem metadata, and file data.In real forensic investigations this triage step is called a keyword search and is one of the first steps in any examination. Tools like Autopsy and Bulk Extractor automate keyword searches across entire disk images and can find strings in compressed or carved files as well.
- Step 2Detect partition layout and mountUse mmls to find the partition offset, then mount the correct partition.
mmls disk.imgsudo mount -o loop,offset=$((512*<start_sector>)) disk.img /mnt/diskls /mnt/diskLearn more
A disk image contains one or more partitions described by a partition table (MBR or GPT) at the start of the image.
mmls(from The Sleuth Kit) reads this table and prints each partition's start sector, size, and type. Each sector is typically 512 bytes, so multiplying the start sector by 512 gives the byte offset where the partition begins.Linux's
mount -o loop,offset=Nmounts a file as a block device starting at byte offset N. This allows you to mount an individual partition from a full disk image without splitting the image into separate files. Theloopoption attaches the file to a loop device (like/dev/loop0) which the kernel then treats as a block device.Alternatively,
kpartx -av disk.imgautomatically creates loop device mappings for every partition in the image, andfdisk -l disk.imgshows the partition table in a familiar format. For read-only analysis it is good practice to mount with-o loop,offset=N,roto prevent accidental writes to the evidence. - Step 3Find and copy the git repositoryLocate the .git directory on the mounted image and copy it to a writable location so git commands work.
find /mnt/disk -name '.git' -type dcp -r /mnt/disk/<repo_path> /tmp/repocd /tmp/repoLearn more
A git repository stores its entire history in the hidden
.gitdirectory at the root of the working tree. This directory contains the object store (.git/objects/), refs (.git/refs/), the HEAD pointer, config, and optional extras like stash and notes. Everything needed to reconstruct any version of every file ever committed is inside.git.Mounted loop devices are usually read-only or have ownership restrictions that prevent running git commands in-place. Copying the repository to
/tmp(or another writable path) before running git commands avoids permission errors and also protects the mounted evidence from accidental modification. After copying, git commands run against the local copy as if it were a normal repository.In digital forensics, working on a copy rather than the original is called working on a forensic duplicate. The original evidence (the disk image) remains intact and can be re-examined if the working copy is corrupted. This is a core principle of the ACPO Good Practice Guide and similar forensic standards.
- Step 4Comprehensive git history searchSearch through all branches, commit patches, stash, tags, notes, reflog, and dangling objects. The flag can be in any of these locations.
git log --all -p | grep -A2 picoCTFgit branch -agit stash listgit tag -lgit notes listgit refloggit fsck --unreachable 2>&1 | grep blobgit fsck --lost-found && ls .git/lost-found/other/Learn more
Git's content-addressed object store means that deleted data is not truly gone until it is garbage-collected. Every commit, tree, blob, and tag is stored as an object identified by the SHA-1 hash of its content. Deleting a file in a commit creates a new commit that does not reference the old blob, but the blob itself remains in
.git/objects/untilgit gcprunes it. The same applies to deleted branches, dropped stash entries, and amended commits — the old objects persist as dangling (unreachable) objects.The eight locations to check are:
- Commit history (
git log --all -p): shows diffs across all branches - Branches (
git branch -a): lists local and remote-tracking branches - Stash (
git stash list): saved work-in-progress snapshots - Tags (
git tag -l): annotated tags can hold arbitrary messages - Notes (
git notes list): metadata attached to commits outside the commit object - Reflog (
git reflog): every movement of HEAD and branch tips, including force-pushes and resets - Dangling blobs (
git fsck --unreachable): objects with no path to any ref - Lost-and-found (
git fsck --lost-found): writes dangling objects to.git/lost-found/
This breadth of hiding locations makes git repositories a rich source of sensitive data in real investigations. Developers often accidentally commit API keys, passwords, or private keys, then delete them in a follow-up commit — but the data remains in history. Tools like truffleHog, git-secrets, and gitleaks scan repositories for secrets in all of these locations.
- Commit history (
- Step 5Read flag contentOnce a suspicious commit or blob is found, show its full content.
git show <commit>git cat-file -p <blob-hash>cat <flag-file>Learn more
git show <hash>displays the content and metadata of any git object: for commits it shows the diff; for blobs it shows the raw file content; for trees it lists the directory entries; for tags it shows the tag message and the tagged object.git cat-file -p <hash>does the same but works with raw hashes and is slightly more low-level — useful when you have a hash fromgit fsckoutput and want to inspect it without knowing the object type.Understanding git's object model at this level is valuable for both forensics and everyday development. Knowing that every version of every file is a blob object with a deterministic hash, and that commit objects reference tree objects (directory snapshots) which reference blob objects, explains why git is so reliable for history — and why deleted data persists until
git gc --prune=now --aggressiveexplicitly removes unreachable objects.
Flag
picoCTF{...}
The flag is hidden in the git repository on the disk image. Check commit history, stash, tags, notes, reflog, and dangling objects.