daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

commit 678e012e70b52aba81298ac65dd92b1ba6556ff2
parent fc223614460bd4cd348bf2127d98763c809c28be
Author: $: DAΞMON <zer0sec.xp@icloud.com>
Date:   Sat, 26 Sep 2026 10:09:59 +0100

Add five DFIR sheets: acquisition, EZ Tools/KAPE, Linux, strings triage, GUI tools

The dfir category had four sheets and no Linux coverage, no strings/carving
reference, and only passing mentions of the GUI tools and the Zimmerman suite.
Add one sheet per gap, each with a subcategory so the category page groups
them (Acquisition / Windows / Linux / Analysis):

- disk-imaging: order of volatility, write blocking, dd/dc3dd/ewfacquire,
  FTK Imager GUI+CLI, WinPmem/DumpIt/AVML/LiME, hashing, mounting E01/raw/VSS
- ez-tools-kape: Get-ZimmermanTools, KAPE targets/modules and the triage
  command, every *Cmd parser with the artefact it reads, Timeline Explorer,
  a worked live-box-to-timeline workflow
- linux-forensics: live response order, /var/log map, wtmp/btmp, journalctl,
  auditd, histories, persistence hunt, package integrity, /proc, timestomping,
  SIFT with Sleuth Kit and plaso
- strings-file-triage: strings in both encodings, IOC grep pass, magic bytes,
  xxd, exiftool, binwalk, maldoc tools, hashing/ssdeep, carving, YARA
- dfir-gui-tools: click-path walkthroughs for Autopsy, FTK Imager, Arsenal
  Image Mounter, Registry/Timeline/ShellBags Explorer, event log GUIs,
  Wireshark, Velociraptor, Timesketch

All cross-links resolve (internal-links test), build passes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Asrc/content/sheets/dfir/dfir-gui-tools.md | 200+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/content/sheets/dfir/disk-imaging.md | 336+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/content/sheets/dfir/ez-tools-kape.md | 404+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/content/sheets/dfir/linux-forensics.md | 365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/content/sheets/dfir/strings-file-triage.md | 365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1670 insertions(+), 0 deletions(-)

diff --git a/src/content/sheets/dfir/dfir-gui-tools.md b/src/content/sheets/dfir/dfir-gui-tools.md @@ -0,0 +1,200 @@ +--- +title: "DFIR GUI Tools" +description: "Click-path walkthroughs for the point-and-click half of DFIR: Autopsy (case, data source, ingest modules, results tree, keyword lists, timeline, report), FTK Imager, Arsenal Image Mounter, Registry Explorer, Timeline Explorer, ShellBags Explorer, Event Log Explorer and Hayabusa HTML, plus Velociraptor and Timesketch for team review." +category: dfir +subcategory: "Analysis" +tags: [dfir, forensics, gui, autopsy, sleuth-kit, ftk-imager, registry-explorer, timeline-explorer, shellbags, event-logs, windows, linux] +tools: ["Autopsy", "FTK Imager", "Arsenal Image Mounter", "Registry Explorer", "Timeline Explorer", "ShellBags Explorer", "MFTExplorer", "EZViewer", "Event Log Explorer", "Hayabusa", "Velociraptor", "Timesketch", "Wireshark", "HxD / ImHex"] +difficulty: intermediate +updated: "2026-09-26" +--- + +# DFIR GUI Tools + +The command-line parsers in [EZ Tools](/sheets/dfir/ez-tools-kape) and [Linux forensics](/sheets/dfir/linux-forensics) produce the data; these tools are where you **read** it, browse an image like a filesystem, and build the picture you put in the report. Each section below is a **click path**: what to open, in what order, what each pane is telling you, and the two or three features people miss for years. Autopsy gets the most room because it is the free, cross-platform full suite most modules teach; the Zimmerman GUIs get the second most because they are what you will use daily on Windows cases. + +> [!info] GUI ≠ point-and-hope +> Every GUI here is a viewer over the same artefacts the CLI tools parse. Know **which artefact** a result node came from (Autopsy tells you in the *Source File* column; Registry Explorer in the key path) so you can cite it, reproduce it on the command line, and explain its caveats. A finding you cannot explain is not a finding. + +## Autopsy + +Open-source (Basis Technology / Sleuth Kit), Windows installer or Linux/macOS via the zip + `unix_setup.sh`. It ingests raw/E01/VHD/VMDK images, local disks, logical folders, and *unallocated-space* files, runs **ingest modules** in the background, and files everything into a **results tree**. Version 4.2x; Java is bundled on Windows. + +### Case → data source → ingest + +1. **New Case** → name, base directory (put it on fast local disk, not the evidence drive), single-user. Fill *Examiner* and *Case number* — they land in the report. +2. **Add Data Source** → *Disk Image or VM File* (E01, dd, VHD, VMDK, split images pick `.001`/`.E01` only) → set **Time zone of the source machine** (critical: Autopsy displays in *its* configured zone; set both under Tools → Options → View) → *Ignore orphan files in FAT* only if you are in a hurry → **Next**. + - Other types: *Local Disk* (needs admin; use a write blocker), *Logical Files* (a KAPE/UAC triage folder — Autopsy will still run keyword/hash/recent-activity modules over it), *Unallocated Space Image File* (the `blkls` output), *Autopsy Logical Imager Results*. +3. **Configure Ingest** — tick modules. First pass, keep it lean so results appear fast: + +| Module | Does | Tick on first pass? | +|---|---|---| +| **Recent Activity** | registry (via RegRipper), browser history/downloads/cookies, Recycle Bin, USB, installed programs, shellbags, recent docs, OS info | yes — the Windows overview | +| **Hash Lookup** | MD5 every file, compare to NSRL (known-good) and your notable sets | yes, once you have imported an NSRL/notable hash set under Tools → Options → Hash Sets | +| **File Type Identification** | libmagic every file (needed by many others) | yes | +| **Extension Mismatch Detector** | `.jpg` that is really an exe | yes | +| **Embedded File Extractor** | opens ZIP/RAR/7z/DOCX/PDF and ingests contents as child files | yes (slow on big images) | +| **Picture Analyzer** | EXIF/GPS from images | yes | +| **Keyword Search** | indexes text (Solr) and runs lists: emails, IPs, URLs, phone, credit cards, your custom regexes | yes with only the lists you need; the **index** step is what makes the case slow | +| **Email Parser** | PST/mbox/EML → messages, attachments | if mail matters | +| **Encryption Detection** | high-entropy files, password-protected Office/PDF/zips | yes | +| **Interesting Files Identifier** | rule sets: files/dirs by name, path, size (ship: cloud storage, crypto wallets, VPN clients, anti-forensics tools) | yes | +| **Central Repository** | correlates hashes/emails/USB IDs across all your cases | yes in a multi-case shop | +| **PhotoRec Carver** | carves unallocated space with PhotoRec | second pass — slow, noisy | +| **Virtual Machine Extractor** | finds `.vmdk`/`.vhd` inside the image and ingests them as data sources | if present | +| **Data Source Integrity** | verifies E01 hash | yes | +| **Plaso** | runs log2timeline over the source for the Timeline view | second pass — very slow; usually run plaso yourself | +| **Android Analyzer / iOS (aLEAPP/iLEAPP)** | mobile extractions | mobile only | +| **YARA Analyzer** | your rule sets over every file | yes with a case rule set | +| **Drone Analyzer, Cyber Triage Malware Scanner** | niche / paid | no | + +4. **Finish**. Ingest runs in the background (progress bar bottom right); results appear in the tree as modules finish. You can browse immediately — file system nodes are available the moment the image is parsed. + +> [!tip] Ingest order that saves an afternoon +> Run *Recent Activity + File Type + Hash Lookup + Extension Mismatch + Interesting Files + Encryption Detection* first (30–90 min for a 500 GB image). Read those results while a **second ingest** (right-click the data source → *Run Ingest Modules*) does Keyword Search indexing, Embedded File Extractor and, if you must, PhotoRec. + +### Reading the tree (left pane) + +| Node | What is in it | +|---|---| +| **Data Sources** → image → volumes | the filesystem as-is; `$OrphanFiles`, `$Unalloc`, `$CarvedFiles` are synthetic dirs Autopsy adds. Deleted files show with a red **X** icon and are browsable | +| **File Views → File Types → By Extension / By MIME Type** | all images, all documents, all executables regardless of where they are — start here for "any PowerShell scripts on this box?" | +| **File Views → Deleted Files** | *File System* (metadata still present — timestamps intact) vs *All* (incl. carved) | +| **File Views → File Size** | 200 MB+ files: containers, archives, VMs, dumps | +| **Data Artifacts** (older versions: Extracted Content) | **Installed Programs, Operating System Information, OS Accounts, Recent Documents, Run Programs, Shell Bags, USB Device Attached, Web Bookmarks/Cookies/Downloads/History/Search/Form Autofill/Account Type, Recycle Bin, Metadata, EXIF, Email Messages, Encryption Detected/Suspected, Extension Mismatch, Interesting Items, Keyword Hits, Hashset Hits, YARA Hits** | +| **Analysis Results** (4.19+) | scored results: **Interesting Items, Keyword Hits, Hashset Hits, Encryption, Extension Mismatch, YARA, Web Categories, Previously Seen/Notable/Unseen** (central repo) | +| **OS Accounts** | every account with SID, home, login count, last login (from SAM + registry) — click one → *OS Account* tab lists everything attributed to it | +| **Tags** | your bookmarks, by tag name | +| **Reports** | generated reports | + +Right pane tabs for any selected file: **Hex, Text (strings / indexed text / translation), Application (image/PDF/HTML/SQLite/registry/PList viewer), File Metadata (all TSK times, MD5, MIME, `$MFT` entry, sectors), OS Account, Data Artifacts, Analysis Results, Context (where it came from: download URL, email attachment, zip parent), Annotations (your comments/tags), Other Occurrences (central repo: seen in another case?)**. + +### The moves that matter + +```text +Right-click any file → Tag File → Bookmark / Follow Up / Notable Item (tags are what you report) + → Extract File(s) (with original name; keep timestamps via Tools → Options → General) + → View in Directory / View Source File / View in Timeline + → Add File to Hash Set (build your notable set as you go) + → Search for files with the same MD5 +Right-click a directory → Extract (whole tree) +Data Source right-click → Run Ingest Modules (second pass) / Add Data Source Hash / Delete +Tools → File Search by Attributes name / size / MIME / date range / known status / hash — "all files modified 20–22 Sep under Users\" +Tools → Run Ingest Modules / View Ingest Progress / Ingest Inbox (alerts as hits land) +Keyword Search (top right bar) exact / substring / regex, over the Solr index — ad hoc, after indexing finished +Tools → Options → Keyword Search → Lists add a case list: attacker tool names, IPs, hostnames, user names, "mimikatz|rubeus|sharphound|rclone|anydesk" +Tools → Options → Interesting Files import the community rules (github.com/sleuthkit/autopsy/... or your own XML) — flags cloud sync, VPN, TOR, wipers by name +Tools → Options → Hash Sets import NSRL (known), notable sets (yours / ClamAV / community); Central Repository → import from case +Tools → Timeline see below +Tools → Image/Video Gallery thumbnail wall grouped by folder, with EXIF/GPS; tag from there +Tools → Communications graph + table of accounts (email, phone, IM) and who talked to whom +Tools → Geolocation every GPS point from EXIF/browser/mobile on a map, KML export +Discovery (toolbar) images / videos / documents / domains by size, date, "past occurrences" — fast triage of media and web activity +Tools → Generate Report HTML / Excel / KML / STIX / Portable Case (a mini-case with just the tagged items you can hand to counsel) +``` + +### Timeline view + +**Tools → Timeline** opens a separate window with two modes. **Counts** (bar chart per time unit — spot the spike of activity at 03:00) → drag to zoom → switch to **Details** (event clusters by path, expand to individual events) or **List** (a table you can sort/filter/export). The **Filters** pane on the left: hide known files, text filter on path, event types (File System: M/A/C/B; Web; Misc: recent docs, installed programs, USB, exif, log entries...). Right-click an event → *View File in Directory*. Everything you **tag** shows as a pin. It draws from TSK timestamps plus the Recent Activity results; if you ran the Plaso ingest module the log events appear too. + +### Autopsy vs Sleuth Kit CLI + +Autopsy **is** Sleuth Kit with a Java GUI and a results database (`autopsy.db`, SQLite — open it with `sqlite3` when you want a bulk export the GUI does not offer: `tsk_files`, `blackboard_artifacts`, `blackboard_attributes`). Anything Autopsy shows, [`fls`/`icat`/`istat`](/sheets/dfir/linux-forensics#sleuth-kit-essentials) can reproduce for the report appendix. + +> [!warning] Autopsy limits +> Single-user cases lock the `.aut` — one analyst at a time (multi-user needs PostgreSQL + Solr + ActiveMQ). Keyword indexing of a full disk can take a day; scope it. Autopsy parses registry via RegRipper and shellbags/browser history natively, but for **serious Windows artefact work** (MFT with `$FN` timestamps, USN, Prefetch run times, Amcache SHA-1, SRUM, ShimCache) export the files and use the EZ Tools. Its browser parsing lags Chrome/Firefox schema changes — cross-check with `SQLECmd`/hindsight. + +## FTK Imager + +Exterro (formerly AccessData), free. Imaging is in [acquisition](/sheets/dfir/disk-imaging#ftk-imager-windows-gui--cli); this is the **viewer** side. + +- **File → Add Evidence Item** → Image File (E01/dd/AD1/AFF), Physical Drive, Logical Drive, or Contents of a Folder. Evidence Tree (top-left) → File List (top-right, with dates/size/type) → **Hex + Properties/Hex Value Interpreter/Custom Content** (bottom). +- Every NTFS metafile is browsable: `[root]\$MFT`, `$LogFile`, `$Extend\$UsnJrnl:$J`, `$Secure`, `$Recycle.Bin`, plus `[unallocated space]` and `[orphan]`. Right-click → **Export Files** (keeps original timestamps, writes an export log with hashes) → this is how you hand `$MFT` and the hives to MFTECmd/Registry Explorer without mounting anything. +- **Alternate Data Streams** show as child entries of the file (`file.exe:Zone.Identifier`). Deleted files show with a red **X** and are exportable if their clusters survive. +- **Properties** tab: all four timestamps, `$MFT` record number, allocated/unallocated, start cluster. **Hex Value Interpreter**: select bytes → see them as DOS date, FILETIME, Unix time, int — decoding a raw timestamp without a calculator. +- **File → Obtain Protected Files** (on a live system): grabs `SAM`, `SECURITY`, `SOFTWARE`, `SYSTEM`, `NTUSER.DAT`s, `$MFT` — locked files nothing else copies. +- **File → Image Mounting**: mount an image as a drive letter (Physical & Logical, **Block Device / Read Only**) so KAPE, EZ Tools or AV can run against it; **File → Unmount** when done. +- **File → Export File Hash List**, **Export Directory Listing** (CSV of every file with timestamps: the poor man's `fls`). +- **Custom Content Image**: right-click any files/dirs → *Add to Custom Content Image (AD1)* → File → Create Custom Content Image — a hashed, forensically-wrapped export of just those items (what a triage collection looked like before KAPE). + +## Arsenal Image Mounter + +Free (the paid tier adds write-temporary and VSS-delete). Mounts **E01/raw/VHD/VMDK/AFF4** as a **real SCSI disk** at the Windows disk layer, so Volume Shadow Copies, BitLocker (if you have the key/password), Storage Spaces and dynamic disks behave — things FTK Imager's mounter does not do. **Mount disk image → pick file → Read only** (or *Write temporary* to let Windows "fix" a dirty NTFS volume in a scratch file without touching the image) → OK. The mounted disk appears in Disk Management; volumes get letters. Then: `vssadmin list shadows /for=F:` and `mklink /d C:\vss1 \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\` to browse a shadow copy, or point KAPE `--tsource F: --vss`. Also **launches a Windows VM from the image** (Hyper-V/VirtualBox) — booting the suspect's OS for a screenshot of their desktop, with the image untouched. + +## Registry Explorer (Eric Zimmerman) + +The registry GUI; `RECmd` is its CLI twin ([own sheet](/sheets/dfir/recmd)). + +- **File → Load hive** → pick `SYSTEM`/`SOFTWARE`/`SAM`/`SECURITY`/`NTUSER.DAT`/`UsrClass.dat`/`Amcache.hve`. If `.LOG1/.LOG2` sit next to it you are asked to **replay transaction logs** — say yes; it writes a clean copy (`*_clean`) and shows what changed. A hive loaded without its logs can be missing the last minutes/hours of writes. Load several hives at once (multi-select). +- **Left tree** = keys with last-write time. **Right grid** = values (name, type, data, raw). Bottom: **Type Viewer** (decoded data: FILETIME, ROT13 UserAssist, binary structures), **Slack Viewer**, **Hex**. +- **Bookmarks tab** (right of the tree): hundreds of pre-defined "keys of interest" grouped by hive — *Common* (Run keys, services, USBSTOR, MountedDevices, NetworkList, TimeZone, ProfileList, Uninstall...) and *User created*. Click one → jump. Bookmarked keys show **red** in the tree, **blue** = has a plugin. +- **Plugins**: keys with a decoder get an extra grid tab — **UserAssist** (decoded names, run counts, focus time), **ShellBags** (in-place), **USBSTOR**, **MountedDevices**, **NetworkList**, **TypedURLs**, **WordWheelQuery**, **RecentDocs**, **OpenSavePidlMRU**, **SAM** (users, RIDs, last login, password set, login count, groups), **Services**, **AppCompatCache**, **BAM**, **TaskCache**, **Amcache**, **TerminalServerClient**, **7-Zip/WinRAR history**, **WinSCP/Putty sessions** ... Right-click a plugin grid → *Export* CSV/XLSX. +- **Deleted keys/values**: `Options → Recover deleted keys/values` (default on) puts a **`Unassociated deleted records`** node and marks recovered items — this is what you show when the attacker cleaned a Run key. +- **Search**: `Ctrl+F` / **Tools → Find** — key names, value names, value data (string/hex), **date range on last-write**, regex; results grid, double-click to jump. "All Run-key-like values with `.exe` under `AppData` in the last 30 days" is one search. +- **Timestamps**: right-click a key → *Export → Key and subkey last write times* for the timeline; the whole hive → *File → Export → Hive to CSV* is what `RECmd --csv` does. +- **Project**: File → Project → Save keeps loaded hives + bookmarks + expanded state. + +## Timeline Explorer (Eric Zimmerman) + +Covered from the CSV side in [EZ Tools](/sheets/dfir/ez-tools-kape#timeline-explorer--reviewing-the-csvs). The habits that make it fast: + +- **File → Open** several CSVs at once (or drag a folder) → one tab each; **File → Session → Save** so tomorrow you reopen the whole case with filters and tags. +- **Filter row** under the header for every column: `%rclone%` (contains), `2026-09-2%` (dates as text), `>2026-09-20 03:00`, `= 4624`, `Like`, `In (4624,4625)`. Click the funnel on a header for the pick-list. **Filter editor** (bottom-left funnel) for boolean chains across columns; the current filter is shown as text at the bottom — copy it into your notes. +- **Ctrl+F**: search all columns; **Ctrl+T** toggles tag on selected rows; **Tag** column checkboxes; **Tools → Show only tagged / Clear tags**. +- **Group by** by dragging a column header into the group band: `EventId` for evtx, `ParentPath` for MFT, `ExecutableName` for Prefetch. Collapse everything, expand the interesting groups. +- **Conditional Formatting** (right-click header) → *Highlight Cell Rules* — colour 4624 green, 4625 red, 4672 yellow, 7045 orange, 1102 purple; save as part of the layout. +- **Column Chooser** (right-click header) to hide the 60 columns you do not need; **Best Fit (all columns)** after. +- **Line/Tag/Wrap**: View → *Word wrap* for `Payload`; **View → Show/hide detail pane**. +- **Copy**: `Ctrl+C` copies selected rows with headers; **File → Export** the filtered/tagged view to XLSX/CSV/HTML → the appendix. +- Timeline Explorer opens **any** CSV/TSV (plaso `psort -o l2tcsv`, Hayabusa CSV, `mactime -d` output). Dates are recognised if they parse; set `--dt` in the EZ tools to ISO if a locale fights you. + +## ShellBags Explorer (Eric Zimmerman) + +**File → Load offline hive** → pick `UsrClass.dat` (Win7+ Explorer bags live here), then it offers to load the matching `NTUSER.DAT`; or **File → Load active registry** on a live box. Left: the **folder tree as the user saw it** — This PC, drives, `\\server\share`, removable, Control Panel, zip files browsed as folders. Click a node → grid of children with **First Interacted / Last Interacted**, **MFT entry/sequence** (pivot to MFTECmd output), **Absolute path**, **Shell type** (GUID-based, network, file entry, zip content...). Pink = has children; italics = derived. **Search** box filters the tree. **Export → CSV** of everything = what `SBECmd` produces. Read the `Value` column: bag numbers give the order the user created them. + +## MFTExplorer and EZViewer + +**MFTExplorer**: File → Load `$MFT` → tree of the volume on the left, and for the selected record the full parsed `$STANDARD_INFORMATION` / `$FILE_NAME` / `$DATA` attributes (all timestamps side by side, resident data in hex, data runs, ADS, security id). It is slow on big MFTs (minutes) but is how you *show* a timestomp in a screenshot. **EZViewer**: drag any file onto it — images, Office, PDF, text, hex — renders without the real application ever touching the evidence; the default double-click target for a forensic workstation. + +## Event logs: Event Viewer, Event Log Explorer, Hayabusa HTML + +- **Windows Event Viewer**: *Action → Open Saved Log* on an exported `.evtx`; *Filter Current Log* by ID, level, time; **XML view** of an event for the fields; *Create Custom View* across channels. Usable, slow, no cross-log timeline — export to EvtxECmd for anything real. +- **Event Log Explorer** (FSPro, free for personal use): opens dozens of `.evtx` at once, merges them into one view, filters by ID/user/text with saved filter sets, and follows `Description` fields to columns. The best pure-GUI evtx reader on Windows. +- **Hayabusa** (`hayabusa.exe csv-timeline -d Logs -o out.csv` or `html-report`): Sigma-rule detection; the **HTML report** (`-H report.html`) gives rule hits by severity, a timeline chart, top computers/users — the executive view before you dive into Timeline Explorer on the CSV. **Chainsaw** (`chainsaw hunt Logs/ -s sigma/ --mapping mappings/sigma-event-logs-all.yml -r rules/ --csv --output out/`) is the alternative, also with an ASCII table output. +- Linux: `journalctl` has no GUI worth using; `gnome-logs` for live boxes, otherwise plaso → Timesketch. + +## Wireshark + +The pcap GUI; the CLI lives in [TShark](/sheets/dfir/tshark). Habits for DFIR: **Statistics → Conversations / Endpoints / Protocol Hierarchy** first (who talked to whom, how much), **Statistics → HTTP → Requests**, **File → Export Objects → HTTP/SMB/TFTP/DICOM** to pull transferred files, **Follow → TCP/TLS stream**, **Analyze → Decode As** for odd ports, colouring rules, and `frame.time >= "2026-09-20 03:00:00"` as a display filter. Add columns by right-click on a field → *Apply as Column* (`http.host`, `tls.handshake.extensions_server_name`, `dns.qry.name`). **Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret log filename** decrypts TLS when you captured `SSLKEYLOGFILE` from the endpoint. + +## Team-scale review: Velociraptor and Timesketch + +- **Velociraptor** (Rapid7, free): a web GUI server + agents; the *Artifacts* are VQL queries for every Windows/Linux/macOS artefact (`Windows.KapeFiles.Targets` runs KAPE targets remotely, `Windows.EventLogs.Hayabusa`, `Linux.Sys.*`, `Generic.Forensic.Timeline`). **Hunts** run an artifact across the fleet and return one table; **Notebooks** let you query results with VQL; **Offline Collector** builds a standalone triage `.exe` for boxes without an agent. When you have more than a handful of hosts, this replaces walking around with KAPE. +- **Timesketch** (Google, free, Docker): upload plaso `.plaso` files or CSV/JSONL timelines → a web timeline multiple analysts search (`data_type:"windows:evtx:record" AND event_identifier:4624`), tag, star, comment and build **stories** on. Sigma rules can be run over it; **Timesketch + plaso** is the Linux-side answer to Timeline Explorer for a whole team. `psort.py -o timesketch` or the web upload feeds it. + +## Which GUI for which job + +| Job | Tool | +|---|---| +| Browse an image, export files, look at deleted ones, run keyword/hash/YARA over everything | **Autopsy** (or FTK Imager for browse/export only) | +| Get `$MFT`, hives, `$J` out of an image or a live box without mounting | **FTK Imager** | +| Mount an image so KAPE / AV / EZ Tools see a drive letter (with VSS) | **Arsenal Image Mounter** | +| Read/parse registry with decoded plugins, deleted keys, transaction logs | **Registry Explorer** | +| Review any parser CSV, filter, tag, colour, export the report table | **Timeline Explorer** | +| Folders a user browsed | **ShellBags Explorer** | +| Show a timestomp or resident data in a screenshot | **MFTExplorer** | +| Open an evidence file safely | **EZViewer** | +| Read many event logs as one, quick | **Event Log Explorer** / **Hayabusa HTML** | +| Pcap | **Wireshark** | +| Media wall, faces, GPS | **Autopsy Image Gallery / Geolocation** | +| Many hosts, one console | **Velociraptor** | +| Team timeline review | **Timesketch** | +| Hex + structure decoding | **ImHex** / **HxD** / **010 Editor** | + +## Related + +- [EZ Tools & KAPE](/sheets/dfir/ez-tools-kape) — the CLI parsers behind Registry/Timeline/ShellBags Explorer +- [RECmd](/sheets/dfir/recmd) — Registry Explorer's command-line twin +- [Acquisition](/sheets/dfir/disk-imaging) — FTK Imager / Guymager imaging, mounting +- [Linux forensics](/sheets/dfir/linux-forensics) — Sleuth Kit CLI that Autopsy wraps, plaso for Timesketch +- [TShark](/sheets/dfir/tshark) — Wireshark's CLI diff --git a/src/content/sheets/dfir/disk-imaging.md b/src/content/sheets/dfir/disk-imaging.md @@ -0,0 +1,336 @@ +--- +title: "Disk & Memory Acquisition" +description: "Forensically sound imaging on Linux and Windows: order of volatility, write blocking, dd/dc3dd/ewfacquire, FTK Imager, WinPmem/DumpIt/AVML/LiME memory capture, hashing, chain of custody and mounting images read-only for analysis." +category: dfir +subcategory: "Acquisition" +tags: [dfir, forensics, acquisition, imaging, memory-forensics, chain-of-custody, e01, dd, windows, linux] +tools: ["dd / dcfldd / dc3dd", "ewfacquire (libewf)", "FTK Imager", "Guymager", "WinPmem", "DumpIt", "AVML", "LiME", "ewfmount / xmount", "Arsenal Image Mounter"] +difficulty: intermediate +updated: "2026-09-26" +--- + +# Disk & Memory Acquisition + +Everything downstream — timelines, registry parsing, carving, memory analysis — is only as good as the image it runs on. Acquisition is the one phase you cannot redo: a live box changes every second and a powered-off box loses its RAM forever. This card is the **order of operations**, the exact **`dd` / `ewfacquire` / FTK Imager** invocations, the **memory capture** tools per OS, and how to **hash, document and mount** the result so the analysis sheets ([EZ Tools](/sheets/dfir/ez-tools-kape), [Linux forensics](/sheets/dfir/linux-forensics), [Volatility](/sheets/dfir/volatility)) have something trustworthy to chew on. + +> [!warning] Two rules that never bend +> 1. **Never write to the evidence.** Hardware write blocker on the suspect drive, or software blocking (`blockdev --setro`, Windows `diskpart` read-only attribute, FTK Imager's own protection) when you must go software-only. +> 2. **Hash before, hash after, write it down.** An image without a matching acquisition hash is a file, not evidence. + +## Order of volatility (RFC 3227) + +Collect what disappears fastest first. Every step below the line you are on is destroying evidence above it. + +| Priority | Evidence | Lifetime | Tool | +|---|---|---|---| +| 1 | Registers, CPU cache | nanoseconds | (not practically collectable) | +| 2 | **RAM**, routing/ARP tables, process table, kernel state | until reboot | WinPmem, DumpIt, AVML, LiME | +| 3 | Network connections, logged-in users, open files | seconds–minutes | `netstat`, `ss`, `w`, `lsof`, `Get-NetTCPConnection` | +| 4 | Temp files, swap/pagefile, hiberfil | until overwritten | included in disk image; grab `pagefile.sys`/`hiberfil.sys` explicitly | +| 5 | **Disk** | until overwritten | `dd`, `ewfacquire`, FTK Imager | +| 6 | Remote logs, SIEM, backups | retention policy | export from source | +| 7 | Physical configuration, topology | long | photos, notes | + +> [!tip] Pull the plug or shut down? +> **Pull the plug** (hard power-off) preserves the disk as-is, including `hiberfil.sys`/`pagefile.sys`, and defeats shutdown-triggered anti-forensics. **Graceful shutdown** flushes caches and may fire a wiper. Default to: capture RAM live → pull the plug → image the disk. Exceptions: encrypted volumes you can only read while unlocked (image them **live**, or capture the key from RAM first), and servers where downtime is not yours to decide. + +## Documentation & chain of custody + +Start the log before the first command. Every image needs: + +- Case ID, examiner, date/time (**with timezone** — record the suspect machine's clock skew against a reference clock too) +- Device: make, model, serial, capacity, interface, physical condition, photos of labels +- Acquisition method, tool + version, write-blocker used +- **Source hash, image hash, verification result** +- Every hand-off: who, when, why, signed + +```bash +# Capture the timestamp and clock skew at the very start +date -u; hwclock -r 2>/dev/null # on the suspect box (Linux) +# Windows: +# w32tm /stripchart /computer:pool.ntp.org /samples:3 /dataonly +``` + +## Live response (before you power off) + +Only do this when the RAM capture is done (or you cannot capture RAM). Run tools **from your own media**, redirect output to your own media, and know that every command you run leaves artefacts — log what you ran so the analyst (probably you) can exclude them later. + +```bash +# Linux — volatile state to an evidence mount +E=/mnt/evidence/$(hostname)-$(date -u +%Y%m%dT%H%MZ); mkdir -p "$E" +date -u > "$E/date.txt" +uptime > "$E/uptime.txt" +w; last -F > "$E/logins.txt" +ps auxwwf > "$E/ps.txt" +ss -tulpan > "$E/net.txt" +ip a; ip r; arp -an > "$E/ip.txt" +lsof -nP > "$E/lsof.txt" +mount; df -h > "$E/mounts.txt" +lsmod > "$E/lsmod.txt" +cat /proc/mounts /proc/partitions > "$E/proc.txt" +``` + +```powershell +# Windows — same idea, PowerShell (run from your USB, output to it) +$E="E:\evidence\$env:COMPUTERNAME-$(Get-Date -f yyyyMMddTHHmm)"; mkdir $E | Out-Null +Get-Date -Format o > "$E\date.txt" +Get-Process -IncludeUserName | Sort StartTime > "$E\ps.txt" +Get-CimInstance Win32_Process | Select ProcessId,ParentProcessId,CommandLine | Export-Csv "$E\cmdlines.csv" +Get-NetTCPConnection -State Established,Listen | Export-Csv "$E\net.csv" +Get-NetNeighbor; Get-NetIPAddress; Get-DnsClientCache | Out-File "$E\netcfg.txt" +query user; net session; net use > "$E\sessions.txt" +Get-ScheduledTask | Export-Csv "$E\tasks.csv" +Get-Service | Export-Csv "$E\services.csv" +Get-SmbOpenFile; Get-SmbSession > "$E\smb.txt" +``` + +## Memory capture + +### Windows + +| Tool | Notes | Command | +|---|---|---| +| **WinPmem** (Velocidex) | Open source, signed driver, raw or AFF4 output. The default choice. | `winpmem_mini_x64_rc2.exe C:\evidence\mem.raw` | +| **DumpIt** (Magnet) | Single exe, one prompt, produces `.dmp` (Vol3 reads it). Good for junior responders. | `DumpIt.exe /OUTPUT E:\mem.dmp /QUIET` | +| **Magnet RAM Capture** | GUI, free, handles Secure Boot / VSM boxes that stop other drivers | click Start | +| **Belkasoft RAM Capturer** | GUI, free, low footprint | pick output dir, Capture | +| **FTK Imager** | File → Capture Memory; can also grab `pagefile.sys` in the same pass | GUI | +| **KAPE** | `--target MemoryFiles` collects hiberfil/pagefile/swapfile; use a `!SANS_Triage`-style target after RAM capture | see [EZ Tools](/sheets/dfir/ez-tools-kape) | + +```powershell +# WinPmem, raw image, then hash it +.\winpmem_mini_x64_rc2.exe E:\evidence\mem.raw +Get-FileHash E:\evidence\mem.raw -Algorithm SHA256 | Tee-Object E:\evidence\mem.raw.sha256 + +# Also copy the swap/hibernation files for Volatility / Hibr2Bin +# (locked while running — use FTK Imager's "Obtain Protected Files" or KAPE MemoryFiles) +``` + +> [!info] Size and time +> RAM image ≈ physical RAM (16 GB box → 16 GB file, 2–5 min over USB 3). Write to **external** media, never the suspect disk. If Volatility later fails to find symbols, capture `C:\Windows\System32\ntoskrnl.exe` too — Vol3 uses its PDB GUID to fetch the right symbol pack. + +### Linux + +| Tool | Notes | Command | +|---|---|---| +| **AVML** (Microsoft) | Static binary, **no kernel module**, works on most kernels via `/proc/kcore` or `/dev/crash`. First choice. | `./avml mem.lime` | +| **LiME** | Kernel module, must be built against the target's exact kernel headers. Output format Vol3 reads natively. | `insmod lime-$(uname -r).ko "path=/mnt/evidence/mem.lime format=lime"` | +| **/proc/kcore** via `dd` | Last resort, huge, not a clean physical map | avoid | + +```bash +# AVML — compress on the fly to your evidence mount +./avml --compress /mnt/evidence/mem.lime.compressed +# or plain +./avml /mnt/evidence/mem.lime && sha256sum /mnt/evidence/mem.lime > /mnt/evidence/mem.lime.sha256 + +# LiME — build on a matching kernel, never on the suspect box if you can avoid it +git clone https://github.com/504ensicsLabs/LiME && cd LiME/src && make +sudo insmod ./lime-$(uname -r).ko "path=/mnt/evidence/mem.lime format=lime" +sudo rmmod lime + +# For Volatility 3 you will need a symbol table for this kernel: +# dwarf2json linux --elf /usr/lib/debug/boot/vmlinux-$(uname -r) > $(uname -r).json +``` + +> [!tip] macOS +> Modern Apple silicon makes live RAM capture impractical without vendor tooling. Collect volatile state with `sysdiagnose`, `ps`, `lsof`, `nettop`, and unified logs (`log collect --output case.logarchive`), then image the disk from Recovery / target disk mode. + +## Disk imaging + +### Identify the device first (and do not mount it) + +```bash +lsblk -o NAME,SIZE,MODEL,SERIAL,TYPE,MOUNTPOINT,FSTYPE +sudo fdisk -l /dev/sdb +sudo smartctl -i /dev/sdb # serial, firmware, power-on hours — into the log +sudo hdparm -I /dev/sdb | head -20 +sudo blockdev --setro /dev/sdb # software write-block if no hardware blocker +sudo blockdev --getro /dev/sdb # → 1 +# Stop the desktop from auto-mounting removable media before you plug in: +# gsettings set org.gnome.desktop.media-handling automount false +``` + +```powershell +# Windows — find the disk and make it read-only in diskpart (no hardware blocker) +Get-Disk | ft Number,FriendlyName,SerialNumber,Size,PartitionStyle +diskpart +# DISKPART> select disk 2 +# DISKPART> attributes disk set readonly +# DISKPART> attributes disk (verify "Read-only: Yes") +``` + +### `dd` family (raw images) + +```bash +# Baseline hash of the source (slow, but the gold standard) +sudo sha256sum /dev/sdb | tee /mnt/evidence/sdb.source.sha256 + +# Plain dd — works everywhere, no progress, no error handling +sudo dd if=/dev/sdb of=/mnt/evidence/sdb.dd bs=4M conv=noerror,sync status=progress + +# dcfldd — hashes while imaging, splits, verifies +sudo dcfldd if=/dev/sdb of=/mnt/evidence/sdb.dd bs=4M \ + hash=sha256 hashlog=/mnt/evidence/sdb.dd.hash \ + conv=noerror,sync statusinterval=256 + +# dc3dd — the DoD successor to dcfldd, best error handling, logs everything +sudo dc3dd if=/dev/sdb of=/mnt/evidence/sdb.dd \ + hash=sha256 hash=md5 log=/mnt/evidence/sdb.dd.log \ + rec=on # keep going on bad sectors, zero-fill them + +# Split raw image into 2 GB chunks (FAT32 evidence drive, or just manageability) +sudo dc3dd if=/dev/sdb ofs=/mnt/evidence/sdb.dd.000 ofsz=2G hash=sha256 log=sdb.log + +# Verify: image hash must equal source hash +sha256sum /mnt/evidence/sdb.dd +``` + +> [!warning] `conv=noerror,sync` matters +> Without `noerror` a single bad sector aborts the image. Without `sync` the bad block is skipped instead of zero-padded, and **every offset after it shifts**, breaking filesystem parsing. Use both, and record the bad-sector list from the log. + +### E01 / EWF (compressed, hashed, metadata-carrying) + +The Expert Witness Format is what commercial suites (EnCase, FTK, X-Ways) expect, compresses well, embeds the case notes and checksums every chunk. Prefer it over raw when the image will be shared. + +```bash +sudo apt install libewf-tools ewf-tools # ewfacquire, ewfverify, ewfmount, ewfinfo + +# Interactive (asks for case number, examiner, notes, compression, segment size) +sudo ewfacquire /dev/sdb + +# Unattended +sudo ewfacquire -u -t /mnt/evidence/sdb -C CASE-042 -E 001 -e "DAEMON" \ + -D "Laptop SSD, Samsung 970, S/N ..." -N "Seized 2026-09-26 room 3" \ + -c best -S 4G -d sha256 -f encase6 /dev/sdb + +ewfinfo /mnt/evidence/sdb.E01 # metadata + stored hashes +ewfverify /mnt/evidence/sdb.E01 # recompute and compare +ewfexport -t /mnt/evidence/sdb.raw /mnt/evidence/sdb.E01 # back to raw when a tool needs it +``` + +### Guymager (Linux GUI) + +Pre-installed on SIFT/Kali/Tsurugi. Right-click the device → **Acquire image** → pick `.dd` or `.E01`, fill case fields, tick **Calculate SHA-256** and **Verify image after acquisition**. It logs to a `.info` file next to the image. Fast, multithreaded, and the sanest option when a colleague has never used `dd`. + +### FTK Imager (Windows GUI + CLI) + +The most common Windows imager. Free, runs from a USB ("FTK Imager Lite"), no install. + +**GUI:** File → **Create Disk Image** → Physical Drive → select the suspect disk → Add destination → choose **E01** (or Raw/dd) → fill Evidence Item Information → set fragment size (0 = single file) and compression (6 default) → tick **Verify images after they are created** → Start. The summary window at the end has the MD5/SHA1 — screenshot it and save the `.txt` log it writes next to the image. + +Other jobs FTK Imager does that nothing else free does as easily: + +| Task | Menu | +|---|---| +| Copy locked live files (`$MFT`, hives, `pagefile.sys`, `NTUSER.DAT`) from a running box | File → **Obtain Protected Files** → tick *Password recovery and all registry files* | +| Export a folder tree from an image with timestamps intact | File → Add Evidence Item → browse → right-click → **Export Files** | +| Mount an E01 as a drive letter | File → **Image Mounting** → Physical & Logical, Read-only → Mount | +| Capture RAM | File → **Capture Memory** (tick *Include pagefile*) | +| Custom content image (only the paths you list, still hashed E01) | File → Create Disk Image → **Contents of a Folder**, or add to Custom Content Sources | +| View file slack, unallocated, and ADS | Evidence tree → `[unallocated space]`, hex pane bottom right | + +```bat +:: FTK Imager CLI (ftkimager.exe, separate download) — image with verification +ftkimager.exe \\.\PhysicalDrive2 E:\evidence\pd2 --e01 --frag 4G --compress 6 ^ + --case-number CASE-042 --evidence-number 001 --examiner DAEMON ^ + --description "Dell laptop SSD" --verify + +:: List physical drives +ftkimager.exe --list-drives +``` + +### Live imaging (encrypted / cannot power off) + +When BitLocker/LUKS/FileVault means a dead image is ciphertext, image the **logical, unlocked volume** while it is mounted, and accept that the image is not a perfect snapshot. + +```bash +# Linux LUKS — image the mapped device, not the raw partition +sudo dc3dd if=/dev/mapper/luks-xxxx of=/mnt/evidence/root-decrypted.dd hash=sha256 log=root.log +``` + +```bat +:: Windows BitLocker — logical drive letter, plus grab the recovery key while you can +manage-bde -protectors -get C: > E:\evidence\bitlocker-keys.txt +ftkimager.exe C: E:\evidence\C-logical --e01 --verify +``` + +> [!tip] Cloud and virtual machines +> VMs: snapshot, then copy the `.vmdk`/`.vhdx`/`.qcow2` and the `.vmem`/`.vmsn` (that *is* the memory image). `qemu-img convert -f vmdk -O raw disk.vmdk disk.raw` gets you something every tool reads. Cloud: snapshot the volume (AWS `create-snapshot`, Azure `az snapshot create`), attach to a forensic instance in the same region, image from there — do not download 500 GB over the internet. + +## Hashing & verification + +```bash +# Multiple algorithms in one pass (GNU coreutils 9+ or via hashdeep) +hashdeep -c md5,sha256 -e /mnt/evidence/sdb.dd > sdb.dd.hashes +# Verify later against that file +hashdeep -c md5,sha256 -a -k sdb.dd.hashes /mnt/evidence/sdb.dd + +# Hash a split image as one stream +cat sdb.dd.0* | sha256sum + +# Hash every file inside a mounted image (for a known-good/bad comparison later) +find /mnt/img -type f -exec sha256sum {} + > case-filehashes.txt +``` + +```powershell +Get-FileHash E:\evidence\pd2.E01 -Algorithm SHA256 +certutil -hashfile E:\evidence\pd2.E01 SHA256 # no PowerShell needed +``` + +> [!info] MD5 is fine here +> MD5 collisions are a real cryptographic weakness but irrelevant to proving an image was not altered by accident. Courts and tools still use MD5+SHA1; adding SHA-256 costs nothing. Record all of them. + +## Mounting images for analysis (read-only, always) + +```bash +# Raw image: find partition offsets, then mount one +sudo mmls sdb.dd # Sleuth Kit: sector offsets of each partition +sudo fdisk -l sdb.dd +sudo mount -o ro,loop,noexec,noload,offset=$((2048*512)) sdb.dd /mnt/img # ext4 (noload = don't replay journal) +sudo mount -o ro,loop,noexec,show_sys_files,streams_interface=windows,offset=$((1050624*512)) sdb.dd /mnt/img # NTFS via ntfs-3g, exposes $MFT and ADS +# Whole disk as block devices +sudo losetup -rfP --show sdb.dd # → /dev/loop0, /dev/loop0p1, ... (-r read-only, -P scan partitions) +sudo mount -o ro,noexec /dev/loop0p2 /mnt/img +sudo losetup -d /dev/loop0 + +# E01: expose as raw first +sudo mkdir -p /mnt/ewf && sudo ewfmount sdb.E01 /mnt/ewf # → /mnt/ewf/ewf1 (raw view) +sudo losetup -rfP --show /mnt/ewf/ewf1 +# or xmount, which also fakes a writable overlay (cache file) so tools that insist on writing work +sudo xmount --in ewf sdb.E01 --out raw --cache /tmp/sdb.cache /mnt/xm + +# LVM inside an image +sudo losetup -rfP --show sdb.dd && sudo vgscan && sudo vgchange -ay && lsblk +# Windows VSS shadow copies inside an NTFS image +sudo vshadowinfo /dev/loop0p2 && sudo vshadowmount /dev/loop0p2 /mnt/vss && ls /mnt/vss # vss1, vss2 ... +``` + +```powershell +# Windows: Arsenal Image Mounter (free, handles E01/raw/VHD, true disk-level mount so +# VSS and BitLocker-unlocked volumes behave). Mount → pick image → "Read only" → OK. +# FTK Imager: File → Image Mounting → Mount Type "Physical & Logical", Mount Method "Block Device / Read Only". +# Native for VHD/VHDX only: +Mount-DiskImage -ImagePath E:\evidence\disk.vhdx -Access ReadOnly +``` + +> [!warning] `ro` is not a write blocker +> A read-only mount stops *you* writing; it does not stop a journaling filesystem replaying its journal on mount (hence `noload` on ext4), and it does not stop macOS Spotlight or Windows indexing from touching an image file you open in a GUI. Analyse **copies** of the image, keep the original hashed on a shelf. + +## Quick decision table + +| Situation | Do | +|---|---| +| Running Windows workstation, suspected malware | RAM (WinPmem) → protected files (FTK Imager) → pull plug → E01 of disk | +| Running Linux server, cannot reboot | AVML → live response script → `dc3dd` of `/dev/mapper/*` if encrypted, else schedule downtime for a dead image | +| Powered-off laptop, unknown encryption | Do **not** boot it. Pull drive, hardware blocker, E01. Check `ewfinfo`/`mmls` for BitLocker signatures; hunt for recovery key in the user's cloud account or AD `msFVE-RecoveryInformation` | +| USB stick / SD card | Blocker, `dc3dd`, then [carve](/sheets/dfir/strings-file-triage) — small media are mostly deleted files | +| Phone | Different discipline entirely (Cellebrite/GrayKey/ALEAPP/iLEAPP); at minimum: airplane mode, Faraday bag, note lock state | +| VM | Snapshot, copy `.vmdk` + `.vmem`, convert with `qemu-img` | + +## Next steps + +- Windows image → [EZ Tools & KAPE](/sheets/dfir/ez-tools-kape) for parsing, [Digital Forensics reference](/sheets/dfir/forensics) for what to look for, [Registry (RECmd)](/sheets/dfir/recmd) +- Linux image → [Linux forensics](/sheets/dfir/linux-forensics) +- Memory image → [Volatility 3](/sheets/dfir/volatility) +- Unknown files, unallocated space → [strings & file triage](/sheets/dfir/strings-file-triage) +- Point-and-click review → [GUI tools](/sheets/dfir/dfir-gui-tools) diff --git a/src/content/sheets/dfir/ez-tools-kape.md b/src/content/sheets/dfir/ez-tools-kape.md @@ -0,0 +1,404 @@ +--- +title: "Eric Zimmerman Tools & KAPE" +description: "The EZ Tools suite end to end: Get-ZimmermanTools install, KAPE targets and modules for triage collection, every *Cmd parser (MFTECmd, PECmd, LECmd, JLECmd, EvtxECmd, AmcacheParser, AppCompatCacheParser, SBECmd, SrumECmd, WxTCmd, RBCmd, SQLECmd, bstrings) with the artefact and the question it answers, then CSV into Timeline Explorer." +category: dfir +subcategory: "Windows" +tags: [dfir, forensics, windows, ez-tools, kape, triage, timeline, mft, prefetch, evtx, amcache, shimcache, shellbags, lnk, jumplist, srum, registry] +tools: ["KAPE / gkape", "Get-ZimmermanTools", "MFTECmd", "PECmd", "LECmd", "JLECmd", "EvtxECmd", "AmcacheParser", "AppCompatCacheParser", "SBECmd / ShellBags Explorer", "SrumECmd", "WxTCmd", "RBCmd", "SQLECmd", "bstrings", "Timeline Explorer", "Registry Explorer / RECmd", "EZViewer", "MFTExplorer", "Hasher"] +difficulty: advanced +updated: "2026-09-26" +--- + +# Eric Zimmerman Tools & KAPE + +**Eric Zimmerman's tools** ("EZ Tools") are the free, open-source parsers that SANS FOR500 and most DFIR modules are built on: one small command-line tool per Windows artefact, all producing the same flat **CSV** that drops straight into **Timeline Explorer**. **KAPE** (Kroll Artifact Parser and Extractor) is the collector and orchestrator that sits in front of them: it copies the artefacts off a live box or mounted image in minutes (*targets*), then runs the parsers over the copy (*modules*). Learn the pattern once — `tool -f <artefact> --csv <out>` — and you know the whole suite. This card is the install, the KAPE triage command you will run on every case, then each parser with **what it reads** and **what question it answers**. + +> [!info] Where they come from +> Everything here is at [ericzimmerman.github.io](https://ericzimmerman.github.io/#!index.md) (tools) and [kroll.com/kape](https://www.kroll.com/en/services/cyber-risk/incident-response-litigation-support/kroll-artifact-parser-extractor-kape) (KAPE, free for non-commercial / internal use, registration required). Builds ship for **.NET 6** and **.NET 9**; grab the one matching the runtime on your analysis box. The `.NET 4` builds are gone. + +## Install and update + +```powershell +# One script pulls every tool (and keeps them updated — rerun it monthly) +Set-ExecutionPolicy Bypass -Scope Process +iwr https://raw.githubusercontent.com/EricZimmerman/Get-ZimmermanTools/master/Get-ZimmermanTools.ps1 -OutFile Get-ZimmermanTools.ps1 +.\Get-ZimmermanTools.ps1 -Dest C:\Tools\EZ -NetVersion 9 # or -NetVersion 6 + +# Runtime the tools need +winget install Microsoft.DotNet.DesktopRuntime.9 # GUI tools (Registry/Timeline/ShellBags Explorer) +winget install Microsoft.DotNet.Runtime.9 # CLI tools + +# KAPE lives separately (download from Kroll, unzip anywhere — it is portable) +# C:\Tools\KAPE\kape.exe CLI +# C:\Tools\KAPE\gkape.exe GUI that builds the CLI command for you +# Point KAPE at the EZ tools once: copy them into C:\Tools\KAPE\Modules\bin\ +Copy-Item C:\Tools\EZ\net9\*.exe C:\Tools\KAPE\Modules\bin\ -Force +# Update KAPE's targets/modules from the community repo +C:\Tools\KAPE\kape.exe --sync +``` + +Add `C:\Tools\EZ\net9` to `PATH` and every example below works as written. Every tool answers `--help`; the `--csv` output flag and `-q` (quiet) are common to almost all. + +> [!tip] Linux / macOS analyst box +> The CLI tools run under the Linux .NET runtime too (`dotnet MFTECmd.dll ...`, or the native builds the script fetches with `-NetVersion 9`). The GUI tools (Timeline Explorer, Registry Explorer) are Windows-only — keep a Windows VM for those, or use `xsv`/`qsv`/`visidata` on the CSVs. + +## The mental model + +```text + collect parse review + live box ──► KAPE --target ──► copy of artefacts ──► KAPE --module / *Cmd ──► CSV ──► Timeline Explorer + or image (files only, (tree mirrors C:\, (MFTECmd, PECmd, ...) (one per artefact) (filter, tag, + timestamps kept) or a VHDX) pivot on time) +``` + +| Layer | Tool | Rule of thumb | +|---|---|---| +| Collection | KAPE **targets** (`.tkape`) | *What files to copy.* Compound targets like `!SANS_Triage`, `KapeTriage`, `!BasicCollection` bundle dozens of single targets | +| Processing | KAPE **modules** (`.mkape`) | *What command to run over them.* `!EZParser` runs every EZ tool; other modules wrap Hayabusa, Chainsaw, Plaso, hindsight, etc. | +| Parser | the `*Cmd` tools | One artefact type each, all → CSV/JSON | +| Review | Timeline Explorer | The CSV viewer that understands the EZ column names (timestamps sort, colour, group) | + +## KAPE + +### The triage command you will actually run + +```bat +:: Live box, collecting from C:, output to your USB (E:), then parse everything +kape.exe --tsource C: --tdest E:\case042\tout --target !SANS_Triage --tflush ^ + --msource E:\case042\tout --mdest E:\case042\mout --module !EZParser --mflush ^ + --gui + +:: Same, but pack the collection into a VHDX (one file, keeps ACLs/timestamps, mounts anywhere) +kape.exe --tsource C: --tdest E:\case042\tout --target KapeTriage --vhdx HOST-042 --zv false + +:: From a mounted image instead of a live box (F: is the mounted E01 / Arsenal Image Mounter letter) +kape.exe --tsource F: --tdest E:\case042\tout --target !SANS_Triage --tflush + +:: Only parse an existing collection (you already ran the target, or someone sent you a triage zip) +kape.exe --msource E:\case042\tout --mdest E:\case042\mout --module !EZParser --mflush +``` + +| Flag | Meaning | +|---|---| +| `--tsource` / `--tdest` | source drive/dir to collect **from**, dir to copy **into** | +| `--target X` | one or more targets, comma separated. `!` prefix = compound | +| `--tflush` | empty `--tdest` first | +| `--vhdx NAME` / `--vhd` / `--zip` | container for the collection instead of a loose tree | +| `--vss` | also pull the same paths out of every Volume Shadow Copy (deleted/overwritten artefacts) | +| `--tdd` | *target de-dupe* on hash when using `--vss` | +| `--msource` / `--mdest` | dir to parse, dir for parser output | +| `--module X` | modules to run; `!EZParser` = all EZ tools | +| `--mef csv\|json\|html` | override module output format | +| `--mvars key:val` | pass variables modules need (e.g. `--mvars computerName:HOST01`) | +| `--gui` | open a window showing progress, leave console log | +| `--debug` / `--trace` | verbose logging | +| `--sync` | update targets/modules from GitHub (KapeFiles repo) | +| `--tlist` / `--mlist` | list available targets / modules (`--tdetail` / `--mdetail` for contents) | + +> [!warning] Footprint and what KAPE is not +> KAPE running on a live box **creates artefacts** (prefetch entry, `kape.exe` in Amcache/ShimCache, a ConsoleHost history line). Note the time you started it and exclude it in the timeline. It also pulls locked files (`$MFT`, hives, `$UsnJrnl`) through raw disk reads — it is not a bit-for-bit image and never will be; if you need one, [image first](/sheets/dfir/disk-imaging). + +### Targets worth knowing + +| Target | Collects | +|---|---| +| `!SANS_Triage` | The FOR500/FOR508 set: `$MFT`, `$LogFile`, `$UsnJrnl`, registry hives + transaction logs, event logs, Prefetch, Amcache, LNK, Jump Lists, SRUM, WMI repository, scheduled tasks, PowerShell history, browser data, Recycle Bin, `$Extend` ... the 90% case | +| `KapeTriage` | Similar scope, tuned for speed on IR engagements | +| `!BasicCollection` | Lighter: hives, evtx, Prefetch, `$MFT`, LNK, Jump Lists | +| `RegistryHives`, `EventLogs`, `Prefetch`, `LNKFilesAndJumpLists`, `Amcache`, `SRUM`, `WebBrowsers`, `PowerShellConsole`, `ScheduledTasks`, `WMI`, `RecycleBin`, `WindowsTimeline`, `SUM`, `Antivirus`, `RDPLogs`, `$MFT`, `$J`, `$LogFile`, `MemoryFiles` | single-purpose, mix as needed | +| `ServerTriage`, `Exchange`, `IIS`, `MSSQL` | server roles | +| `RemoteAdminTools` | AnyDesk, TeamViewer, ScreenConnect, Atera ... logs (ransomware cases) | + +```bat +kape.exe --tlist :: everything +kape.exe --tdetail !SANS_Triage :: exactly which .tkape files a compound target includes +type "C:\Tools\KAPE\Targets\Compound\!SANS_Triage.tkape" +``` + +### Modules worth knowing + +| Module | Runs | +|---|---| +| `!EZParser` | every EZ CLI tool over the matching artefact, one CSV each in `mout\` sub-folders | +| `!EZParser` output dirs | `FileSystem\` (MFTECmd), `ProgramExecution\` (PECmd, Amcache, ShimCache), `EventLogs\` (EvtxECmd), `Registry\` (RECmd batch), `FileFolderAccess\` (LECmd, JLECmd, SBECmd), `SRUMDatabase\`, `WindowsTimeline\`, `RecycleBin\` ... | +| `Hayabusa`, `Chainsaw` | Sigma rule detection over the collected `.evtx` | +| `Plaso_Timeline`, `MFTECmd_$MFT_Bodyfile` → `mactime` | full super-timeline | +| `hindsight`, `BrowsingHistoryView` | browser history | +| `Hasher`, `Sysinternals_Sigcheck`, `DensityScout` | hashing / sig / entropy over collected binaries | +| `LogParser`, `EvtxECmd_RDP` | targeted event-log queries | + +### gkape (GUI) + +Tick *Use Target options*, pick source/destination, tick targets in the tree (search box top right), tick *Use Module options*, pick `!EZParser`, and read the **command line box at the bottom** — that is the exact CLI it will run. Copy it into your notes; gkape is a command builder, nothing more. + +## The parsers, one artefact at a time + +Every EZ CLI tool accepts `-f <file>` or `-d <directory>`, `--csv <outdir>` (add `--csvf name.csv` to fix the filename), `--json <outdir>`, `-q` to suppress per-record console output, `--dt "yyyy-MM-dd HH:mm:ss.fffffff"` to change the timestamp format, and `--debug`. Timestamps are **UTC** unless you pass `--tz`. Paths below assume a KAPE collection under `E:\case042\tout\C\`. + +### MFTECmd — `$MFT`, `$J` (USN journal), `$LogFile`, `$Boot`, `$SDS` + +*Every file that ever existed (until the record is reused), with all eight timestamps.* The backbone of any timeline. + +```bat +:: Parse the MFT — one row per file/dir, $STANDARD_INFORMATION and $FILE_NAME timestamps side by side +MFTECmd.exe -f "E:\case042\tout\C\$MFT" --csv E:\case042\mout\mft --csvf mft.csv + +:: Include the $FILE_NAME-only timestamps too (timestomp detection) and the resident data / ADS +MFTECmd.exe -f "$MFT" --csv out --fl --at + +:: Bodyfile for mactime / plaso (drive letter for the paths) +MFTECmd.exe -f "$MFT" --body out --bodyf mft.body --bdl C + +:: One entry in full detail (entry number 0x1A5 or decimal), including data runs +MFTECmd.exe -f "$MFT" --de 421 +MFTECmd.exe -f "$MFT" --de 421 --dr + +:: USN journal: what happened to files (create/delete/rename/overwrite) with the MFT to resolve full paths +MFTECmd.exe -f "E:\case042\tout\C\$Extend\$J" -m "E:\case042\tout\C\$MFT" --csv out --csvf usn.csv + +:: $LogFile (NTFS transaction log) and $Boot / $SDS (security descriptors) +MFTECmd.exe -f "$LogFile" --csv out +MFTECmd.exe -f "$Boot" --csv out +MFTECmd.exe -f "$SDS" --csv out + +:: Pull from volume shadow copies too (needs --vss and admin, live or mounted image) +MFTECmd.exe -f "F:\$MFT" --csv out --vss +``` + +> [!tip] Reading the MFT CSV in Timeline Explorer +> `Created0x10` / `LastModified0x10` etc. are `$STANDARD_INFORMATION` (what Explorer shows, **user-space tools can change these**). `Created0x30` etc. are `$FILE_NAME` (kernel-set, much harder to fake). Filter `SI<FN` = **true** to find files whose visible timestamps predate their filename record — classic **timestomping**. `InUse` = false → deleted but record not yet reused. `HasAds` → alternate data streams; `ZoneIdContents` gives the download URL from `Zone.Identifier`. + +### PECmd — Prefetch (`C:\Windows\Prefetch\*.pf`) + +*Which executables ran, when (last 8 run times on Win8+), how many times, and which files/volumes they touched in the first ~10 s.* + +```bat +PECmd.exe -d "E:\case042\tout\C\Windows\Prefetch" --csv out -q +PECmd.exe -f "E:\case042\tout\C\Windows\Prefetch\MIMIKATZ.EXE-1A2B3C4D.pf" +PECmd.exe -d Prefetch --csv out -k "temp,appdata,downloads,users\\public" :: highlight rows whose loaded files match keywords +PECmd.exe -d Prefetch --csv out --vss :: shadow copies too +PECmd.exe -d Prefetch --json out +``` + +Output is two CSVs: `*_PECmd_Output.csv` (one row per `.pf`: exe, run count, last run + 7 previous, volume serials) and `*_PECmd_Output_Timeline.csv` (one row **per run**, ready to merge into a timeline). Hash in the filename changes with the path the exe ran from — two `CMD.EXE-xxxx.pf` files = cmd launched from two locations. Prefetch is **off by default on servers and SSD-tuned boxes**; check `HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\PrefetchParameters\EnablePrefetcher` before concluding "never ran". + +### LECmd — LNK shortcut files + +*Files the user opened (Recent), with the target's path, size, timestamps, volume serial, and the MAC address / hostname of the machine the LNK was made on.* + +```bat +LECmd.exe -d "E:\case042\tout\C\Users\bob\AppData\Roaming\Microsoft\Windows\Recent" --csv out -q +LECmd.exe -d "E:\case042\tout\C\Users" --csv out -q --all :: every .lnk under every profile (Desktop, Start Menu, ...) +LECmd.exe -f suspicious.lnk --mp :: more precise timestamps; also dumps the full structure +LECmd.exe -d Recent --csv out --neb :: include LNKs with no TargetIDList/ExtraBlocks (rare edge cases) +``` + +`TargetCreated`/`TargetModified`/`TargetAccessed` are the **target file's** timestamps at LNK creation; `SourceCreated/Modified` are the LNK's own = first/last time the file was opened. `MachineID`/`MacAddress` place a USB-carried LNK on a specific host. Phishing LNKs: check `Arguments` for `powershell -enc`, and `WorkingDirectory`. + +### JLECmd — Jump Lists (Automatic + Custom Destinations) + +*Per-application recent items — Word documents, RDP hosts (mstsc), Explorer folders — surviving longer than Recent LNKs.* + +```bat +JLECmd.exe -d "E:\case042\tout\C\Users\bob\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations" --csv out -q +JLECmd.exe -d "...\Recent\CustomDestinations" --csv out -q +JLECmd.exe -d Recent --csv out -q --ld :: include full LNK detail per entry +JLECmd.exe -d Recent --csv out -q --withDir :: dump the embedded LNKs to disk +JLECmd.exe -f "5f7b5f1e01b83767.automaticDestinations-ms" --fd :: full dump of one list +``` + +The filename hash is the **AppID**: `5f7b5f1e01b83767` = Quick Access, `9b9cdc69c1c24e2b` = Notepad, `f01b4d95cf55d32a` = Explorer, `1b4dd67f29cb1962` = Windows Explorer pinned. JLECmd resolves the known ones for you (`AppIdDescription` column). RDP jump list (`mstsc.exe`, `{...}`) lists every hostname/IP the user connected to. + +### EvtxECmd — Event logs (`.evtx`) + +*The normalised event log view: every EVTX in one CSV with a `MapDescription` and a `PayloadData1..6` extraction of the fields that matter (user, IP, process, service name), courtesy of community maps.* + +```bat +:: Update the maps first — they turn raw XML into "Logon: user X from IP Y" +EvtxECmd.exe --sync + +:: All logs from a collection +EvtxECmd.exe -d "E:\case042\tout\C\Windows\System32\winevt\Logs" --csv out --csvf evtx.csv + +:: Only the IDs you care about, in a date window +EvtxECmd.exe -d Logs --csv out --inc 4624,4625,4634,4648,4672,4688,4698,4720,4732,7045,1102 ^ + --sd "2026-09-20 00:00:00" --ed "2026-09-27 00:00:00" + +:: Exclude noise +EvtxECmd.exe -d Logs --csv out --exc 5156,5158,4689 + +:: Single log, JSON for jq / SIEM ingestion +EvtxECmd.exe -f Security.evtx --json out +EvtxECmd.exe -f Security.evtx --xml out :: original XML per event + +:: Live system (admin) +EvtxECmd.exe -d C:\Windows\System32\winevt\Logs --csv E:\case042\live-evtx --vss +``` + +Key columns: `TimeCreated`, `EventId`, `Channel`, `Computer`, `UserId`, `MapDescription`, `PayloadData1-6` (mapped fields), `RemoteHost`, `ExecutableInfo`, `Payload` (full XML). Sort by time, filter `MapDescription` — this is how you find the 4624 type 10 from the odd IP in minutes. The Event ID table lives in the [Digital Forensics reference](/sheets/dfir/forensics). For Sigma-rule detection over the same files use **Hayabusa** / **Chainsaw** (KAPE modules exist for both). + +### AmcacheParser — `Amcache.hve` + +*Every executable and driver the system inventoried — full path, **SHA-1 of the first 31 MB**, publisher, compile time, first-seen time. Survives the binary being deleted.* + +```bat +AmcacheParser.exe -f "E:\case042\tout\C\Windows\appcompat\Programs\Amcache.hve" --csv out +AmcacheParser.exe -f Amcache.hve --csv out -i :: include only unassociated file entries (executables not tied to an installed program) — the malware view +AmcacheParser.exe -f Amcache.hve --csv out -w known-good.txt :: whitelist of SHA-1s to drop +AmcacheParser.exe -f Amcache.hve --csv out -b iocs.txt :: blacklist: flag matches +``` + +Outputs several CSVs: `*_UnassociatedFileEntries` (the interesting one), `*_AssociatedFileEntries`, `*_ProgramEntries`, `*_DriverBinaries`, `*_ShortCuts`, `*_DeviceContainers`/`*_DevicePnps` (USB devices!). `FileKeyLastWriteTimestamp` ≈ first execution / first seen (not exact). Throw the SHA-1 column at VirusTotal. + +### AppCompatCacheParser — ShimCache (`SYSTEM` hive) + +*Executables that were **present** (and usually run) — path + `$SI` modification time, in insertion order. No run count, no run time, but it records binaries Prefetch never saw (servers).* + +```bat +AppCompatCacheParser.exe -f "E:\case042\tout\C\Windows\System32\config\SYSTEM" --csv out +AppCompatCacheParser.exe -f SYSTEM --csv out -t :: sort by insertion order (most recent first) — the "what ran last" view +AppCompatCacheParser.exe -f SYSTEM --csv out -c 1 :: specific ControlSet +AppCompatCacheParser.exe --csv out :: live system +``` + +`LastModifiedTimeUTC` is the file's **modification** time, not execution. `CacheEntryPosition` 0 = most recently inserted. Written to the hive only at **shutdown/reboot** — a live box's cache is in memory (Volatility `windows.shimcachemem`). `Executed` flag exists only on Win7/8 formats. + +### SBECmd / ShellBags Explorer — ShellBags (`NTUSER.DAT`, `UsrClass.dat`) + +*Folders the user browsed in Explorer — including on USB drives, network shares, zip files and paths that no longer exist — with first/last interaction times.* + +```bat +SBECmd.exe -d "E:\case042\tout\C\Users\bob" --csv out :: finds NTUSER.DAT + UsrClass.dat under the profile +SBECmd.exe -d "E:\case042\tout\C\Users" --csv out --dedupe :: all users +SBECmd.exe -l --csv out :: live (current user, admin for others) +SBECmd.exe -d Users\bob --csv out --nl :: don't replay transaction logs +``` + +**ShellBags Explorer** (`ShellBagsExplorer.exe`) is the GUI: File → Load offline hive (pick `UsrClass.dat`, then it asks for `NTUSER.DAT`), tree on the left mirrors the folder hierarchy the user saw. `AbsolutePath`, `FirstInteracted`, `LastInteracted`, `MFTEntryNumber`/`SequenceNumber` (pivot back into MFTECmd output). Look for `\\server\share`, `D:\`/`E:\` (removable), `This PC\...` under GUIDs, and folder names like `Tools`, `loot`, `exfil`. + +### SrumECmd — SRUM (`SRUDB.dat` + `SOFTWARE` hive) + +*Per-application network bytes sent/received, energy and CPU usage in hourly buckets for ~30–60 days. The exfiltration detector.* + +```bat +SrumECmd.exe -f "E:\case042\tout\C\Windows\System32\sru\SRUDB.dat" -r "E:\case042\tout\C\Windows\System32\config\SOFTWARE" --csv out +:: If the ESE database is dirty (collected live) repair it first: +esentutl.exe /r sru /i /d (run in a copy of the sru folder) +esentutl.exe /p SRUDB.dat +``` + +CSVs: `*_NetworkUsages` (app, user SID, interface, `BytesSent`/`BytesReceived`, hour), `*_NetworkConnections`, `*_AppResourceUseInfo`, `*_EnergyUsage`, `*_PushNotifications`, `*_vfuprov`. Sort `BytesSent` descending; a 4 GB upload by `rclone.exe` or `powershell.exe` at 03:00 is the story. + +### WxTCmd — Windows Timeline (`ActivitiesCache.db`) + +*App focus and file-open activity per user (Win10 1803 – 22H2), with start/end times and duration.* + +```bat +WxTCmd.exe -f "E:\case042\tout\C\Users\bob\AppData\Local\ConnectedDevicesPlatform\L.bob\ActivitiesCache.db" --csv out +``` + +Two CSVs: `*_Activity` (app, `StartTime`, `EndTime`, `Duration`, payload with the document path) and `*_Activity_PackageId`. Disabled by default on Win11. + +### RBCmd — Recycle Bin (`$Recycle.Bin\<SID>\$I*`) + +*Original path, size and deletion time of every file in the bin (the `$I` metadata file; `$R` is the content).* + +```bat +RBCmd.exe -d "E:\case042\tout\C\$Recycle.Bin" --csv out -q +RBCmd.exe -f "$IABC123.txt" +``` + +### SQLECmd — any SQLite (browsers, Teams, Slack, Windows Search, Signal ...) + +*Runs a library of community "maps" (queries) against every SQLite DB it recognises, producing a tidy CSV per map instead of you hand-writing SQL.* + +```bat +SQLECmd.exe --sync :: update maps +SQLECmd.exe -d "E:\case042\tout\C\Users" --csv out :: finds and parses everything it has a map for +SQLECmd.exe -f "History" --csv out :: Chrome/Edge history +SQLECmd.exe -f places.sqlite --csv out --dedupe :: Firefox +``` + +### bstrings — better `strings` + +*`strings` with regex, Unicode/UTF-16 aware, and built-in patterns for IPs, emails, URLs, GUIDs, MACs, credit cards, bitcoin addresses.* + +```bat +bstrings.exe -f pagefile.sys --lr ipv4 :: built-in regex: ipv4, ipv6, email, url, guid, mac, ssn, cc, b64, win_path, unc, reg_path, sid, aeon, bitcoin, xml +bstrings.exe -f mem.raw --ls "Invoke-Mimikatz" -q :: literal search +bstrings.exe -f mem.raw --lr "https?://[^\s\"']+" -m 8 -x 200 :: custom regex, min/max length +bstrings.exe -f hiberfil.sys --fs iocs.txt -o hits.txt :: file of strings to search for +bstrings.exe -d E:\case042\tout\C\Users\bob\Downloads --lr email --ro :: recurse a dir, --ro = show offsets +bstrings.exe -p :: list built-in patterns +``` + +The Linux/macOS counterpart is in [strings & file triage](/sheets/dfir/strings-file-triage). + +### The rest of the toolbox + +| Tool | Purpose | +|---|---| +| **RECmd** / **Registry Explorer** | registry hives, batch files, deleted keys, transaction log replay — [own sheet](/sheets/dfir/recmd) | +| **Timeline Explorer** | the CSV viewer — below and in [GUI tools](/sheets/dfir/dfir-gui-tools#timeline-explorer-eric-zimmerman) | +| **MFTExplorer** | GUI hex+tree view of `$MFT`, drill into any record, resident data, data runs | +| **EZViewer** | universal viewer for dozens of file types (images, Office, PDF, hex) without running the real application on evidence | +| **Hasher** | drag-and-drop hashing (MD5/SHA1/SHA256/...) of files, folders, text | +| **RecentFileCacheParser** | `RecentFileCache.bcf` (Win7/2008 pre-Amcache execution evidence) | +| **PECmd** sibling **RBCmd**, **SumECmd** | Recycle Bin (above); `SumECmd` = User Access Logging (`SUM` databases on **servers**: which user/IP hit which role, per day, 2+ years) | +| **SDB Explorer** | shim databases (`.sdb`) — application-compat persistence | +| **iisGeolocate** | GeoIP-enrich IIS logs | +| **VSCMount** | mount every Volume Shadow Copy of a live or attached volume as a folder tree | +| **TimeApp** | tiny clock overlay for screen-recorded live response | +| **XWFIM** | X-Ways forensics installer manager (only if you own X-Ways) | +| **KAPE** module **`!EZParser`** | runs all of the above in the right order over a collection | + +## Timeline Explorer — reviewing the CSVs + +Open any EZ CSV (or drag several in — each becomes a tab). It recognises the column names and sets types (timestamps sort as time, not text). + +| Action | How | +|---|---| +| Filter a column | type in the **filter row** under the header (`%mimikatz%` wildcard, `>2026-09-20` on time columns) | +| Filter builder | funnel icon bottom-left → boolean expressions across columns | +| Search everything | `Ctrl+F` finds across all columns of the current tab | +| Hide / reorder columns | right-click header → **Column Chooser**, drag to the header to show | +| Group by | drag a header into the band above the grid (group evtx by `EventId`, MFT by `ParentPath`) | +| Tag rows | tick the `Tag` checkbox column, then **Tools → Show only tagged**; tags survive export | +| Conditional colouring | right-click header → **Conditional Formatting** → e.g. `EventId = 4624` green, `= 4625` red | +| Sessions / layouts | **File → Session → Save**; reopens every tab with filters intact | +| Export | **File → Export** → CSV/XLSX/HTML of the *filtered* view — what goes in the report | +| Pivot on a time | copy a timestamp, paste it into the filter row of every other tab with `~` (approx) to see what else happened then | + +> [!tip] The merged-timeline shortcut +> `PECmd`'s `*_Timeline.csv`, `EvtxECmd` output, `MFTECmd`'s CSV and `LECmd` all share a first timestamp column. Open all of them in Timeline Explorer, filter each to the same 15-minute window, and you have a multi-artefact timeline without plaso. When you need the full super-timeline (thousands of files, every artefact) use `MFTECmd --body` + `mactime`, or the KAPE `Plaso_Timeline` module. + +## Worked triage: from live box to answers in ~30 minutes + +```bat +:: 1. Collect (USB E:, 3–8 min for a workstation) +kape.exe --tsource C: --tdest E:\c42\tout --target !SANS_Triage --vhdx WS042 --zv false --gui +:: 2. Mount the VHDX read-only (Disk Management → Attach VHD → Read-only, or Arsenal Image Mounter), say it lands on F: +:: 3. Parse everything +kape.exe --msource F: --mdest E:\c42\mout --module !EZParser --gui +:: 4. Detection pass over the event logs while you wait +hayabusa.exe csv-timeline -d F:\C\Windows\System32\winevt\Logs -o E:\c42\hayabusa.csv --no-wizard -p verbose +``` + +Then in Timeline Explorer, in this order: + +1. **`EventLogs\*.csv`** — filter `EventId` in `4624,4625,4648,4672,4688,7045,4698,1102`; note first suspicious logon time **T0** and the account. +2. **`ProgramExecution\*Amcache*UnassociatedFileEntries.csv`** — sort `FileKeyLastWriteTimestamp` ≥ T0; anything under `Users\`, `Temp\`, `ProgramData\`, `Public\` gets its SHA-1 checked. +3. **`ProgramExecution\*PECmd_Output_Timeline.csv`** — same window; confirm execution and get the *exact* run times. +4. **`FileSystem\*MFTECmd*.csv`** — filter `Created0x10` in the window, `ParentPath` for the dirs above; check `SI<FN` for timestomps, `ZoneIdContents` for download origin. +5. **`FileFolderAccess\`** (LECmd/JLECmd/SBECmd) — what did the account open/browse; ShellBags for USB/shares. +6. **`Registry\`** (RECmd batch) — Run keys, services, `UserAssist`, `BAM`, `MountedDevices`, `TypedPaths`. +7. **`SRUMDatabase\*NetworkUsages.csv`** — bytes out per process in the window. +8. Tag rows as you go, `File → Export` the tagged set per tab → the timeline in your report. + +## Related + +- [Digital Forensics reference](/sheets/dfir/forensics) — the "where is it and what does it prove" table this sheet parses +- [RECmd registry forensics](/sheets/dfir/recmd) — the registry half of the suite +- [Acquisition](/sheets/dfir/disk-imaging) — image first when you can +- [GUI tools](/sheets/dfir/dfir-gui-tools) — Registry Explorer, Timeline Explorer, Autopsy, FTK Imager walkthroughs +- [Volatility 3](/sheets/dfir/volatility) — the memory image KAPE cannot give you diff --git a/src/content/sheets/dfir/linux-forensics.md b/src/content/sheets/dfir/linux-forensics.md @@ -0,0 +1,365 @@ +--- +title: "Linux Forensics" +description: "Linux DFIR from live response to dead-disk analysis: order of collection, the /var/log map, wtmp/btmp/lastlog, journalctl and auditd queries, shell histories, persistence hunting (cron, systemd, rc, ld.so.preload, SSH keys), package integrity, /proc tricks, timestomp detection, then SIFT Workstation with Sleuth Kit and plaso/log2timeline for the super-timeline." +category: dfir +subcategory: "Linux" +tags: [dfir, forensics, linux, incident-response, logs, journalctl, auditd, persistence, systemd, cron, sleuth-kit, plaso, sift, timeline] +tools: ["SIFT Workstation", "Sleuth Kit (mmls, fls, icat, istat, tsk_recover)", "plaso / log2timeline / psort", "journalctl", "ausearch / aureport", "last / lastb / utmpdump", "debsums / rpm -Va", "chkrootkit / rkhunter", "UAC (Unix-like Artifacts Collector)", "Velociraptor", "AVML / LiME"] +difficulty: advanced +updated: "2026-09-26" +--- + +# Linux Forensics + +Linux boxes leave fewer "did it run" artefacts than Windows (no Prefetch, no Amcache, no ShimCache) and far more **text**: logs, histories, unit files, crontabs. The investigation therefore leans on **logs and timestamps** — `journalctl`, auditd, `wtmp`, the four `stat` times on every inode — and on knowing the **dozen places persistence hides**. This card is the live-response order, the `/var/log` map, the queries, the persistence hunt, and then the dead-disk workflow on **SIFT Workstation** with **Sleuth Kit** and **plaso** that produces the super-timeline. + +> [!warning] Every command you run on a live box changes it +> Your shell writes `~/.bash_history`, `atime`s update on files you read (unless the filesystem is `relatime`/`noatime`), and your tools appear in `ps` and the journal. Run statically-linked binaries **from your own media**, redirect output to your own mount, note the exact time you started, and prefer capturing memory ([acquisition sheet](/sheets/dfir/disk-imaging)) before anything else. + +## Live response, in order + +```bash +# 0. Your evidence mount and a log of what you run +E=/mnt/usb/evidence/$(hostname)-$(date -u +%Y%m%dT%H%MZ); mkdir -p "$E"; exec > >(tee -a "$E/collector.log") 2>&1 +date -u; uptime; cat /etc/os-release; uname -a; hostnamectl 2>/dev/null + +# 1. Memory (see acquisition sheet) — AVML needs no kernel module +/mnt/usb/bin/avml --compress "$E/mem.lime.compressed" + +# 2. Volatile: who / what / where +w; last -Faiw | head -50; lastb -F | head -50 +ps -eo pid,ppid,user,lstart,etime,stat,%cpu,%mem,cmd --forest +ss -tulpan; ss -xlp # sockets incl. unix +lsof -nP +L1 # deleted-but-open files (malware unlinking itself) +lsof -nP -i # network by process +ip a; ip r; ip neigh; cat /etc/resolv.conf /etc/hosts +cat /proc/mounts; df -hT; lsblk -f +lsmod; cat /proc/modules # compare later for hidden modules +cat /proc/sys/kernel/tainted # non-zero + no proprietary drivers = suspicious +systemctl list-units --type=service --state=running --no-pager +systemctl list-timers --all --no-pager +env; cat /proc/1/environ | tr '\0' '\n' # LD_PRELOAD in PID 1's env? + +# 3. Per-process detail for anything odd (deleted binary, weird cwd, no tty) +for p in $(pgrep -f suspicious); do + echo "== $p"; ls -l /proc/$p/exe /proc/$p/cwd; cat /proc/$p/cmdline | tr '\0' ' '; echo + cat /proc/$p/maps | head; ls -l /proc/$p/fd + cp /proc/$p/exe "$E/proc-$p.exe" # recover a deleted binary from memory +done + +# 4. Users and auth surface +cat /etc/passwd /etc/group /etc/shadow /etc/sudoers; ls -la /etc/sudoers.d/ +for h in /root /home/*; do echo "== $h"; ls -la "$h/.ssh" 2>/dev/null; cat "$h/.ssh/authorized_keys" 2>/dev/null; done +getent passwd | awk -F: '$3==0 || $7 !~ /(nologin|false)$/' # UID 0 or interactive shells + +# 5. Copy the artefacts you will parse offline (timestamps preserved) +tar --xattrs --acls -czpf "$E/logs.tgz" /var/log /etc /var/spool/cron /var/spool/at /root /home --exclude='*.iso' 2>/dev/null +journalctl -o export > "$E/journal.export" # lossless journal dump +find / -xdev -newermt "$(date -d '7 days ago' +%F)" -type f 2>/dev/null > "$E/recent-files.txt" +``` + +> [!tip] Automate it +> **UAC** (`github.com/tclahr/uac`) is the Linux/macOS/*BSD answer to KAPE: `./uac -p ir_triage /mnt/usb/evidence` collects all of the above (and hundreds more artefacts) using its own statically-linked binaries, hashed, in minutes. **Velociraptor** does the same at fleet scale (`Linux.Triage.*` artifacts). **CyLR** is the older, smaller option. Use one of these on real cases; the manual list above is for understanding what they grab. + +## The `/var/log` map + +Debian/Ubuntu on the left, RHEL/Fedora/SUSE where different. + +| File | Contains | Notes | +|---|---|---| +| `auth.log` / `secure` | sshd, sudo, su, PAM, login, useradd — **the** account log | `grep -E 'Accepted|Failed|Invalid|session opened|sudo:'` | +| `syslog` / `messages` | everything else the daemon caught: cron, kernel, services | | +| `kern.log`, `dmesg` | kernel ring buffer: USB plug-ins, module loads, OOM, segfaults | `dmesg -T` for human timestamps | +| `wtmp` | login/logout/reboot records (binary) | `last -F -f wtmp` | +| `btmp` | failed logins (binary) | `lastb -F -f btmp` — brute-force evidence | +| `lastlog` | last login per user (binary, sparse) | `lastlog`; `lastlog -b 30` = not logged in for 30 days | +| `utmp` (`/run/utmp` or `/var/run/utmp`) | **currently** logged-in sessions | `who`, `w`; `utmpdump /run/utmp` | +| `journal/` | systemd journal (binary, structured) | `journalctl -D /path/to/journal` on an image | +| `audit/audit.log` | auditd — syscalls, execve, file access per rule set | `ausearch`, `aureport` | +| `cron.log` (or in syslog) | cron job executions `CMD (...)` | | +| `dpkg.log`, `apt/history.log` / `yum.log`, `dnf.log` | package installs/removals with time | `grep -E ' install | remove ' dpkg.log` | +| `apache2/`, `nginx/`, `httpd/` | web access/error logs — webshell uploads, LFI, scanner UAs | `access.log`: sort by IP, look at POSTs to odd paths | +| `mysql/`, `postgresql/` | DB errors, slow queries, sometimes general log | | +| `faillog`, `tallylog` | pam_tally/faillock counters | `faillock` | +| `Xorg.*.log`, `lightdm/`, `gdm/`, `sddm.log` | graphical logins | | +| `cloud-init*.log` | cloud first-boot: user-data script contents run as root | | +| `installer/` (`anaconda/`) | OS install time | | +| `~/.xsession-errors`, `~/.local/share/xorg/` | per-user X errors | | + +```bash +# Binary login records +last -Faiw -f /mnt/img/var/log/wtmp # -F full times, -a host in last column, -i IPs, -w full names +last -Faiw -f wtmp reboot # boots +lastb -F -f /mnt/img/var/log/btmp | awk '{print $3}' | sort | uniq -c | sort -rn | head # top attacking IPs +utmpdump /mnt/img/var/log/wtmp # raw records incl. terminal and pid; also: utmpdump wtmp > wtmp.txt; edit; utmpdump -r < wtmp.txt +lastlog -R /mnt/img # per-user last login from an image root +# Rotated logs: read them all at once +zgrep -h 'Accepted' /mnt/img/var/log/auth.log* | sort -k1M -k2n +``` + +## `journalctl` — the systemd journal + +```bash +# Point at an image's journal dir (not the live one) +J="journalctl -D /mnt/img/var/log/journal --no-pager -o short-iso --utc" + +$J --list-boots # boot IDs with first/last times +$J -b -1 # previous boot only +$J --since "2026-09-20" --until "2026-09-22 06:00" +$J -u ssh -u sshd # a unit (or several) +$J _COMM=sudo # by process name +$J _UID=1001 # by user +$J -p warning..emerg # priority range (0 emerg … 7 debug) +$J -k # kernel messages (dmesg equivalent, all boots) +$J _SYSTEMD_UNIT=cron.service # cron executions +$J -g 'Accepted|Failed password|session opened|COMMAND=' # regex grep across everything +$J -o verbose _PID=4242 # every field of matching entries +$J -o json | jq -r 'select(.MESSAGE|test("useradd|usermod|passwd")) | [.__REALTIME_TIMESTAMP,.MESSAGE]|@tsv' +$J --disk-usage; $J --verify # is the journal intact? tampered files fail verification +$J -o export > journal.export # lossless copy; re-import with systemd-journal-remote +``` + +> [!info] Volatile vs persistent +> If `/var/log/journal/` does not exist, the journal was **volatile** (`/run/log/journal`) and died at power-off — that is your gap. `Storage=` in `/etc/systemd/journald.conf` tells you which. Check `MaxRetentionSec`/`SystemMaxUse` for how far back you can expect to see. + +## auditd — when it is on, it is the best log you have + +```bash +cat /etc/audit/auditd.conf /etc/audit/rules.d/*.rules # what was being recorded +auditctl -l # live rules + +A="ausearch -if /mnt/img/var/log/audit/audit.log -i" # -i = interpret UIDs/syscalls; -if = file +$A -m USER_LOGIN,USER_AUTH,USER_START # logins +$A -m USER_CMD; $A -m EXECVE # sudo commands / every execve (if rule -a always,exit -S execve) +$A -ua 1001 -ts 09/20/2026 00:00:00 -te 09/22/2026 23:59:59 # by audit UID (survives su/sudo) in a window +$A -k persistence # by rule key +$A -f /etc/passwd; $A -f /etc/shadow # who touched a file +$A -sc connect; $A -sc ptrace # by syscall +$A -x /usr/bin/curl # by executable +$A --session 42 # everything in one login session +$A -m AVC # SELinux denials — malware tripping policy + +aureport -if audit.log --summary # counts by type +aureport -if audit.log -au -i # authentication report +aureport -if audit.log -x -i --summary # executables by frequency +aureport -if audit.log -f -i | grep -E '/tmp|/dev/shm|/var/tmp' # files touched in world-writable dirs +``` + +`auid` (audit UID) is the login user even after `sudo -i`; `uid`/`euid` show the effective identity. `ses=` links every record of a session. Records with `key=` came from a named rule — many hardening baselines (CIS, Neo23x0's `auditd` rules) tag `T1xxx` MITRE keys straight into the log. + +## Shell histories and user activity + +```bash +for h in /mnt/img/root /mnt/img/home/*; do + for f in .bash_history .zsh_history .python_history .mysql_history .psql_history .lesshst .viminfo .wget-hsts .rediscli_history .sqlite_history .node_repl_history .local/share/fish/fish_history .local/share/nano/search_history .config/nvim/shada; do + [ -s "$h/$f" ] && { echo "=== $h/$f ($(stat -c %y "$h/$f"))"; cat "$h/$f"; } + done +done 2>/dev/null + +# zsh extended history has epoch timestamps: ": 1758866400:0;whoami" +awk -F'[:;]' '/^: [0-9]+/ {cmd=$0; sub(/^: [0-9]+:[0-9]+;/,"",cmd); print strftime("%F %T",$2), cmd}' .zsh_history +# bash with HISTTIMEFORMAT set writes "#1758866400" lines above each command +awk '/^#[0-9]+$/ {ts=strftime("%F %T",substr($0,2)); next} {print ts, $0}' .bash_history + +# Signs of history tampering +ls -la ~/.bash_history # symlink to /dev/null? size 0 with old mtime? +grep -E 'HISTSIZE=0|HISTFILE=|unset HIST|history -c|set \+o history' /mnt/img/etc/profile /mnt/img/etc/bash.bashrc /mnt/img/home/*/.bashrc /mnt/img/home/*/.profile 2>/dev/null +# Other traces: .viminfo (files edited, searches), .lesshst, .recently-used.xbel (GTK file dialogs), .cache/, Trash +cat /mnt/img/home/*/.local/share/recently-used.xbel | grep -o 'href="[^"]*"' | sort -u +ls -la /mnt/img/home/*/.local/share/Trash/{files,info}/ +``` + +> [!tip] History is written at shell **exit** +> A live attacker's session has no history file yet. Read it from memory instead: `cat /proc/<bash pid>/mem` regions via `gdb`/`pymem`, or Volatility's `linux.bash` plugin on the RAM image. `~/.bash_history` also has **no timestamps** unless `HISTTIMEFORMAT` was set — order is all you get. + +## Persistence hunt + +Check every one of these; attackers use the boring ones. + +| Mechanism | Where to look | +|---|---| +| **cron** | `/etc/crontab`, `/etc/cron.d/*`, `/etc/cron.{hourly,daily,weekly,monthly}/*`, `/var/spool/cron/crontabs/*` (Debian) / `/var/spool/cron/*` (RHEL), `/etc/anacrontab`, `/var/spool/anacron/`; `at` jobs in `/var/spool/at/` or `/var/spool/cron/atjobs/` | +| **systemd units** | `/etc/systemd/system/`, `/usr/lib/systemd/system/`, `/run/systemd/system/`, per-user `~/.config/systemd/user/`; look for `ExecStart=` pointing at `/tmp`, `/dev/shm`, `/var/tmp`, home dirs, base64 or `curl|sh`; **timers** (`*.timer`) and **path** units; `systemd-run` transient units; generators in `/etc/systemd/system-generators/` | +| **init / rc** | `/etc/rc.local`, `/etc/rc*.d/`, `/etc/init.d/`, `/etc/inittab` (legacy), `/etc/init/*.conf` (upstart) | +| **shell startup** | `/etc/profile`, `/etc/profile.d/*.sh`, `/etc/bash.bashrc`, `/etc/bashrc`, `/etc/zsh/*`, `~/.bashrc`, `~/.bash_profile`, `~/.profile`, `~/.zshrc`, `~/.bash_logout`, `/etc/environment` | +| **ld.so.preload / LD_PRELOAD** | `/etc/ld.so.preload` (should not exist or be empty), `/etc/ld.so.conf.d/`, env of running processes; a rootkit here hides itself from `ls`/`ps` — verify with a static busybox | +| **SSH** | `~/.ssh/authorized_keys` (all users incl. service accounts), `/etc/ssh/sshd_config` (`AuthorizedKeysFile`, `AuthorizedKeysCommand`, `PermitRootLogin`, extra `Include`), `/etc/ssh/sshd_config.d/`, host keys changed, `~/.ssh/rc` | +| **PAM** | `/etc/pam.d/*` modified (`pam_exec.so`, unknown modules in `/lib/security/` or `/usr/lib/x86_64-linux-gnu/security/`), `/etc/security/` | +| **users & groups** | new UID 0, users with shells that should not, `sudoers` / `sudoers.d` NOPASSWD, `wheel`/`sudo`/`docker`/`disk` group members, `/etc/passwd` vs `/etc/shadow` inconsistencies, `chage -l user` | +| **SUID / capabilities** | `find / -xdev -perm -4000 -type f -newer /etc/os-release`, `getcap -r / 2>/dev/null` — compare against a clean install | +| **kernel modules** | `/etc/modules`, `/etc/modules-load.d/`, `/etc/modprobe.d/`, modules in `/lib/modules/$(uname -r)/` not owned by a package, `/proc/modules` vs `lsmod` vs `/sys/module/` mismatch | +| **udev** | `/etc/udev/rules.d/*.rules` with `RUN+=` | +| **XDG autostart** | `/etc/xdg/autostart/*.desktop`, `~/.config/autostart/*.desktop` | +| **package hooks** | `/etc/apt/apt.conf.d/` (`DPkg::Post-Invoke`), `/etc/yum/pluginconf.d/`, `/etc/dnf/plugins/` | +| **web** | webshells in the docroot (recent `.php`/`.jsp`/`.aspx`, `eval(`, `base64_decode(`, `system(`), `.htaccess` rewrites, cron-driven `wget` | +| **containers / orchestrators** | `docker ps -a`, `/var/lib/docker/containers/*/config.v2.json`, privileged containers, host mounts; k8s CronJobs | +| **MOTD / issue** | `/etc/update-motd.d/*` runs as root at login | +| **binary replacement** | `debsums -c` / `rpm -Va` below; `ls -la /usr/bin/ls /bin/ps /usr/bin/ss /usr/bin/netstat` sizes and mtimes vs siblings | + +```bash +R=/mnt/img +# One-shot sweep of the file-based locations (adjust R for live: R=/) +grep -rE 'ExecStart|ExecStartPre|ExecStop' $R/etc/systemd/system $R/usr/lib/systemd/system $R/home/*/.config/systemd 2>/dev/null | grep -Ev 'ExecStart=/(usr/)?(s?bin|lib)/' | sort -u +cat $R/etc/crontab $R/etc/cron.d/* $R/var/spool/cron/crontabs/* $R/var/spool/cron/* 2>/dev/null | grep -Ev '^\s*(#|$)' +cat $R/etc/ld.so.preload 2>/dev/null; ls -la $R/etc/ld.so.preload 2>/dev/null +find $R/etc/systemd $R/etc/cron* $R/etc/profile.d $R/etc/init.d $R/etc/rc.local $R/etc/ssh $R/etc/pam.d $R/etc/sudoers.d $R/etc/udev/rules.d -type f -printf '%TY-%Tm-%Td %TH:%TM %p\n' 2>/dev/null | sort | tail -40 +find $R -xdev -type f \( -path '*/tmp/*' -o -path '*/dev/shm/*' -o -path '*/var/tmp/*' \) -perm -u+x -ls 2>/dev/null +find $R -xdev -type f -name '.*' -perm -u+x -not -path '*/.git/*' -ls 2>/dev/null | head -50 +find $R -xdev -type f -perm -4000 -printf '%TY-%Tm-%Td %p\n' 2>/dev/null | sort | tail -20 +# Anything in a bin dir not owned by a package (Debian / RHEL) +for f in $R/usr/bin/* $R/usr/sbin/* $R/bin/* $R/sbin/*; do dpkg -S "${f#$R}" >/dev/null 2>&1 || echo "unowned: $f"; done 2>/dev/null | head +``` + +## Package integrity + +```bash +# Debian/Ubuntu — compare installed files against package MD5s (from the image's own dpkg DB) +debsums -c --root /mnt/img 2>/dev/null # changed files only +debsums -ac --root /mnt/img # include config files +# RHEL/Fedora — S=size M=mode 5=md5 D=device L=link U=user G=group T=mtime P=caps +rpm -Va --root /mnt/img 2>/dev/null | grep -Ev '^\.{9}\s+c ' # ignore untouched configs +rpm -Va --root /mnt/img | awk '$1 ~ /5/' # content changed +rpm -qf /mnt/img/usr/bin/ls; rpm -V coreutils +# Arch +pacman -Qkk --root /mnt/img 2>/dev/null | grep -v '0 altered' +# Package DB itself tampered? Verify against the repo copy: +apt-get download coreutils && dpkg-deb -x coreutils_*.deb /tmp/clean && diff <(sha256sum /tmp/clean/usr/bin/ls) <(sha256sum /mnt/img/usr/bin/ls) +``` + +Rootkit scanners are a cheap second opinion, not a verdict: `chkrootkit -r /mnt/img`, `rkhunter --check --rootdir /mnt/img`. Better: `debsums`/`rpm -Va` plus comparing `ls`, `ps`, `netstat`, `ss`, `lsof`, `find` outputs against a **static busybox** on a live box (`busybox ps` vs `ps`). + +## Hidden processes and `/proc` + +```bash +# PIDs in /proc that ps does not show (LD_PRELOAD rootkits hook readdir in libc, not the kernel) +diff <(ls /proc | grep -E '^[0-9]+$' | sort -n) <(ps -eo pid= | tr -d ' ' | sort -n) +# Brute-force /proc — bypasses readdir hooks +for p in $(seq 1 65535); do [ -d /proc/$p ] && [ ! -e /proc/$p/status.x ] && echo -n "$p "; done 2>/dev/null; echo +# Sockets with no owning process in ss (hidden by rootkit) vs /proc/net/tcp +cat /proc/net/tcp /proc/net/tcp6 | awk 'NR>1{print $2, $4, $10}' # local addr (hex), state (0A=LISTEN), inode +# Process running from a deleted binary +ls -l /proc/*/exe 2>/dev/null | grep '(deleted)' +# Executable memory mappings not backed by a file (injected code, packers) +grep -E 'rwx|r-xp 00000000 00:00 0' /proc/*/maps 2>/dev/null | head +# Namespaces: container escapes / hidden via unshare +ls -la /proc/*/ns/pid 2>/dev/null | awk '{print $NF}' | sort | uniq -c +# Mount namespace tricks: bind-mount over /proc/<pid> to hide it +cat /proc/mounts | grep -E ' /proc/[0-9]+' +# Kernel modules: /proc/modules vs sysfs +diff <(awk '{print $1}' /proc/modules | sort) <(ls /sys/module | sort) | grep '^>' +``` + +## Timestamps and timestomping + +Every inode has **atime, mtime, ctime**, and on ext4/xfs/btrfs also **crtime** (birth). `touch -d` and `utimensat` change atime/mtime freely; **ctime cannot be set from userspace** (it moves to *now* on any change), and **crtime** never changes. A file whose mtime is 2019 but ctime is last Tuesday was modified last Tuesday. + +```bash +stat file # Access / Modify / Change / Birth +stat -c '%n %w %x %y %z' /mnt/img/usr/bin/* | awk '$3 > $4 || $4 > $5' # mtime after ctime = impossible without tampering +# crtime on ext4 when stat shows "-" for Birth (older coreutils): +debugfs -R "stat /usr/bin/ls" /dev/loop0p2 | grep -E 'crtime|ctime|mtime|atime' +# Files changed in a window, sorted by ctime (attacker cannot fake ctime) +find /mnt/img -xdev -type f -newerct '2026-09-20' ! -newerct '2026-09-22' -printf '%CY-%Cm-%Cd %CH:%CM %p\n' 2>/dev/null | sort +# Fractional-second timestamps: tools that set a whole-second time (touch -r, tar defaults) leave .000000000 — real writes almost never do +find /mnt/img/usr/bin -type f -printf '%TS %p\n' | grep -E '^00\.000000000' | head +# Mismatch between package mtime and actual +dpkg-query -W -f='${Package}\n' | head -1 >/dev/null; ls -l --time-style=full-iso /mnt/img/usr/bin/ps /mnt/img/usr/bin/pgrep # siblings from one package share mtimes +``` + +> [!info] `relatime` and `noatime` +> Most distros mount with `relatime`: atime updates only if it is older than mtime/ctime or >24 h old. So "atime = last read" is only sometimes true. Check `/etc/fstab` and `/proc/mounts` before concluding anything from access times. + +## Dead-disk analysis on SIFT Workstation + +**SIFT** (SANS Investigative Forensic Toolkit) is the free Ubuntu-based analysis VM that FOR508 and most Linux DFIR modules run on: Sleuth Kit, plaso, Volatility, `ewf-tools`, `libvshadow`, RegRipper, Autopsy, `bulk_extractor`, YARA, `hindsight`, and the EZ CLI tools under .NET are preinstalled. Get it as an OVA from `sans.org/tools/sift-workstation` or bolt it onto any Ubuntu with `sift install` (Cast: `github.com/teamdfir/sift-cli`). Alternatives with the same toolset: **Tsurugi Linux**, **CAINE**, **REMnux** (malware focus), and Kali's `forensics` metapackage. + +### Mount the image (read-only) + +```bash +mmls sdb.E01 # partition table with start sectors (TSK reads E01 natively) +fsstat -o 2048 sdb.E01 | head -30 # filesystem type, block size, last mount, label +# Mount via ewfmount → loop → mount (see acquisition sheet for LVM / LUKS) +sudo ewfmount sdb.E01 /mnt/ewf && sudo losetup -rfP --show /mnt/ewf/ewf1 +sudo mount -o ro,noexec,noload /dev/loop0p2 /mnt/img # ext4: noload = don't replay journal +sudo mount -o ro,noexec,norecovery /dev/loop0p3 /mnt/img/var # xfs uses norecovery +``` + +### Sleuth Kit essentials + +| Layer | Tool | Use | +|---|---|---| +| media | `mmls`, `mmstat` | partitions and offsets | +| filesystem | `fsstat` | fs metadata, journal info, block/inode counts | +| filename | `fls`, `ffind` | list files (incl. **deleted**), find name for an inode | +| metadata | `istat`, `ifind`, `icat`, `ils` | inode detail, inode for a block, dump file by inode, list inodes | +| block | `blkls`, `blkcat`, `blkstat`, `blkcalc` | unallocated space, raw blocks | +| journal | `jls`, `jcat` | ext3/4 journal entries (old versions of blocks!) | +| recovery | `tsk_recover`, `tsk_loaddb`, `tsk_gettimes` | carve all deleted files, load into SQLite, bodyfile | + +```bash +O=2048 # partition offset in sectors from mmls +fls -o $O -r -p sdb.E01 > fls-all.txt # recursive, full paths; deleted entries are marked "* " and "(realloc)" +fls -o $O -r -p -d sdb.E01 # deleted only +fls -o $O -r -p -m / sdb.E01 > body.txt # bodyfile (mactime format) with mount point / +istat -o $O sdb.E01 131074 # timestamps, size, block list for inode 131074 +icat -o $O sdb.E01 131074 > recovered.bin # dump a (deleted) file by inode +ifind -o $O -n /etc/passwd sdb.E01 # inode for a path +ffind -o $O sdb.E01 131074 # path(s) for an inode +tsk_recover -o $O -e sdb.E01 ./recovered/ # -e = everything incl. allocated; default = unallocated (deleted) only +blkls -o $O sdb.E01 > unalloc.raw # unallocated blocks → feed to bulk_extractor / foremost +jls -o $O sdb.E01 | head; jcat -o $O sdb.E01 8 1234 | xxd | head # journal block 1234 (old content) +tsk_loaddb -o $O -d case.db sdb.E01 # SQLite of the whole fs → query with sqlite3 / Autopsy +``` + +### Timeline: `mactime` (fast) and plaso (complete) + +```bash +# 1. Filesystem-only timeline from the bodyfile — seconds +mactime -b body.txt -d -z UTC 2026-09-15..2026-09-27 > fs-timeline.csv +# columns: Date,Size,Type(m a c b),Mode,UID,GID,Meta,File Name + +# 2. Super-timeline with plaso: filesystem + logs + journal + histories + browser + apt/dpkg + cron + ... in one file +log2timeline.py --storage-file case.plaso --parsers linux /mnt/img # against the mounted tree (preset "linux") +log2timeline.py --storage-file case.plaso --partitions all sdb.E01 # or the image directly (TSK), all partitions +log2timeline.py --storage-file case.plaso --parsers 'linux,!filestat' --hashers sha256 /mnt/img # tune parsers, add hashes +pinfo.py case.plaso # what got parsed, counts, errors + +# 3. Output: filter a window and sort +psort.py -o l2tcsv -w timeline.csv case.plaso "date > '2026-09-20 00:00:00' AND date < '2026-09-23 00:00:00'" +psort.py -o dynamic --fields datetime,timestamp_desc,source,source_long,message,parser,display_name -w tl.csv case.plaso +psort.py -o l2tcsv case.plaso "message contains 'authorized_keys' OR message contains 'crontab'" | head +psort.py --analysis tagging --tagging-file /usr/share/plaso/tag_linux.txt case.plaso # tag logins, sudo, package installs ... +psort.py -o opensearch ... / -o timesketch → Timesketch for collaborative review +``` + +Open `timeline.csv` in Timeline Explorer (Windows VM) or `visidata timeline.csv` on SIFT; filter around the first suspicious login and read outward. `timestamp_desc` tells you *which* timestamp (`Content Modification`, `Last Access`, `Metadata Modification`, `Creation`); `source` tells you the artefact (`FILE`, `LOG`, `WEBHIST`, `BASH`, `UTMP`, `JOURNAL`). + +> [!tip] Pick the tool for the question +> `fls`/`mactime`: "what files changed between 02:00 and 03:00" — a minute of work. plaso: "everything that happened, from every source, in one sorted list" — an hour of parsing, but it is the artefact of record for the report. + +### Carving, strings and unknowns + +Unallocated space (`blkls` output), swap, and unknown binaries go to [strings & file triage](/sheets/dfir/strings-file-triage): `bulk_extractor`, `foremost`/`scalpel`, `strings`, `binwalk`, YARA. Memory → [Volatility 3](/sheets/dfir/volatility) (`linux.pslist`, `linux.bash`, `linux.lsof`, `linux.malfind`, `linux.check_syscall`, `linux.check_modules` need a symbol table built with `dwarf2json` from the exact kernel's debug symbols). + +## Quick answers + +| Question | Command | +|---|---| +| First and last time an IP logged in | `last -Faiw -f wtmp \| grep 203.0.113.9`; `zgrep 203.0.113.9 auth.log*` | +| Which key was used for an SSH login | `auth.log`: `Accepted publickey for bob from ... ssh2: ED25519 SHA256:xxxx` → match fingerprint with `ssh-keygen -lf authorized_keys` | +| What ran with sudo | `grep 'COMMAND=' auth.log*`; `ausearch -m USER_CMD -i`; `journalctl _COMM=sudo` | +| Was a user added | `grep -E 'useradd|usermod|groupadd|passwd\[' auth.log*`; `journalctl _COMM=useradd`; compare `/etc/passwd-` (backup) with `/etc/passwd` | +| What did cron run | `grep CRON syslog*`; `journalctl _SYSTEMD_UNIT=cron.service`; `journalctl -u '*.timer'` | +| USB plugged in | `journalctl -k -g 'usb .*New USB device\|sd .* Attached SCSI'`; `kern.log` | +| Files modified last 48 h, by ctime | `find / -xdev -newerct '48 hours ago' -type f ! -path '/proc/*' ! -path '/sys/*' -printf '%CY-%Cm-%Cd %CH:%CM %p\n' \| sort` | +| Deleted but running | `ls -l /proc/*/exe \| grep deleted`; `lsof +L1` | +| Reboots / uptime gaps | `last -Fx reboot shutdown`; `journalctl --list-boots` | +| Package installed when | `grep ' install ' dpkg.log*`; `rpm -qa --last \| head` | + +## Related + +- [Acquisition](/sheets/dfir/disk-imaging) — AVML/LiME memory, `dc3dd`/`ewfacquire`, mounting images +- [Volatility 3](/sheets/dfir/volatility) — the memory side +- [strings & file triage](/sheets/dfir/strings-file-triage) — unknown binaries, carving unallocated space +- [Digital Forensics reference](/sheets/dfir/forensics) — the cross-platform overview +- [Linux credential & flag hunting](/sheets/password-attacks/linux-credential-flag-hunting) — the same file locations from the other side of the table diff --git a/src/content/sheets/dfir/strings-file-triage.md b/src/content/sheets/dfir/strings-file-triage.md @@ -0,0 +1,365 @@ +--- +title: "strings & File Triage" +description: "Triage an unknown file or a blob of unallocated space: strings in depth (-n, -t x, -e l for UTF-16, piping into IOC regexes), file/magic bytes, xxd and hexdump, exiftool metadata, binwalk, carving with foremost/scalpel/photorec, bulk_extractor, hashing and ssdeep, PDF and Office maldoc checks, YARA — with the Windows equivalents (Sysinternals strings, Get-FileHash, certutil, Format-Hex) alongside." +category: dfir +subcategory: "Analysis" +tags: [dfir, forensics, strings, file-analysis, carving, hexdump, magic-bytes, metadata, yara, malware-triage, windows, linux] +tools: ["strings / bstrings / Sysinternals strings.exe", "file / libmagic", "xxd / hexdump / Format-Hex", "exiftool", "binwalk", "foremost / scalpel / photorec", "bulk_extractor", "ssdeep / hashdeep", "pdfid / pdf-parser / peepdf", "oletools (olevba, oleid, oledump)", "YARA", "Detect It Easy (DiE)", "pestudio / pev / readpe", "HxD / ImHex"] +difficulty: intermediate +updated: "2026-09-26" +--- + +# strings & File Triage + +Before a disassembler, before a sandbox, there is the ten-minute pass every unknown file gets: **what is it really** (`file`, magic bytes), **what is written inside it** (`strings`, in the right encoding), **what does it carry** (`exiftool`, `binwalk`, embedded objects), **what does it match** (hashes, `ssdeep`, YARA). The same tools, pointed at a memory image, a swap file or the unallocated blocks of a disk, become **carving** — pulling files and indicators out of space no filesystem points to. This card is those tools, Linux/macOS first with the **Windows equivalent** next to each, and the patterns that make `strings` output readable instead of a 40,000-line wall. + +> [!warning] Handle malware like malware +> Work on a copy, in a VM with no shared clipboard or network, with the file renamed to `sample.bin` so nothing double-clicks it. `strings`, `file` and `exiftool` do not execute anything — but `binwalk -e`, `7z x` and PDF tools that render *can* trigger parser bugs. Snapshot first. + +## Identify: what is it really? + +```bash +file sample.bin # libmagic: type, arch, linker, packer hints +file -b --mime-type sample.bin # bare MIME +file -z archive.gz # look inside compressed +file -k sample.bin # keep going, report all matches (polyglots) +file * # a whole directory, quickly +xxd sample.bin | head -4 # eyeball the magic yourself +``` + +```powershell +# Windows: no `file`, so read the magic +Format-Hex .\sample.bin -Count 32 +certutil -dump .\sample.bin | Select -First 5 # for certs/PKCS +# or install: winget install GnuWin32.File / use TrID (trid.exe sample.bin) +``` + +| Magic (hex) | ASCII | Type | +|---|---|---| +| `4D 5A` | `MZ` | Windows PE (exe/dll/sys) — `PE\0\0` follows at offset in `0x3C` | +| `7F 45 4C 46` | `.ELF` | Linux/Unix executable | +| `CF FA ED FE` / `CA FE BA BE` | | Mach-O 64-bit / universal (fat) | +| `50 4B 03 04` | `PK..` | ZIP — also **docx/xlsx/pptx, jar, apk, ipa, odt** | +| `D0 CF 11 E0 A1 B1 1A E1` | | OLE2 / CFB — legacy **doc/xls/ppt, msi, msg** | +| `25 50 44 46` | `%PDF` | PDF | +| `7B 5C 72 74 66` | `{\rtf` | RTF (CVE-2017-11882 territory) | +| `1F 8B` | | gzip | +| `42 5A 68` | `BZh` | bzip2 | +| `FD 37 7A 58 5A 00` | `.7zXZ.` | xz | +| `37 7A BC AF 27 1C` | `7z..'.` | 7-Zip | +| `52 61 72 21 1A 07` | `Rar!..` | RAR | +| `75 73 74 61 72` @257 | `ustar` | tar | +| `FF D8 FF` | | JPEG | +| `89 50 4E 47 0D 0A 1A 0A` | `.PNG` | PNG | +| `47 49 46 38` | `GIF8` | GIF | +| `52 49 46 46` | `RIFF` | WAV/AVI/WebP | +| `53 51 4C 69 74 65` | `SQLite` | SQLite DB (browser history, Windows Timeline, mobile apps) | +| `72 65 67 66` | `regf` | Windows registry hive | +| `45 6C 66 46 69 6C 65` | `ElfFile` | Windows EVTX | +| `4C 00 00 00 01 14 02 00` | `L...` | Windows LNK | +| `4D 41 4D 04` / `53 43 43 41` @4 | `MAM.` / `SCCA` | Prefetch (compressed Win10 / raw) | +| `EF BB BF` / `FF FE` / `FE FF` | | UTF-8 BOM / UTF-16 LE / BE text | +| `23 21` | `#!` | script with shebang | +| `4D 53 43 46` | `MSCF` | CAB | +| `43 57 53` / `46 57 53` | `CWS`/`FWS` | Flash | +| `00 00 00 xx 66 74 79 70` | `ftyp` @4 | MP4/MOV/HEIC | +| `4B 44 4D` | `KDM` | VMware VMDK | +| `63 6F 6E 65 63 74 69 78` | `conectix` | VHD | +| `45 56 46 09 0D 0A FF 00` | `EVF` | EnCase E01 | +| `4C 69 4D 45` | `LiME` | LiME memory dump | + +> [!tip] Extension lies, magic mostly doesn't +> `invoice.pdf.exe`, `report.docm` renamed to `.doc`, a JPEG with a PHP webshell appended (`file` says JPEG, `strings` says `<?php`), a ZIP that is also a valid PDF. `file -k` and looking at **both ends** of the file (`xxd | head`, `xxd | tail`) catch most polyglots and appended payloads. Trailing data after the PNG `IEND` chunk or after the ZIP central directory is never innocent. + +## `strings` in depth + +GNU `strings` (binutils) prints runs of printable characters ≥ 4 bytes. The defaults hide half of what a Windows binary contains. + +```bash +strings sample.bin # ASCII (and single-byte) runs ≥ 4 chars +strings -n 8 sample.bin # minimum length 8 — cuts the noise dramatically +strings -a sample.bin # -a: scan the WHOLE file, not just loaded sections (default on modern binutils, be explicit) +strings -e l sample.bin # 16-bit little-endian = UTF-16LE: Windows API strings, paths, registry keys, PowerShell in .NET binaries +strings -e b sample.bin # UTF-16 big-endian (rare: Java, some Mac formats) +strings -e S sample.bin # single 8-bit incl. high-ASCII (Latin-1 lures, some packers) +strings -t x sample.bin # prefix each string with its hex offset (-t d decimal, -t o octal) → jump there in a hex editor +strings -t x -e l sample.bin | grep -i 'cmd\|powershell\|http' +strings -a -n 6 sample.bin; strings -a -n 6 -e l sample.bin # ALWAYS run both — ASCII and UTF-16 — on Windows samples +strings -f *.bin # -f: print filename before each string (many files) +strings sample.bin | sort | uniq -c | sort -rn | head # repeated strings = table entries, decoy padding +strings sample.bin | awk 'length > 60' # long strings: base64 blobs, embedded scripts, URLs with parameters +``` + +| Platform | Equivalent | +|---|---| +| macOS | `strings` is from LLVM/cctools: `strings -a sample.bin`; **no `-e l`** — use `iconv`/`python` below, or `brew install binutils` → `gstrings -e l` | +| Windows | **Sysinternals** `strings.exe -a -n 6 sample.bin` scans ASCII **and** Unicode by default (`-u` Unicode only, `-a` ASCII only, `-o` offsets, `-s` recurse); **bstrings** (EZ Tools) adds regex and built-in IOC patterns — see [EZ Tools](/sheets/dfir/ez-tools-kape#bstrings--better-strings) | +| PowerShell | `Select-String -Path sample.bin -Pattern 'http' -Encoding Unicode` for a quick UTF-16 grep, or `[IO.File]::ReadAllBytes` + regex | +| Any | FLOSS (Mandiant `floss sample.exe`) — recovers **obfuscated** and **stack** strings from PE files that plain `strings` never sees; run it after `strings` disappoints | + +```bash +# UTF-16 strings on macOS/Windows-less environments without -e l +python3 -c "import re,sys;d=open(sys.argv[1],'rb').read();print('\n'.join(m.group().decode('utf-16le') for m in re.finditer(rb'(?:[\x20-\x7e]\x00){6,}',d)))" sample.bin +``` + +### Making `strings` output useful: the IOC pass + +Save the full output once (`strings -a -n 6 sample.bin > s.txt; strings -a -n 6 -e l sample.bin >> s.txt`) and grep the file instead of re-running. + +```bash +S=s.txt +grep -Eio 'https?://[a-z0-9./?=_%:-]+' $S | sort -u # URLs +grep -Eo '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' $S | sort -u | grep -Ev '^(0|127|255)\.' # IPv4 (drops version-number noise imperfectly — eyeball it) +grep -Eio '\b[a-z0-9.-]+\.(com|net|org|io|ru|cn|top|xyz|info|biz|onion|pw|cc|su)\b' $S | sort -u # domains +grep -Eio '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}' $S | sort -u # emails +grep -Ei '\\\\[^\\ ]+\\[^\\ ]+' $S | sort -u # UNC paths +grep -Ei 'HKEY_|HKLM|HKCU|CurrentVersion\\Run|Software\\Microsoft' $S | sort -u # registry +grep -Ei '\.(exe|dll|bat|ps1|vbs|js|hta|scr|lnk|tmp|dat|log)\b' $S | sort -u # filenames +grep -Ei 'C:\\|%APPDATA%|%TEMP%|\\Temp\\|\\Users\\|/tmp/|/dev/shm|/var/tmp' $S | sort -u # paths +grep -Ei 'powershell|cmd\.exe|/c |-enc|-nop|-w hidden|IEX|Invoke-|DownloadString|FromBase64|wscript|cscript|mshta|rundll32|regsvr32|certutil|bitsadmin|schtasks|sc \|net user|whoami' $S # LOLBins & PS +grep -Ei 'CreateRemoteThread|VirtualAlloc|WriteProcessMemory|NtUnmapViewOfSection|SetWindowsHookEx|IsDebuggerPresent|GetTickCount|CryptEncrypt|InternetOpen|WinHttp|URLDownloadToFile|ShellExecute|WinExec|RegSetValue|AdjustTokenPrivileges|OpenProcess|LoadLibrary|GetProcAddress' $S | sort -u # suspicious imports +grep -Ei 'user-agent|mozilla/|accept:|content-type' $S # hardcoded HTTP client +grep -Ei 'password|passwd|pwd=|token|secret|api[_-]?key|BEGIN (RSA|OPENSSH|EC) PRIVATE|AKIA[0-9A-Z]{16}' $S # credentials +grep -Ei 'mutex|Global\\|Local\\' $S # mutex names → hunt on other hosts +grep -Ei 'pdb$|\.pdb\b|[A-Z]:\\Users\\[^\\]+\\(source|Desktop|Documents)' $S # PDB path = developer username & project name +grep -Ei 'upx|aspack|themida|vmprotect|mpress|petite|\.packed|this program cannot be run' $S # packer strings +grep -Eo '[A-Za-z0-9+/]{40,}={0,2}' $S | head # base64 blobs → decode below +grep -Eo '[0-9a-f]{32}\b|[0-9a-f]{40}\b|[0-9a-f]{64}\b' $S | sort -u | head # hashes / hex keys +grep -Eio '(bitcoin|btc|monero|xmr|wallet|\.onion|ransom|decrypt|your files)' $S # ransomware notes +``` + +```bash +# Decode what you found +echo 'SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQA' | base64 -d | iconv -f utf-16le -t utf-8 # PowerShell -enc is UTF-16LE base64 +echo 'aHR0cDov...' | base64 -d | file - # what is inside the blob? +printf '\x68\x74\x74\x70' | xxd; echo 68747470 | xxd -r -p # hex ↔ bytes +python3 -c "import sys;print(''.join(chr(ord(c)^0x41) for c in open('blob','rb').read().decode('latin1')))" # single-byte XOR guess +# Brute-force single-byte XOR / ROT / base64 layers: CyberChef "Magic" op, or `xortool -c 20 blob`, or `unxor` +``` + +> [!tip] Strings are cheap for the attacker to fake +> Decoy strings (legitimate-looking URLs, fake PDB paths, whole English dictionaries) are common in packed samples. A binary with **almost no** strings, or only library boilerplate, is *more* suspicious than one full of them: it is packed or the strings are built at runtime — run FLOSS, or dump it from memory after it unpacks (see [Volatility](/sheets/dfir/volatility) `windows.dumpfiles` / `malfind`). + +## Hex: `xxd`, `hexdump`, `od`, editors + +```bash +xxd sample.bin | less # canonical: offset, hex, ASCII +xxd -s 0x3C -l 4 sample.bin # -s seek, -l length: read the PE header offset +xxd -s -64 sample.bin # last 64 bytes (appended data?) +xxd -c 32 -g 1 sample.bin | head # 32 bytes per line, grouped by 1 +xxd -p sample.bin | head -c 200 # plain hex, no formatting (for grep / diff) +xxd -r -p hex.txt > out.bin # hex → binary +hexdump -C sample.bin | head # BSD-style canonical (macOS default) +od -A x -t x1z -v sample.bin | head # od: hex offsets, 1-byte hex, ASCII, no * for repeats +cmp -l a.bin b.bin | head # byte-level diff; `vbindiff a b`, `radiff2 -x a b` for visual +dd if=sample.bin bs=1 skip=$((0x1000)) count=512 2>/dev/null | xxd | head # cut a region +``` + +```powershell +Format-Hex .\sample.bin -Offset 0x3C -Count 4 +Format-Hex .\sample.bin | Select -Last 4 +[BitConverter]::ToString((Get-Content .\sample.bin -Encoding Byte -TotalCount 16)) -replace '-',' ' +``` + +GUI hex editors: **HxD** (Windows, free, huge files, disk/RAM editing, data inspector), **ImHex** (cross-platform, pattern language decodes structures for you, hex + entropy + strings + yara in one), **010 Editor** (paid, binary templates for every format), `hexyl` (terminal, coloured). Registry hive, PE and NTFS record templates exist for all three — worth it when you need to read a structure by hand. + +## Metadata: `exiftool` + +```bash +exiftool sample.jpg # everything: camera, GPS, timestamps, software, author +exiftool -G1 -a -s sample.docx # group names, all duplicates, tag names as they are in the file +exiftool -a -u -g1 -ee sample.pdf # unknown tags, embedded streams +exiftool -gps:all -n photo.jpg # coordinates in decimal +exiftool -Author -Creator -Producer -CreateDate -ModifyDate *.pdf *.doc* # who made the docs, with what +exiftool -r -csv -common -CreateDate -Author /mnt/img/home/*/Documents > docs.csv # recurse, CSV for Timeline Explorer +exiftool -time:all -a -G0:1 -s sample.docx # every timestamp incl. Office internal ones vs filesystem +exiftool -if '$gpslatitude' -r -p '$filename,$gpslatitude,$gpslongitude' . # only geotagged files +exiftool -X sample.jpg > meta.xml; exiftool -j sample.jpg > meta.json +exiftool -all= copy.jpg # strip (never on evidence — for your own redaction) +``` + +What to read: **Office** `Creator`/`LastModifiedBy`/`Company`/`RevisionNumber`/`TotalEditTime`/`Template` (`normal.dotm` vs something odd), `Application` and `AppVersion` (a "2019 Word" doc from a 2010 build); **PDF** `Producer`/`Creator` (`LibreOffice`, `Microsoft: Print To PDF`, `mPDF` from a webapp, `Ghostscript`), `CreateDate` vs `ModifyDate`, XMP history; **images** `Make`/`Model`/`Software`/`GPSPosition`/`DateTimeOriginal`, thumbnail that does not match the image (edited photo); **LNK/EXE** `exiftool` reads PE version info (`CompanyName`, `OriginalFileName` — a `svchost.exe` whose original name is `stealer.exe`). + +## Archives, containers, embedded objects + +```bash +# Office docs are ZIPs — look inside without Office +unzip -l sample.docx; unzip -p sample.docx word/vbaProject.bin | file - # macro container present? +unzip -p sample.docx word/_rels/document.xml.rels | grep -io 'target="http[^"]*"' # remote template injection +unzip -p sample.docx docProps/core.xml docProps/app.xml # metadata XML +7z l sample.bin; 7z x -osample_extracted sample.bin # 7z opens almost anything: zip, cab, msi, iso, vhd, wim, chm, pe resources +binwalk sample.bin # signature scan for embedded files (firmware, but also polyglots and stego) +binwalk -e sample.bin # extract them (into _sample.bin.extracted/) +binwalk -E sample.bin # entropy graph — flat high entropy = encrypted/compressed/packed +binwalk -R '\x50\x4b\x03\x04' sample.bin # raw byte search with offsets +foremost -t all -i sample.bin -o carved/ # carve known types out of ANY blob (same tool as disk carving below) +``` + +```bash +# PE quick look (Linux) — imports, sections, entropy, timestamps +readpe -h sample.exe; readpe -i sample.exe | head -60 # pev +pescan sample.exe # pev heuristics +python3 -c "import pefile;p=pefile.PE('sample.exe');print(p.FILE_HEADER.dump());[print(e.dll,[i.name for i in e.imports][:8]) for e in p.DIRECTORY_ENTRY_IMPORT]" +diec sample.exe # Detect It Easy: compiler, packer, protector +# Windows GUI: pestudio (indicators, imports scored, strings, VT lookup), DiE, CFF Explorer, PE-bear +``` + +## Documents: PDF and Office maldoc triage + +```bash +# PDF +pdfid.py sample.pdf # counts of /JS /JavaScript /OpenAction /AA /Launch /EmbeddedFile /URI /ObjStm — any of the first five = open it further +pdf-parser.py -a sample.pdf # stats: objects by type +pdf-parser.py --search JavaScript sample.pdf # find the object +pdf-parser.py -o 12 -f -w sample.pdf # dump object 12, filters applied, raw +pdf-parser.py --search /Launch sample.pdf; pdf-parser.py --search /URI sample.pdf +peepdf -i sample.pdf # interactive; `js_analyse` / `extract js` +qpdf --qdf --object-streams=disable sample.pdf out.pdf # decompress everything so grep/strings work on it +strings out.pdf | grep -Ei 'javascript|/JS|/Launch|/URI|/EmbeddedFile|/OpenAction|cmd|powershell' + +# Office (OLE and OOXML) — oletools +oleid sample.doc # summary: encrypted? VBA? XLM? external relationships? flash? +olevba sample.docm # dump macros + auto-exec triggers + suspicious keywords + IOCs table +olevba --decode --deobf sample.docm # decode hex/base64/StrReverse, attempt deobfuscation +olevba --reveal sample.docm # macro source with obfuscated strings replaced +oledump.py sample.doc # streams; 'M' = macro stream; -s 7 -v = decompress stream 7 +oledump.py -p plugin_http_heuristics sample.doc +mraptor sample.doc # AutoExec + Write/Execute = suspicious, one line +rtfobj sample.rtf # embedded OLE objects in RTF (Equation Editor exploits) +msodde sample.docx # DDE field attacks +xlmdeobfuscator -f sample.xls # Excel 4.0 (XLM) macros +``` + +## Hashing, fuzzy hashing, known-good/bad + +```bash +sha256sum sample.bin; md5sum sample.bin; sha1sum sample.bin +hashdeep -c md5,sha1,sha256 -r ./samples/ > hashes.txt # recursive multi-hash +hashdeep -c sha256 -r -a -k known-good.txt ./bin/ # audit dir against a known set (-a audit, -k known) +hashdeep -c sha256 -r -m -k nsrl.txt /mnt/img/usr/bin # -m: show only files that MATCH known (whitelist noise) +hashdeep -c sha256 -r -x -k nsrl.txt /mnt/img/usr/bin # -x: show only UNKNOWN files — the review list +ssdeep -b sample.bin # context-triggered piecewise hash +ssdeep -bl -r ./samples/ > fuzzy.txt; ssdeep -bm fuzzy.txt new.bin # match a new sample against a set (score 0–100) +ssdeep -d -r ./samples/ # cluster near-duplicates within a directory +tlsh -f sample.bin # TLSH — better on small/heavily-modified files +# Look up, don't upload (an upload tells the attacker you found it): +# VirusTotal hash search, MalwareBazaar, Hybrid Analysis, `vt file <sha256>` CLI, ANY.RUN search +``` + +```powershell +Get-FileHash .\sample.bin -Algorithm SHA256 # MD5, SHA1, SHA256, SHA384, SHA512 +Get-ChildItem -Recurse | Get-FileHash -Algorithm SHA256 | Export-Csv hashes.csv +certutil -hashfile .\sample.bin SHA256 # no PowerShell +certutil -hashfile .\sample.bin MD5 +sigcheck64.exe -a -h -vt .\sample.exe # Sysinternals: signature, version, hashes, VirusTotal (-vt requires accepting ToS) +sigcheck64.exe -e -u -vr -s C:\Windows\System32 > unsigned.txt # unsigned exes in a tree +``` + +## Carving: files from unallocated space, swap, memory + +```bash +# Get the blob: unallocated blocks of a partition (Sleuth Kit), or the whole image, or pagefile/hiberfil/mem +blkls -o 2048 disk.dd > unalloc.raw + +# foremost — header/footer carving, config-driven, fast +foremost -t jpg,png,pdf,doc,zip,exe -i unalloc.raw -o carved-foremost/ # -t types (or all), writes audit.txt with offsets +foremost -c /etc/foremost.conf -i disk.dd -o carved/ # custom signatures in the conf +# scalpel — same idea, faster, edit /etc/scalpel/scalpel.conf to UNCOMMENT the types you want first +scalpel -c /etc/scalpel/scalpel.conf -o carved-scalpel/ unalloc.raw +# photorec — best for media (400+ formats), interactive TUI, works on damaged fs; /cmd for scripting +photorec /log /d carved-photorec/ /cmd disk.dd fileopt,everything,enable,search +# bulk_extractor — does NOT carve files; it scans EVERY byte for FEATURES: emails, URLs, IPs, phone numbers, credit cards, +# JSON, base64, EXIF, zip/gzip/rar contents (recursively decompressed!), Windows PE headers, hex keys, domains +bulk_extractor -o be-out/ disk.dd # 10–30 min per 100 GB; multi-threaded +bulk_extractor -o be-out/ -E email -E url -E domain -E exif unalloc.raw # -E enable only listed scanners +ls be-out/ # email.txt url.txt domain.txt ip.txt telephone.txt ccn.txt exif.txt zip.txt json.txt *_histogram.txt +head be-out/url_histogram.txt be-out/email_histogram.txt # ranked — top hits first +bulk_extractor -o be-out/ -F iocs.txt disk.dd # -F: search for your own list of strings (offsets to features.txt) +bulk_extractor -o be-out/ -f 'Invoke-|-enc ' disk.dd # -f: regex +# Every hit carries a byte offset (or "offset-ZIP-offset" for compressed) → dd/xxd there, or `fiwalk`/`identify_filenames.py` to map offset → filename via the fs +identify_filenames.py --all be-out/ be-out-annotated/ --image disk.dd +``` + +```bash +# Manual carving when you know the header: find offsets, cut +grep -obUaP '\xFF\xD8\xFF\xE0' unalloc.raw | head # -o offsets, -b byte offset, -U binary, -a text, -P perl regex +python3 - <<'PY' +import re +d=open('unalloc.raw','rb').read() +for i,m in enumerate(re.finditer(rb'%PDF-1\.\d.*?\x25\x25EOF', d, re.S)): + open(f'carved_{i}.pdf','wb').write(m.group()); print(i, m.start(), len(m.group())) +PY +# Strings over the whole blob, with offsets, into the IOC pass above +strings -a -t x -n 8 unalloc.raw > unalloc-strings.txt; strings -a -t x -n 8 -e l unalloc.raw >> unalloc-strings.txt +# Windows pagefile/hiberfil: strings + bstrings work directly; decompress hiberfil first for real analysis +# hibr2bin.exe hiberfil.sys hiber.raw (Comae/Magnet) then Volatility / strings on hiber.raw +``` + +> [!info] What carving cannot do +> Fragmented files come out corrupt (only the first fragment, or garbage joined on). SSDs with TRIM zero deleted blocks quickly — carving an SSD imaged days after deletion mostly yields nothing. Encrypted/compressed containers carve as one opaque blob. Carved files have **no name, no path, no timestamps**: to get those back, match the carved file's hash/offset to `$MFT`/`fls -d` deleted entries or the USN journal. + +## YARA: match what you already know + +```bash +yara -r rules/ sample.bin # recurse a rules dir +yara -s rule.yar sample.bin # -s print matching strings + offsets +yara -m -s rules.yar sample.bin # -m print rule metadata +yara -r -w -f rules/ /mnt/img/home/ # -w no warnings, -f fast mode +yara -C compiled.yarc sample.bin; yarac rules/*.yar compiled.yarc # precompile big rule sets +yara rules.yar 1234 # scan a live PID (Linux, root) +yara -D sample.bin # dump module data (pe, elf, hash, math) +yara -d filename=sample.bin -d filesize=$(stat -c%s sample.bin) rules.yar sample.bin # external variables +yara-x scan rules/ sample.bin # yara-x: the Rust rewrite, same syntax, faster, better errors +``` + +```yara +rule triage_suspicious_pe_strings +{ + meta: + author = "DAEMON" + description = "Quick triage: PE with injection APIs + a URL + a PowerShell flag" + strings: + $api1 = "VirtualAllocEx" ascii + $api2 = "WriteProcessMemory" ascii + $api3 = "CreateRemoteThread" ascii + $url = /https?:\/\/[a-z0-9.\-]+\/[a-z0-9\/._-]*/ ascii wide nocase + $ps = "-enc" ascii wide nocase + $ps2 = "FromBase64String" ascii wide + condition: + uint16(0) == 0x5A4D and filesize < 5MB and + 2 of ($api*) and ($url or any of ($ps*)) +} +``` + +Rule sources: **Neo23x0/signature-base** (Loki/Thor), **Yara-Rules/rules**, **ReversingLabs**, **Elastic protections-artifacts**, **InQuest awesome-yara** index, ESET, Malpedia (login), plus the packer/crypto/capability rules in **mandiant/capa** (`capa sample.exe` gives you ATT&CK capabilities without writing rules). Keep an `iocs.yar` per case with the hashes, mutexes, PDB paths and URLs you pulled out of `strings` — then scan every other host's triage collection with it. + +## Ten-minute triage checklist + +```bash +f=sample.bin +file -k $f; sha256sum $f; ssdeep -b $f; exiftool -G1 -s $f | head -40 +xxd $f | head -4; xxd $f | tail -4 +strings -a -n 6 $f > s.txt; strings -a -n 6 -e l $f >> s.txt; wc -l s.txt +grep -Eio 'https?://[^ "]+|\b([0-9]{1,3}\.){3}[0-9]{1,3}\b|[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}' s.txt | sort -u +grep -Ei 'powershell|cmd\.exe|-enc|VirtualAlloc|WriteProcessMemory|CreateRemoteThread|HKCU|HKLM|\\Temp\\|\.pdb' s.txt | sort -u | head -40 +binwalk $f | head; binwalk -E $f >/dev/null && echo "see entropy png" +diec $f 2>/dev/null || pescan $f 2>/dev/null +yara -r ~/rules/ $f; capa $f 2>/dev/null | head -40 +# then decide: benign / known-bad (hash hit) / needs sandbox (ANY.RUN, CAPE, Joe) / needs a reverser +``` + +1. **Identity**: `file -k`, magic head/tail, size, entropy. Packed? Polyglot? Appended data? +2. **Hashes**: SHA-256 to VT/MalwareBazaar *search* (do not upload yet); `ssdeep` against your case corpus. +3. **Metadata**: `exiftool` — author, tool, timestamps, PDB path, version info vs filename. +4. **Strings**, both encodings, then the IOC greps. Save everything to the case notes with offsets. +5. **Structure**: imports/sections (PE), objects (PDF), macros (Office), embedded files (`binwalk`, `7z l`). +6. **Match**: YARA rule sets, capa. Write the case rule from what you found. +7. **Decide** the next stop: sandbox, [Volatility](/sheets/dfir/volatility) for a memory-resident stage, or a reverser. + +## Related + +- [EZ Tools & KAPE](/sheets/dfir/ez-tools-kape) — `bstrings`, `Hasher`, `EZViewer` on Windows +- [Linux forensics](/sheets/dfir/linux-forensics) — where unallocated space comes from (`blkls`), Sleuth Kit +- [Acquisition](/sheets/dfir/disk-imaging) — getting the disk/memory image to carve +- [Volatility 3](/sheets/dfir/volatility) — dumping unpacked binaries from memory for a second `strings` pass +- [Hashing](/sheets/cryptography/hashing) — algorithm reference