Disk image forensics: partitions, deleted files, and slack
A forensics challenge hands you a raw disk image and no map. The layered way to take it apart - partition table, filesystem, deleted files, unallocated space, and slack - so the flag stops hiding in the gaps between files.
A disk image is the whole story of a machine frozen in a file: its partitions, its filesystem, the files that are there, and - crucially for CTF - the files that were deleted but never actually erased. When a challenge gives you a .dd, .img, .raw, or .E01, the flag is rarely in an obvious file. It is in a deleted one, in the space between partitions, or in the tail end of a block that a small file did not fill. This article is the layered dig that finds it.
The discipline that matters is working outside-in - the same layered order as image steganography or audio: identify the container before the contents, the filesystem before the files, the allocated files before the deleted ones. Skipping a layer is how people spend an hour in a hex editor on something the partition table would have told them in one command.
Layer 0: what am I even holding?
file disk.img # raw image? filesystem? partition table?
# An E01 is compressed and must be converted or mounted first:
ewfmount disk.E01 /mnt/ewf # exposes /mnt/ewf/ewf1 as a raw image
# Then treat /mnt/ewf/ewf1 as the raw disk everywhere below.Layer 1: the partition table
A whole-disk image starts with a partition table that says where each filesystem begins - and the 446 bytes before it are code, which is a challenge of its own in the boot process as a target. Read the table first, because every later tool needs the byte offset of the partition you care about, and because the gaps the table reveals are themselves a hiding place.
mmls disk.img
# Slot Start End Length Description
# 000: 0000000000 0000002047 0000002048 Unallocated
# 002: 000:0000002048 0000206847 0000204800 Linux (0x83)
# 003: 0000206848 0000208895 0000002048 Unallocated <-- a gapLayer 2: the filesystem, and the files that are still there
With the partition offset in hand, list the filesystem. The Sleuth Kit's fls walks the file table and - this is the point - shows deleted entries too, marked with an asterisk, as long as their metadata survives.
# -o is the partition offset from mmls. -r recurses, -d shows only deleted.
fls -o 2048 -r disk.img
# r/r 14-128-3: notes.txt
# r/r * 21-128-1: secret.txt <-- the * means deleted, metadata intact
# Pull a file out by its inode number (the 21 above):
icat -o 2048 disk.img 21 > secret.txtFor a quick win before careful work, mount the image read-only and just look. mount -o ro,loop,offset=$((2048*512)) disk.img /mnt gives you a normal filesystem to grep. But mounting only shows allocated files - it hides exactly the deleted and unallocated data the challenge is usually about, which is why fls/icat come first for anything non-trivial.
Layer 3: unallocated space and carving
When a file is deleted and its metadata is gone too, fls cannot see it - but the file's bytes are still sitting in unallocated blocks until something overwrites them. Carving ignores the filesystem entirely and scans the raw bytes for file signatures: a JPEG starts with FF D8 FF, a PNG with 89 50 4E 47, a ZIP with 50 4B 03 04. Find the header, read to the footer, and you have recovered a file with no help from the filesystem.
# Pull just the unallocated space, then carve only that (faster, cleaner):
blkls -o 2048 disk.img > unalloc.bin
foremost -t all -i unalloc.bin -o carved/
# or:
photorec disk.img # interactive, excellent recovery rate
scalpel -o carved/ disk.imgLayer 4: slack space, the last hiding place
Filesystems allocate storage in fixed blocks. A 10-byte file in a 4096-byte block leaves 4086 bytes of slack - space that belongs to the file but that the file does not use. The OS never clears it, so it holds whatever was in that block before. Authors hide flags here precisely because no ordinary file listing will ever show it.
# Extract the slack space across the filesystem:
blkls -s -o 2048 disk.img > slack.bin
strings slack.bin | grep -iE 'flag|ctf\{'The dig, in order
- Identify the container; convert or ewfmount an E01 to raw.
- mmls the partition table; note offsets and every unallocated gap.
- fls the filesystem for files including deleted ones; icat or tsk_recover to extract.
- blkls the unallocated space and carve it with foremost or photorec.
- blkls -s the slack space and strings it.
- Throughout: strings the whole image and check the human directories.