// BTLO  ·  Incident Response

Bad Import

BTLO Medium Splunk

Scenario

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.


Methodology

Identifying the C2 — DEV-WKS-01

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.

Tracing the Infection — Supply Chain via npm

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.

Identifying the Renamed Binary

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.

Persistence — Registry Run Key

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:

  • Key: HKU\...\Software\Microsoft\Windows\CurrentVersion\Run\WindowsTerminalUpdate
  • Value: C:\ProgramData\.run.bat

The dot-prefix on .run.bat and the key name WindowsTerminalUpdate are both masquerading attempts — one at the filesystem level, one at the registry level.

Credential Collection and Lateral Movement

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.

BUILD-01 — Timestamp Forgery and Deployment Abuse

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

SRV-APP-01 — Malicious Plugin, C2 Callback, and Persistence

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


Attack Summary

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 WindowsTerminalUpdateC:\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

IOCs

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)

MITRE ATT&CK

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

Defender Takeaways

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 hygienesvc_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.


Which workstation first contact the attacker's command-and-control server? Provide the Workstation, the destination IP, and the destination port
Click flag to reveal DEV-WKS-01, 142.11.206.73, 8000
From which working directory did the attacker's first payload execute on the workstation?
Click to reveal answer C:\projects\analytics-portal\node_modules\plain-crypto-js
The attacker placed a renamed Windows binary in a non-standard location. What is the actual file? As per description.
Click flag to reveal Windows PowerShell
What is the full path of the script file the attacker dropped on the workstation as the first stage of execution?
Click to reveal answer C:\Users\jcarter\AppData\Local\Temp\6202033.ps1
A persistence mechanism was established by the Attacker? Provide the registry path written, and the value content.
Click flag to reveal HKU\S-1-5-21-1004336348-11772389​15-682003330-1001\Software\Microsoft\Windows\CurrentVersion\Run\WindowsTerminalUpdate, C:\ProgramData\.run.bat
The attacker collected several files containing user credentials and secrets from the workstation. Provide the file name (in alphabetical order)(Dot file being the first).
Click to reveal answer .npmrc, ConsoleHost_history.txt, credentials, id_rsa, Login Data
Attacker then laterally moved to next machine. What is the machine and which account was used?
Click flag to reveal build01, svc_jenkinsploy
At what date and time did the attacker Logged into the next machine?
Click to reveal answer 2026-04-02, 19:35:47
On the recently compromised host, the attacker planted a file and modified an existing build script in the Jenkins workspace. To lower the suspicion they forged a creation timestamp on one of them, what fake timestamp value was applied?
Click flag to reveal 2026-01-14, 11:00:00
The attacker's modifications to compromised server enabled their planted code to reach a third host as part of a scheduled deployment. Identify the hostname of this next host and the protocol (TCP port) used to transfer files to it
Click to reveal answer SRV-APP-01, 4985
A new file appeared under the customer-portal application's plugins folder. Provide its full path?
Click flag to reveal C:\inetpub\customer-portal\plugins\telemetry.js
A process running on the recent compromised host made the first outbound connection to the attacker's infrastructure shortly after the deployment landed. Provide the process's full image path and the user account it ran as?
Click to reveal answer C:\Program Files\nodejs\node.exe, svc_portal
The attacker escalated privileges on the production server. Provide the full path of the binary used for the escalation, and the named pipe associated with the abuse.
Click flag to reveal C:\ProgramData\svc_helper.exe, spoolss
The attacker achieved persistence on the production server. Provide the name of used/registered for that.
Click to reveal answer WinTelemetrySvc
🔒
// active lab
writeup locked
withheld in accordance with platform guidelines
to avoid spoiling live challenges.
password provided to recruiters on request.