Meridian Labs is a 240-person engineering company with one customer-facing application. On Friday morning, three Windows machines begin communicating with an unknown external server. Engineering reports no changes were made that week. The investigation spans three hosts — DEV-WKS-01, BUILD-01, and SRV-APP-01 — tracing a supply chain compromise from initial infection through to production server persistence.
Starting with a broad destination IP survey across all hosts to surface external connections:
index=* sourcetype=_json DestinationIp="*"
| stats count by DestinationIp
| sort - count

Filtering out known-good IPs (GitHub 140.82.114.3, Microsoft ranges, loopback) leaves a shortlist of suspicious destinations. Adding timestamps to identify the first contact:
index=* sourcetype=_json DestinationIp IN ("117.12.214.46","142.11.206.73","75.193.107.196","63.62.199.89","160.44.93.98","179.212.234.136")
| stats earliest(_time) as first_seen by host, DestinationIp, DestinationPort
| eval first_seen=strftime(first_seen,"%Y-%m-%d %H:%M:%S")
| sort first_seen

VirusTotal confirms 142.11.206.73 as malicious — 19/91 detections, hosted on Hostwinds LLC infrastructure.
Show Image
Pivoting on the confirmed C2 IP to identify patient zero:
index=* sourcetype=_json DestinationIp="142.11.206.73"
| table _time, host, DestinationIp, DestinationPort, Image, CommandLine
| sort _time

DEV-WKS-01 made the first connection at 2026-03-31 00:45:23 from C:\ProgramData\wt.exe on port 8000 — beaconing at approximately one-minute intervals, consistent with a C2 implant.
Narrowing to process creation events around the time of first C2 contact on DEV-WKS-01:
index=* sourcetype=_json host=DEV-WKS-01 EventID=1
| where _time >= strptime("2026-03-31 00:30:00", "%Y-%m-%d %H:%M:%S")
AND _time <= strptime("2026-03-31 00:50:00", "%Y-%m-%d %H:%M:%S")
| table _time, Image, CommandLine, CurrentDirectory, ParentImage, ParentCommandLine
| sort _time

The process chain tells the full story. A git pull origin main from C:\projects\analytics-portal pulled a poisoned repository. npm install followed, pulling in the malicious package plain-crypto-js. node setup.js then executed from within the package directory — the supply chain payload. Seconds later, wt.exe spawned executing a PowerShell script dropped to the user’s temp folder.
The full infection chain:
git pull → npm install → node setup.js (C:\projects\analytics-portal\node_modules\plain-crypto-js)
→ wt.exe → C:\Users\jcarter\AppData\Local\Temp\6202033.ps1 → C2 beacon
This aligns with threat intelligence for the Axios supply chain compromise reported 2026-04-01.
wt.exe in C:\ProgramData\ is immediately suspicious — Windows Terminal’s legitimate path is under WindowsApps. Querying the PE description field from Sysmon process creation metadata confirms the true identity:
index=* sourcetype=_json host=DEV-WKS-01 Image="C:\\ProgramData\\wt.exe"
| table _time, Image, Description, Product, Company, CommandLine
| sort _time
| head 1

The Description field reads Windows PowerShell — the attacker renamed powershell.exe to wt.exe and placed it in a non-standard directory to evade process-based detections.
index=* sourcetype=_json host=DEV-WKS-01 EventID=13
| table _time, TargetObject, Details, Image
| sort _time

node.exe wrote a registry run key at 00:45:22 — one second before the first C2 beacon:
HKU\...\Software\Microsoft\Windows\CurrentVersion\Run\WindowsTerminalUpdateC:\ProgramData\.run.batThe dot-prefix on .run.bat and the key name WindowsTerminalUpdate are both masquerading attempts — one at the filesystem level, one at the registry level.
Pivoting on the ps1 script to trace post-exploitation activity:
index=* sourcetype=_json host=DEV-WKS-01
| search CommandLine="*6202033*" OR Image="*6202033*" OR ParentCommandLine="*6202033*"
| table _time, EventID, Image, CommandLine, TargetFilename
| sort _time

The ps1 orchestrated systematic credential harvesting via cmd.exe /c copy, staging files to C:\Users\jcarter\AppData\Local\Temp\ as single-character .tmp files:
| Source File | Staged As |
|---|---|
Chrome Login Data |
ld.tmp |
AppData\Roaming\npm\.npmrc |
n.tmp |
.aws\credentials |
a.tmp |
.ssh\id_rsa |
k.tmp |
PowerShell ConsoleHost_history.txt |
h.tmp |
The attacker also queried cmdkey /list and reg query HKCU\Software\SimonTatham\PuTTY\Sessions — enumerating stored Windows credentials and PuTTY saved sessions.

Lateral movement immediately followed — powershell.exe invoked Invoke-Command against BUILD-01.meridian.local using credentials MERIDIAN\svc_jenkinsploy, with an initial recon ScriptBlock of whoami; hostname; dir C:\Jenkins\workspace. The logon timestamp: 2026-04-02 19:35:47.
On BUILD-01, the attacker planted a file in the Jenkins workspace and modified the existing build.bat to include their payload. To reduce suspicion, they forged the creation timestamps on both files using Sysmon EventID=2 (file creation time changed):
index=* sourcetype=_json host=BUILD-01 EventID=2
| table _time, TargetFilename, CreationUtcTime, PreviousCreationUtcTime, Image
| sort _time

Both C:\Jenkins\workspace\customer-portal\build.bat and C:\Jenkins\workspace\customer-portal\dist\customer-portal\app.js had their timestamps backdated to 2026-01-14 11:00:00.000 — making them appear to have existed since January, well before the compromise.
The modified build script triggered a scheduled Jenkins deployment to SRV-APP-01 via Robocopy over TCP port 4985:
index=* sourcetype=_json host=BUILD-01 EventID=3 DestinationIp="10.20.5.41"
| table _time, Image, DestinationIp, DestinationPort, DestinationHostname
| sort _time

The Robocopy deployment landed telemetry.js in the customer-portal plugins folder — the attacker’s malicious Node.js plugin masquerading as a telemetry module:
index=* sourcetype=_json host=SRV-APP-01 EventID=11
| search TargetFilename="*plugin*" OR TargetFilename="*customer-portal*"
| table _time, TargetFilename, Image
| sort _time

Within minutes, node.exe running as MERIDIAN\svc_portal established the first outbound C2 connection from SRV-APP-01 to 75.193.107.196:8443 — the malicious plugin executing as the portal service account and phoning home.
index=* sourcetype=_json host=SRV-APP-01 EventID=3
| where _time >= strptime("2026-04-03 08:24:00", "%Y-%m-%d %H:%M:%S")
| search DestinationIp!="10.*" DestinationIp!="192.168.*" DestinationIp!="172.*"
| table _time, Image, DestinationIp, DestinationPort, User
| sort _time

Privilege escalation followed via named pipe impersonation — C:\ProgramData\svc_helper.exe abused the \spoolss named pipe (PrintSpooler abuse) to elevate privileges on the production server:
index=* sourcetype=_json host=SRV-APP-01 EventID=17 OR EventID=18
| table _time, EventType, PipeName, Image, User
| sort _time

Persistence was established by registering a new Windows service WinTelemetrySvc — binary at C:\ProgramData\WinTelemetry\svc.exe, DisplayName Windows Telemetry Service — designed to survive reboots and blend into the service list:
index=* sourcetype=_json host=SRV-APP-01 EventID=12 OR EventID=13
| table _time, EventType, TargetObject, Details, Image
| sort _time

| Phase | Action |
|---|---|
| Initial Access | Poisoned plain-crypto-js npm package pulled via git pull on analytics-portal repo |
| Execution | node setup.js executes payload from node_modules\plain-crypto-js, drops 6202033.ps1 |
| Defense Evasion | powershell.exe renamed to wt.exe, placed in C:\ProgramData\ |
| C2 | wt.exe beacons to 142.11.206.73:8000 at one-minute intervals |
| Persistence (WKS) | Registry run key WindowsTerminalUpdate → C:\ProgramData\.run.bat |
| Collection | Chrome Login Data, .npmrc, .aws/credentials, .ssh/id_rsa, PSReadLine history staged to Temp |
| Lateral Movement | Invoke-Command to BUILD-01 as MERIDIAN\svc_jenkinsploy — 2026-04-02 19:35:47 |
| Defense Evasion | Jenkins workspace files backdated to 2026-01-14 11:00:00 |
| Lateral Movement | Modified build.bat deploys via Robocopy to SRV-APP-01 over TCP 4985 |
| Execution (Prod) | telemetry.js plugin loaded by node.exe as svc_portal, beacons to 75.193.107.196:8443 |
| Privilege Escalation | svc_helper.exe abuses \spoolss named pipe for local privilege escalation |
| Persistence (Prod) | WinTelemetrySvc service registered at C:\ProgramData\WinTelemetry\svc.exe |
| Type | Value |
|---|---|
| IP (C2) | 142[.]11[.]206[.]73 |
| IP (C2) | 75[.]193[.]107[.]196 |
| IP (C2) | 117[.]12[.]214[.]46 |
| Port (C2) | 8000 |
| Port (C2) | 8443 |
| File | C:\ProgramData\wt.exe (renamed powershell.exe) |
| File | C:\ProgramData.run.bat |
| File | C:\Users\jcarter\AppData\Local\Temp\6202033.ps1 |
| File | C:\ProgramData\svc_helper.exe |
| File | C:\ProgramData\WinTelemetry\svc.exe |
| File | C:\inetpub\customer-portal\plugins\telemetry.js |
| Package | plain-crypto-js (malicious npm package) |
| Registry | HKU…\CurrentVersion\Run\WindowsTerminalUpdate |
| Service | WinTelemetrySvc |
| Named Pipe | \spoolss |
| Account | MERIDIAN\svc_jenkinsploy (used for lateral movement) |
| Account | MERIDIAN\svc_portal (used for C2 callback) |
| Technique | ID | Description |
|---|---|---|
| Compromise Software Supply Chain | T1195.002 | Malicious plain-crypto-js npm package introduced via poisoned repository |
| PowerShell | T1059.001 | 6202033.ps1 executed via renamed powershell.exe; Invoke-Command lateral movement |
| Masquerading: Rename System Utilities | T1036.005 | powershell.exe renamed to wt.exe in C:\ProgramData\ |
| Boot or Logon Autostart: Registry Run Keys | T1547.001 | WindowsTerminalUpdate run key pointing to .run.bat |
| Unsecured Credentials | T1552.001 | Chrome Login Data, .aws/credentials, .npmrc, .ssh/id_rsa, PSReadLine history harvested |
| Remote Services: WinRM | T1021.006 | Invoke-Command used to move laterally to BUILD-01 |
| Timestomp | T1070.006 | Jenkins workspace files backdated to 2026-01-14 11:00:00 |
| Ingress Tool Transfer | T1105 | svc_helper.exe, wt.exe, telemetry.js dropped to non-standard locations |
| Create or Modify System Process: Windows Service | T1543.003 | WinTelemetrySvc registered for persistent execution on SRV-APP-01 |
| Process Injection via Named Pipe | T1055 | svc_helper.exe abuses \spoolss named pipe for privilege escalation |
| Application Layer Protocol | T1071.001 | C2 beaconing over HTTP port 8000 and HTTPS port 8443 |
npm package integrity is not guaranteed by name — The malicious plain-crypto-js package was pulled as a dependency through a normal npm install workflow. Dependency pinning with integrity hashes (package-lock.json committed and verified), private registry mirroring, and tools like npm audit and Socket.dev in CI pipelines would have flagged or blocked the malicious package before execution.
PE description fields survive renaming — Renaming powershell.exe to wt.exe fools a naive process name check but not Sysmon. EventID=1 logs the PE Description, Product, and Company metadata regardless of filename. SIEM rules alerting on Description=Windows PowerShell running from non-standard paths (!= C:\Windows\System32\) catch this class of evasion reliably.
Timestomping is detectable via Sysmon EventID=2 — The attacker backdated Jenkins workspace files to January to hide the intrusion timeline. Sysmon EventID=2 records both the new (forged) and previous (real) creation timestamps, making the forgery immediately visible. Any PreviousCreationUtcTime that differs significantly from the _time of the modification event is a high-fidelity indicator.
Service account credential hygiene — svc_jenkinsploy credentials were harvested from the developer workstation — likely stored in .npmrc, AWS credentials, or PSReadLine history — and used directly for lateral movement. Service accounts with lateral movement capability should follow least privilege and never have credentials stored on developer endpoints.
Malicious CI/CD pipeline abuse is high-impact — Modifying build.bat on the build server allowed the attacker to reach production through a trusted, scheduled deployment process. The payload arrived on SRV-APP-01 via Robocopy — a legitimate Windows binary — making it indistinguishable from normal deployment traffic without file integrity monitoring on the Jenkins workspace and the production deployment target.