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. (If the image turns out to be RAM rather than a disk, it is a memory dump and a different toolchain entirely.) 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?
- allocated fileswhat a file manager would show you
- deleted but recoverablestill in the metadata, not yet overwritten
- unallocated spaceno metadata at all - carving territory
- slack spacethe tail of the last cluster of every file
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 before anything else touches it.Read the partition table
mmls. Note the offsets, and every unallocated gap.List the filesystem
flsfor files including deleted ones;icatortsk_recoverto extract.Carve the unallocated space
blkls, thenforemostorphotorec.Read the slack
blkls -s, then strings.Throughout
Strings the whole image, and actually look in the human directories.