sleuthkit::tsk

tool sleuthkit disk forensics
Conceptsmmlsfsstat flsinode toolsTimeline Block toolsJournalWorkflow
01Core Concepts
TSK abstraction layers
LayerToolsWhat it sees
Disk / Volumemmls, mmstat, mmcatPartition table, MBR/GPT
FilesystemfsstatSuperblock, block size, layout
File / Namefls, ffindDirectory entries, filenames, deleted
Inode / Metails, istat, icatInode table, timestamps, block pointers
Block / Datablkls, blkcat, blkstatRaw data blocks, allocated/unallocated
Journaljls, jcatext3/4 journal entries
Common flags (apply to most tools)
FlagMeaning
-o <sector>Partition offset in 512-byte sectors (from mmls)
-b <size>Sector size override (default 512)
-f <type>Force filesystem type: ext4, ntfs, fat32, hfs…
-i <type>Force image type: raw, ewf, vmdk…
-z <tz>Timezone for timestamps
-vVerbose
02mmls — Partition Layout
Usage
# List all partitions (MBR or GPT)
mmls disk.img

# Force partition table type
mmls -t dos disk.img   # MBR
mmls -t gpt disk.img   # GPT
mmls -t mac disk.img   # Apple partition map

# Output columns:
# Slot | Start | End | Length | Description
# → use Start sector as -o value for other tools
Reading mmls output
# Example output:
# 002:  000:000   0000002048   0000616447   Linux (0x83)
# 003:  000:001   0000616448   0001140735   Linux Swap
# 004:  000:002   0001140736   0002097151   Linux (0x83)

# Partition types by ID:
# 0x83 = Linux ext2/3/4
# 0x82 = Linux swap (check for plaintext data!)
# 0x07 = NTFS / exFAT
# 0x0b/0x0c = FAT32
# 0xee = GPT protective MBR
# 0x05/0x0f = Extended partition

# Swap partition — no filesystem but may hold data
dd if=disk.img bs=512 skip=616448 count=524288 | strings | grep "picoCTF"
03fsstat — Filesystem Info
Usage
# Single partition image
fsstat partition.img

# Partition inside disk image (-o in sectors)
fsstat -o 2048 disk.img
fsstat -o 1140736 disk.img

# Key info to note from output:
# - File System Type (ext4, NTFS, FAT32…)
# - Block Size (usually 4096)
# - Block Count (total blocks)
# - Inode Range (first/last inode)
# - Journal Inode (usually 8 for ext4)
# - Volume Label / UUID
# - Last mounted / written time
What fsstat reveals
FieldCTF relevance
FS TypeDetermines which tools to use
Last mount timeTimeline anchor
Volume labelMay contain a hint or flag part
Free blocksLow = lots of data; compare with apparent content
Block sizeNeeded for manual block math
Journal inodeUse icat to extract journal raw data
04fls — File Listing
Key flags
# Basic listing
fls partition.img
fls -o 2048 disk.img

# Recursive
fls -r partition.img

# Deleted files only ← most useful in CTF
fls -rd partition.img
fls -rd -o 1140736 disk.img

# Full paths (easier to read)
fls -rp partition.img

# For MAC timeline bodyfile
fls -r -m "/" partition.img > body.txt
fls -r -m "/" -o 2048 disk.img > body_p1.txt
Reading fls output
# Output format: type/type inode filename
# r/r  = regular file (allocated)
# d/d  = directory
# * r/r = DELETED file (the * is the key)

# Example:
# r/r 2043:   etc/passwd
# * r/r 4945: bin/bcab        ← deleted, inode 4945
# d/d 1024:   home

# Extract any file by inode:
icat partition.img 4945 > /tmp/out
icat -o 1140736 disk.img 4945 > /tmp/out

# Pipe to grep for specific names
fls -rp partition.img | grep -i "flag\|secret\|pass\|key"
05Inode Tools — istat, icat, ils
istat — inode metadata
# Show full inode info: timestamps, size, blocks
istat partition.img <inode>
istat -o 2048 disk.img <inode>

# Output includes:
# Size, UID/GID, mode/permissions
# Accessed / Modified / Changed / Created times
# Direct/indirect block pointers

# Timestomping tells:
# - All 4 times identical → set by tool at once
# - Modified < Created → impossible, flag it
# - Epoch 0 (1970-01-01) → zeroed out
icat — extract by inode
# Extract file content by inode number
icat partition.img <inode> > /tmp/recovered
icat -o 1140736 disk.img <inode> > /tmp/recovered

# Always check what you got
file /tmp/recovered
xxd /tmp/recovered | head -4
strings /tmp/recovered
binwalk /tmp/recovered

# Special inodes (ext4):
# 1  = bad blocks
# 2  = root directory
# 8  = journal  ← icat img 8 > journal.bin
# 11 = lost+found
ils — list inodes
# List all inodes (allocated + unallocated)
ils partition.img
ils -o 2048 disk.img

# Unallocated (deleted) inodes only
ils -u partition.img

# Allocated only
ils -a partition.img

# Pipe to sort by size (field 7)
ils partition.img | sort -t'|' -k7 -rn | head

# ffind: find filename for a given inode
ffind partition.img <inode>
06MAC Timeline
Build & analyze
# Step 1: create bodyfile from each partition
fls -r -m "/" -o 2048    disk.img > body_p1.txt
fls -r -m "/" -o 1140736 disk.img > body_p3.txt
cat body_p1.txt body_p3.txt > body_all.txt

# Step 2: generate timeline
mactime -b body_all.txt -d > timeline.csv
mactime -b body_all.txt -d -z UTC > timeline_utc.csv

# Step 3: filter for anomalies
grep "197[0-9]\|198[0-9]\|Jan 01 1970" timeline.csv
sort timeline.csv | head -30        # oldest first

# Filter by date range
mactime -b body_all.txt 1970-01-01,1990-01-01
bodyfile format
# Pipe-delimited: 11 fields
# MD5|name|inode|mode|uid|gid|size|atime|mtime|ctime|crtime
#  1    2    3    4    5   6   7     8     9     10    11

# Sort directly by mtime (field 9)
sort -t'|' -k9 -n body_all.txt | head -20

# Timeline CSV output format:
# Date,Size,Type,Mode,UID,GID,Inode,Filename
# e.g: Tue Jan 01 1985 18:00:00,41,macb,...,4945,"/bin/bcab"
#                                  ↑              ↑inode
# macb = Modified+Accessed+Changed+Born all same → timestomped

# Extract the suspicious file:
icat -o <offset> disk.img <inode> > /tmp/sus
07Block Tools
blkls, blkcat, blkstat
# blkls: dump unallocated (free) blocks
blkls partition.img > unalloc.bin
blkls -o 2048 disk.img > unalloc.bin
strings unalloc.bin | grep "picoCTF"
foremost -i unalloc.bin -o ./carved/

# blkls with flags
blkls -e partition.img   # allocated + unallocated
blkls -a partition.img   # allocated only
blkls -s partition.img   # slack space only

# blkcat: dump a specific block number
blkcat partition.img <block>
blkcat -o 2048 disk.img <block> | xxd

# blkstat: info about a block
blkstat partition.img <block>
# shows: allocated/free, which inode owns it
Block math
# Convert sector offset → byte offset
python3 -c "print(2048 * 512)"           # → 1048576

# Convert block number → byte offset within partition
python3 -c "print(1234 * 4096)"          # block_num * block_size

# Extract a block with dd
dd if=partition.img of=block.bin \
   bs=4096 skip=<block_num> count=1

# Extract partition from disk image
dd if=disk.img of=p1.img \
   bs=512 skip=2048 count=614400
08Journal Analysis (ext4)
jls & jcat
# jls: list journal entries
jls partition.img
jls -o 2048 disk.img

# jcat: dump a journal entry block
jcat partition.img <seq_num>
jcat -o 2048 disk.img <seq_num> | xxd

# Extract entire journal (inode 8 in ext4)
icat partition.img 8 > journal.bin
strings journal.bin | grep -i "flag\|picoCTF\|pass"

# The journal may contain:
# - Previous versions of files (before deletion)
# - Old content of overwritten files
# - Metadata changes
debugfs (interactive)
# Open image interactively
debugfs partition.img
debugfs -i disk.img   # disk image mode

# Useful debugfs commands:
#   ls -l /           list root, including deleted
#   lsdel             list deleted inodes
#   stat <file>       inode info
#   dump <file> /tmp/out   extract file
#   cat <file>        print file content
#   show_super_stats  superblock details
#   logdump           journal dump
09CTF Workflow
Single partition image
file img.img && fsstat img.img
fls -rp  img.img | grep -i "flag\|key\|secret"    # filenames
fls -rd  img.img                                     # deleted files
fls -r -m "/" img.img > body.txt && mactime -b body.txt -d | grep "197\|198"
blkls img.img | strings | grep "picoCTF"             # unallocated data
icat img.img <inode> > /tmp/f && file /tmp/f && strings /tmp/f
Full disk image (MBR/GPT)
mmls disk.img                                        # partition map
fsstat -o <p1_start> disk.img                        # identify each FS
fls -rd -o <p1_start> disk.img                       # deleted in p1
fls -r -m "/" -o <p1_start> disk.img > b1.txt        # bodyfile p1
fls -r -m "/" -o <p3_start> disk.img > b3.txt        # bodyfile p3
mactime -b <(cat b1.txt b3.txt) -d | grep "197\|198" # anomalies
dd if=disk.img bs=512 skip=<swap_start> count=<swap_len> | strings | grep "CTF"
QUICK REFERENCE →  mmls partition layout  · fsstat -o filesystem info  · fls -rd -o deleted files  · fls -r -m "/" -o → bodyfile → mactime timeline  · icat -o extract by inode  · blkls -o unallocated space  · icat img 8 journal