Back to BEC Investigation

Attack Timeline

POST/api/v1/bec/timeline

Build a unified attack timeline by correlating sign-in logs, inbox rules, OAuth grants, mail access patterns, message deletions, and permission changes. Events are clustered into sessions, mapped to attack phases, and risk-scored to reconstruct the full BEC kill chain.

Permissions

AuditLog.Read.All
MailboxSettings.Read
bec:read

Credits

10 per request

Plans

All plans

Timeout

180 seconds

CLI Usage

Terminal
# Build a timeline for a single user (last 30 days)
dfir-cli bec timeline --user admin@contoso.com

# Custom date range with all data sources
dfir-cli bec timeline --user admin@contoso.com \
  --since 2026-03-01 --until 2026-03-31 \
  --include-sign-ins --include-rules --include-oauth \
  --include-mail-access --include-deletions \
  --include-search --include-permissions

# Filter by IP and attack phase
dfir-cli bec timeline --user admin@contoso.com \
  --filter-ip 185.220.101.42 --filter-phase persistence

# Summary-only view grouped by phase
dfir-cli bec timeline --user admin@contoso.com \
  --summary-only --group-by phase --min-risk 60

# Import from previously exported JSON
dfir-cli bec timeline --import timeline-export.json

Flags

FlagTypeDescription
--userstringTarget user email address (UPN) to build the timeline for
--daysintNumber of days to look back (default: 30)
--sincestringStart date for the timeline window (ISO 8601)
--untilstringEnd date for the timeline window (ISO 8601)
--include-sign-insboolInclude Azure AD sign-in log events
--include-rulesboolInclude inbox rule creation/modification events
--include-oauthboolInclude OAuth consent grant events
--include-mail-accessboolInclude mailbox access and folder enumeration events
--include-deletionsboolInclude message deletion events
--include-searchboolInclude mailbox search/eDiscovery events
--include-permissionsboolInclude permission and role change events
--filter-ipstringOnly include events from this IP address
--filter-phasestringOnly include events in this attack phase
--min-riskintMinimum risk score to include (0-100, default: 0)
--group-bystringGroup events by "time" (default), "phase", or "session"
--no-correlateboolSkip session correlation and phase mapping
--summary-onlyboolReturn only the summary without individual events
--importstringImport and re-analyze a previously exported timeline JSON file

Request Body

{
  "user": "admin@contoso.com",
  "days": 30,
  "since": "2026-03-01T00:00:00Z",
  "until": "2026-03-31T23:59:59Z",
  "include_sign_ins": true,
  "include_rules": true,
  "include_oauth": true,
  "include_mail_access": true,
  "include_deletions": true,
  "include_search": true,
  "include_permissions": true,
  "filter_ip": null,
  "filter_phase": null,
  "min_risk": 0,
  "group_by": "time",
  "no_correlate": false,
  "summary_only": false
}
FieldTypeRequiredDescription
userstringYesTarget user UPN to build the attack timeline for
daysintegerNoNumber of days to look back. Default: 30. Ignored if since/until are set.
sincestringNoStart of the timeline window (ISO 8601). Overrides days.
untilstringNoEnd of the timeline window (ISO 8601). Default: now.
include_sign_insbooleanNoInclude sign-in log events. Default: true.
include_rulesbooleanNoInclude inbox rule events. Default: true.
include_oauthbooleanNoInclude OAuth consent grant events. Default: true.
include_mail_accessbooleanNoInclude mailbox access events. Default: true.
include_deletionsbooleanNoInclude message deletion events. Default: true.
include_searchbooleanNoInclude mailbox search events. Default: false.
include_permissionsbooleanNoInclude permission/role change events. Default: false.
filter_ipstringNoOnly include events originating from this IP address.
filter_phasestringNoOnly include events mapped to this attack phase.
min_riskintegerNoMinimum risk score threshold (0-100). Default: 0.
group_bystringNo"time", "phase", or "session". Default: "time".
no_correlatebooleanNoSkip session clustering and phase mapping. Default: false.
summary_onlybooleanNoReturn only the summary object. Default: false.

Response

{
  "data": {
    "events": [
      {
        "timestamp": "2026-04-08T18:16:58Z",
        "source": "oauth_grant",
        "operation": "app_consent",
        "actor": "Microsoft App Access Panel",
        "actor_ip": "",
        "actor_agent": "",
        "target": "0000000c-0000-0000-c000-000000000000",
        "details": "OAuth app consented: Microsoft App Access Panel (AppID: 0000000c-...)",
        "risk_level": "high",
        "risk_score": 40,
        "phase": "persistence",
        "session_id": "",
        "correlation_id": "9cf92b94-0873-4082-9010-dbd07b4814b0",
        "location": "",
        "confidence": 50,
        "tags": ["oauth", "consent", "third_party_app"]
      },
      {
        "timestamp": "2026-04-10T10:10:14Z",
        "source": "sign_in",
        "operation": "interactive_sign_in",
        "actor": "admin@devopsdfirlab.onmicrosoft.com",
        "actor_ip": "31.10.224.253",
        "actor_agent": "Chrome 135.0",
        "target": "",
        "details": "Sign-in to Microsoft 365 via Chrome",
        "risk_level": "low",
        "risk_score": 0,
        "phase": "info",
        "session_id": "4a8b1c2d-...",
        "correlation_id": "f9e8d7c6-...",
        "location": "Glarus, CH",
        "confidence": 50,
        "tags": []
      }
    ],
    "correlations": [],
    "sessions": [
      {
        "session_id": "session_0",
        "ip": "unknown",
        "location": "unknown",
        "user_agent": "unknown",
        "event_count": 168,
        "phases": ["persistence", "info"],
        "classification": "attacker_likely",
        "risk_score": 60,
        "first_seen": "2026-04-08T18:16:58Z",
        "last_seen": "2026-04-10T13:23:32Z"
      }
    ],
    "summary": {
      "first_compromise": "2026-04-08T18:16:58Z",
      "persistence_mechanisms": ["oauth_consent", "Add service principal", "Update user"],
      "emails_accessed": 0,
      "emails_sent": 0,
      "emails_deleted": 0,
      "blast_radius": 183,
      "attacker_ips": ["unknown", "2603:1026:2400::9", "2603:1026:2407::2b"],
      "attacker_sessions": 4,
      "phases_observed": ["persistence", "initial_access"],
      "kill_chain_complete": false,
      "total_events": 227,
      "by_phase": { "persistence": 191, "initial_access": 15, "info": 21 },
      "by_source": { "oauth_grant": 168, "sign_in": 15, "directory_audit": 44 },
      "critical": 0,
      "high": 171,
      "medium": 23,
      "low": 33
    }
  },
  "meta": {
    "request_id": "req_abc123",
    "credits_used": 10,
    "credits_remaining": 4923,
    "processing_time_ms": 4520
  }
}
FieldTypeDescription
data.eventsarrayChronologically ordered array of timeline events
data.events[].idstringUnique event identifier
data.events[].timestampstringISO 8601 timestamp of when the event occurred
data.events[].phasestringAttack phase classification (see Attack Phases below)
data.events[].typestringEvent type (sign_in, mail_access, inbox_rule_created, smtp_forwarding, etc.)
data.events[].risk_scorenumberRisk score from 0 to 100 for this individual event
data.events[].summarystringHuman-readable summary of what happened
data.events[].detailsobjectEvent-specific details (IP, user agent, rule actions, etc.)
data.events[].session_idstringCorrelated session identifier linking related events
data.correlationsarraySession clusters grouping related events by IP and time proximity
data.sessionsobjectSession summary: total count, attacker sessions, unique IPs
data.summaryobjectAggregate statistics: event counts by phase, risk distribution, time range
data.summary.attack_duration_hoursnumberTotal duration of the attack from first to last event in hours
meta.credits_usednumberCredits consumed by this request (10)
meta.credits_remainingnumberRemaining credit balance after this request

Attack Phases

Each event is automatically classified into one of five BEC attack phases based on its type, timing, and context. The correlation engine uses session clustering and temporal proximity to map events to the kill chain.

PhaseDescriptionTypical Events
initial_accessAttacker gains access to the mailboxAnomalous sign-ins, MFA bypass, token theft
reconAttacker explores the mailbox and organizationBulk mail reads, folder enumeration, GAL access
persistenceAttacker establishes ongoing access or exfiltrationInbox rules, SMTP forwarding, OAuth grants, delegate permissions
impersonationAttacker sends emails as the compromised userOutbound emails to internal targets, wire transfer requests
cleanupAttacker covers tracks by deleting evidenceBulk deletions, sent item removal, rule deletion

Session Correlation

The correlation engine clusters events into sessions using IP address, user agent, and temporal proximity. Events from the same IP within a configurable time window are grouped into a single session, making it easy to distinguish attacker activity from legitimate user behavior.

Use --no-correlate to disable session clustering and receive raw, uncorrelated events. Use --group-by session to organize the output by session rather than chronologically.

Important Notes

  • Requires an active Microsoft 365 connection. Run dfir-cli bec status to verify your session before building a timeline.
  • AuditLog.Read.All is required to access the unified audit log. Without it, only sign-in and mailbox settings data will be available.
  • Microsoft 365 audit logs are retained for 90 days on standard plans and 1 year with E5/G5 licensing. The --days flag cannot exceed the tenant retention period.
  • Timeline construction queries multiple Microsoft Graph endpoints. For large date ranges (60+ days), expect processing times of 30-60 seconds. The 180-second timeout covers worst-case scenarios.
  • Costs 10 credits per request. Use --summary-only for a quick overview before running a full timeline.
  • Use --import to re-analyze a previously exported timeline offline without consuming additional credits or requiring an active M365 connection.