Manual Timeline Building
Automated tools (Plaso, Timesketch) excel at scale but sometimes you need a curated, analyst-built timeline: for a court presentation, for stakeholder communication, or when only a handful of key artifacts are relevant. This chapter covers building clean, narrative-quality timelines manually — the kind that go into executive reports.
Legal has asked for a clean 12-event timeline of the attack: the initial access, the credential theft, the lateral movement, and the exfiltration — each event with the supporting evidence and source. Plaso has 1.4 million events. You need to distill those into a 12-row timeline that tells the story to a non-technical audience without losing forensic precision. This chapter covers that distillation process.
Manual Timeline Format
Investigation Timeline — CASE-2026-009
═══════════════════════════════════════════════════════════════════
Columns:
Timestamp (UTC) | Host | Event | Evidence Source | Notes
2026-09-17 01:47:22 | FINANCE-SRV01 | Cobalt Strike beacon executed
Source: Prefetch — svchst.exe.pf (run at 01:47)
Notes: Located in C:\Windows\Temp\; filename mimics svchost.exe
2026-09-17 01:47:24 | Firewall | First C2 check-in to 185.220.101.47
Source: Firewall log — HTTPS egress rule, src FINANCE-SRV01
Notes: 2 seconds after beacon execution (consistent with CS default)
2026-09-17 02:13:05 | DC01 | jsmith's credentials used from FINANCE-SRV01
Source: Event 4624 on DC01 — Network logon Type 3 from 10.10.5.20
Notes: jsmith is Finance Director with broad file server access
2026-09-17 02:14:01 | FS01 | Mass file access — Finance Q3 reports
Source: Event 4663 on FS01 — 847 file accesses by jsmith in 3 minutes
Notes: Abnormal — jsmith's normal daily access count is 15-20 files
...
(continue through all key events)
Evidence-to-Event Mapping Script
#!/usr/bin/env python3
"""
Build a curated investigation timeline from CSV exports of individual artifacts.
Combines Plaso CSV, manual notes, and artifact CSVs into a sorted master timeline.
"""
import csv
import json
from datetime import datetime, timezone
TIMELINE_ENTRIES = []
def add_event(timestamp_str, host, description, source, notes="", confidence="HIGH"):
"""Add a manually identified key event to the master timeline."""
dt = datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
TIMELINE_ENTRIES.append({
"datetime": dt.strftime("%Y-%m-%d %H:%M:%S UTC"),
"host": host,
"description": description,
"source": source,
"notes": notes,
"confidence": confidence
})
# Add manually identified key events
add_event("2026-09-17T01:47:22Z", "FINANCE-SRV01",
"Cobalt Strike beacon executed (svchst.exe)",
"Prefetch: C:\\Windows\\Prefetch\\SVCHST.EXE-A1B2C3D4.pf",
"Filename mimics svchost.exe; located in C:\\Windows\\Temp\\")
add_event("2026-09-17T01:47:24Z", "FINANCE-SRV01",
"First C2 check-in to 185.220.101.47:443",
"Firewall log — rule EGRESS_HTTPS; src 10.10.5.20 dst 185.220.101.47",
"2 second delay post-execution consistent with CS default sleep setting")
add_event("2026-09-17T02:13:05Z", "DC01",
"jsmith credentials used via network logon from FINANCE-SRV01",
"Event 4624 on DC01 (EventRecordID 1023847)",
"Logon Type 3 (network); NTLMSSP authentication; jsmith = Finance Director")
add_event("2026-09-17T02:14:01Z", "FS01",
"Mass file access — 847 Finance files in 3 minutes",
"Event 4663 on FS01 — Object Access audit; jsmith account",
"Accessed \\\\FS01\\Finance\\2026\\Q3\\ — revenue forecasts and customer contracts")
add_event("2026-09-17T02:55:30Z", "FINANCE-SRV01",
"Staging archive created: C:\\Windows\\Temp\\update.zip (3.1 GB)",
"MFT record 123458 — creation timestamp; TSK fls output",
"File deleted at 03:02:14 after upload; partially recovered via icat")
add_event("2026-09-17T02:58:12Z", "Firewall",
"3.1 GB HTTPS upload to 45.132.10.77 from FINANCE-SRV01",
"Firewall log + NetFlow (nfdump output)",
"Second external IP — likely exfiltration staging server distinct from C2")
# Sort and write
TIMELINE_ENTRIES.sort(key=lambda x: x['datetime'])
output_file = "/cases/CASE-2026-009/MASTER-TIMELINE.csv"
with open(output_file, 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=["datetime","host","description","source","notes","confidence"])
writer.writeheader()
writer.writerows(TIMELINE_ENTRIES)
print(f"Master timeline: {len(TIMELINE_ENTRIES)} events → {output_file}")
# Also output as formatted text for report
print("\nATTACK TIMELINE")
print("=" * 80)
for e in TIMELINE_ENTRIES:
print(f"\n{e['datetime']} {e['host']}")
print(f" {e['description']}")
print(f" Evidence: {e['source']}")
if e['notes']:
print(f" Notes: {e['notes']}")
The Pivot Technique
In a manual timeline, each event should be a pivot point — it answers the current question and points to the next:
| Event identified | Pivot question it raises | Where to look next |
|---|---|---|
| Beacon executed at 01:47 | How did the beacon get there? | MFT creation time = 01:47 but Zone.Identifier = downloaded from internet at 01:45 → phishing delivery |
| jsmith credentials used at 02:13 | How did the attacker get jsmith's credentials? | Memory — LSASS at time of beacon → jsmith was logged into FINANCE-SRV01 (TS session) |
| 847 files accessed on FS01 | What specific files were accessed? | Event 4663 details — each access logs the full path; build file list for scope assessment |
| 3.1 GB upload at 02:58 | Is this the only exfiltration event? | NetFlow — check all sessions to 45.132.10.77 for full 47-day period; SRUM for bandwidth history |
Q & A
Q: How precise should timestamps be in the manual timeline — to the second, or is the minute sufficient?
Use the precision that the evidence actually supports. Event logs and MFT timestamps have second-level precision — use them. Firewall logs are often per-second. NetFlow has per-second start times. So your forensic working document should use second-level precision for each event (it shows rigor and lets you sequence events within the same minute). For the executive summary version, minute-level precision is acceptable and easier to read. The rule: always document the full precision in the evidence record, then round down for presentation. If you write "02:13 UTC" in a report, your notes should show "02:13:05 UTC (Event 4624 on DC01, RecordID 1023847)" — so anyone who questions your timeline can verify the exact source. Court presentations and legal proceedings require exact second-level timestamps with source citations; executive summaries can use minutes.