Deleted File Recovery
Deleted files on NTFS volumes often remain recoverable because Windows marks clusters as available without overwriting them. This chapter covers metadata-based recovery (using intact MFT records), unallocated space recovery (when MFT records are gone), and how to prioritize recovery efforts on high-activity systems.
An attacker exfiltrated data and then deleted their staging directory with three PowerShell one-liners. You can see the folder deletion in the MFT (marked as deleted directory with deleted-file children). The MFT records for the individual files are still intact — all their metadata is there: filenames, sizes, timestamps. The data clusters may still be intact in unallocated space. Running TSK's icat on the MFT inodes recovers the actual file content. Within 30 minutes of discovering the deletion, you've recovered 11 of 13 staged files — the other 2 were overwritten by Windows system processes writing temp files. The 11 recovered files include a network scan output and a credentials file — evidence of exactly what the attacker accessed and intended to exfiltrate.
Metadata-Based Recovery (Intact MFT Records)
When the MFT record for a deleted file is still present (not yet reused), you can recover both the metadata (filename, timestamps, size) and the content (if the data clusters haven't been overwritten).
IMAGE="/cases/CASE-2026-009/FINANCE-SRV01.E01"
OFFSET=2048
CASE_DIR="/cases/CASE-2026-009"
RECOVERED="$CASE_DIR/recovered-files"
mkdir -p $RECOVERED
# Step 1: List deleted files matching extension filters
fls -r -d -o $OFFSET $IMAGE | grep -iE "\.(exe|ps1|bat|zip|7z|rar|txt|csv|xlsx)" | \
tee $CASE_DIR/deleted-file-list.txt
# fls output format:
# r/r * 123456-128-1: STAGING/output.csv (* = deleted, 123456 = inode)
# d/d * 78901-144-4: STAGING (* = deleted directory)
# Step 2: Extract specific file by inode
# Get inode from fls output (the number before the dash)
INODE=123456
icat -o $OFFSET $IMAGE $INODE > "$RECOVERED/output.csv"
# Verify: check size matches what's in MFT record
ls -la "$RECOVERED/output.csv"
# Step 3: Batch recovery of all deleted files matching the filter
grep -oP "\d+(?=-\d+-\d+:)" $CASE_DIR/deleted-file-list.txt | while read inode; do
FNAME=$(grep "^.*${inode}-" $CASE_DIR/deleted-file-list.txt | head -1 | sed 's/.*: //')
BASEFNAME=$(basename "$FNAME" | tr -d '*')
echo "Recovering inode $inode: $BASEFNAME"
icat -o $OFFSET $IMAGE $inode > "$RECOVERED/$inode-$BASEFNAME" 2>/dev/null
# Check if recovery was successful (non-zero file size)
SIZE=$(stat -c%s "$RECOVERED/$inode-$BASEFNAME" 2>/dev/null)
if [ "$SIZE" = "0" ]; then
echo " FAILED: clusters overwritten"
rm "$RECOVERED/$inode-$BASEFNAME"
fi
done
Verifying Recovered File Integrity
RECOVERED="/cases/CASE-2026-009/recovered-files"
# For each recovered file:
for f in $RECOVERED/*; do
filename=$(basename "$f")
size=$(stat -c%s "$f")
ext="${filename##*.}"
# File type identification (check header, not just extension)
file_type=$(file -b "$f")
echo "=== $filename ==="
echo " Size: $size bytes"
echo " Type (file cmd): $file_type"
# Check if file type matches extension
case $ext in
zip) echo $(echo "$file_type" | grep -i "zip" || echo " MISMATCH: not a ZIP") ;;
exe) echo $(echo "$file_type" | grep -i "PE" || echo " MISMATCH: not a PE") ;;
esac
# Hash for VT lookup
sha256sum "$f"
echo ""
done
Recovery from Unallocated Space (MFT Record Gone)
When the MFT record has been reused, you've lost the metadata. Content recovery via carving from unallocated clusters is the fallback.
IMAGE="/cases/CASE-2026-009/FINANCE-SRV01.E01"
OFFSET=2048
CASE_DIR="/cases/CASE-2026-009"
# Extract only unallocated clusters
# blkls -e: extract unallocated (not allocated, not slack)
blkls -e -o $OFFSET $IMAGE > $CASE_DIR/unallocated.dd
echo "Unallocated space: $(du -sh $CASE_DIR/unallocated.dd)"
# Carve known file types from unallocated space
foremost \
-t exe,dll,zip,7z,pdf,doc,docx,xlsx,ps1 \
-i $CASE_DIR/unallocated.dd \
-o $CASE_DIR/carved-unalloc/ \
-v
# Count recovered files by type
for dir in $CASE_DIR/carved-unalloc/*/; do
count=$(ls "$dir" 2>/dev/null | wc -l)
echo "$(basename $dir): $count files"
done
Factors Affecting Recovery Success
| Factor | Effect on recovery |
|---|---|
| Time since deletion | Shorter = better recovery chance. Files deleted 10 minutes ago on a low-activity server are usually recoverable; files deleted 3 days ago on a busy server may be 90% overwritten. |
| System activity after deletion | High-activity systems (busy web server, active workstation) overwrite free clusters faster. Server at 02:00 UTC = lower activity = better recovery window. |
| SSD with TRIM enabled | TRIM instructs the SSD to erase freed blocks. Modern Windows with SSDs: recovery window may be minutes to hours, not days. |
| File fragmentation | Contiguous files recover cleanly. Highly fragmented large files may have partial clusters overwritten in different parts of the disk. |
| Windows Volume Shadow Copy | VSS snapshot created before deletion → recover from snapshot. Doesn't depend on cluster availability at all. |
| File size | Small files (<1 cluster = 4KB) may reside in MFT resident data attribute and survive MFT record reuse as well — always check the inode for inline data. |
Q & A
Q: icat returns a file but it's clearly corrupt — partial data then zeros. What happened?
Some of the file's data clusters were overwritten after deletion. The MFT record is intact and still points to the original cluster addresses, but some of those clusters have been reused by new file data. icat reads the clusters faithfully — the zeros (or whatever the new file wrote) are now where part of the original content was. What you get is a partial recovery: the intact portions are genuine, the zeroed or garbage portions reflect overwritten clusters. Recovery strategy: (1) Analyze what you have — partial recovery of a large file often still contains valuable content (e.g., a partially recovered credential file may still contain the relevant credentials). (2) Check if the file was fragmented — istat shows the cluster run list. If the file had multiple fragments, some may be intact. (3) Look for the file in VSS if a snapshot predates the deletion — VSS copy would be intact regardless of cluster state.