top of page

ScreenConnect Partner Compromise: Hunt the Second Client

Writer: Inception Security
Inception Security
11 minutes ago
8 min read
A four-panel comic: a technician about to connect, a second ScreenConnect client waiting, the instruction not to connect, and Hunt the Second Client.

What this means for your clients: If your IT provider uses ScreenConnect to reach your computers, ask them today whether any machine has a second ScreenConnect client you did not approve, and do not have them remote in until they have checked.

 

On November 11, 2026, at 3:45 PM Arizona time (MST), ConnectWise published a ScreenConnect advisory about unauthorized access to partner accounts and unauthorized remote-access software in customer environments. Cause and scope are still under investigation, and how the accounts were taken is not established. This is not a confirmed product vulnerability, and not a platform breach at ConnectWise.

 

Starting at about 10:48 AM MST the same day (17:48 UTC), Huntress saw an unauthorized party use Run Command on ScreenConnect instances to install a second, attacker-controlled ScreenConnect client and a toolkit across groups of managed endpoints. Sophos X-Ops is investigating the same campaign and, in one case, watched curl.exe pull ScreenConnect.ClientSetup.msi from 185.99.255.28 to C:\Windows\TEMP\sc.msi, then a ScreenConnect-generated command file launch msiexec to install that client silently.

 

How a trusted Run Command planted a second client

 

Someone obtained a partner account, by a method still unknown, and used Run Command, the same control an MSP already has, to install a real ScreenConnect client that reports to attacker infrastructure, outside the partner's own console. The authority to put it there was the partner account. The instance it phones home to is not yours. The same command then retrieved a PowerShell toolkit, installed a scheduled task named TrapWatch that runs as SYSTEM every 10 seconds, and placed a modified client that impersonates the legitimate one. The toolkit is designed to spread to a technician who connects, including from a new or trial instance. Huntress has not observed a successful jump from an affected host to a technician workstation, or from that workstation to other endpoints.

 

Sophos also saw PowerShell with execution-policy bypass retrieving a script from ns.mo7sen.dev/EadF.php?p=4. The published instance IDs are in Hunt 2 and in the indicator list below, and they are not a complete list.

 

ConnectWise's initial assessment suspects the actor used five independent third-party extensions partners may have added: Labtech Plus, RMM Plus, RMM+, RMM Plus Password Link, and RMM+ Password Link. It found about 400 partner environments with one or more of them and disabled them as a precaution. That is not a confirmed root cause, and having them is not proof of impact. On-prem partners have to disable them themselves, and nobody should re-enable them pending further guidance. Disabling them is containment, not cleanup. Partners who have not deployed them are asked to stay on alert and follow the ScreenConnect hardening guide.

 

What this means for a company

 

For an MSP, this is a blast-radius problem. One compromised partner instance can touch every host that instance can Run Command against, and the second client is a backdoor the partner may never see in their own ScreenConnect view. A well-intentioned support session can extend the compromise into the MSP's other customers.

 

What to do first

 

Do not connect with ScreenConnect to a suspected host from any instance, including a new or trial instance, and investigate with other tooling instead. Treat every technician workstation that connected to an affected host as compromised until checked. If the five extensions are present, disable them and leave them off. From a trusted device, not a technician workstation that may already be compromised, revoke active ScreenConnect and identity-provider sessions, reset relevant passwords, enforce MFA, remove unknown users, and review session groups, extensions, and Toolbox. Rotate credentials on affected hosts, starting with domain admin, service, backup, hypervisor, and browser-saved credentials, and assume domain-level exposure where domain controllers were hit.

 

Preserve authentication, extension, administrative, Run Command, and Toolbox records. An update does not replace investigation. Block the indicators below at the firewall and DNS. Isolation blocks the attacker's remote access but does not remove the toolkit, and disabling extensions and resetting credentials alone may not remove access already on endpoints.

 

Before you hunt

 

We wrote these from vendor documentation and haven't run them in a live SIEM, so test each one first.

 

The first place to look is the ScreenConnect instance itself: Run Command and Toolbox history for unexpected mass commands, unexpected client deployments, or commands that download a ScreenConnect installer or a script. Those logs reach Splunk, Elastic, CrowdStrike or Microsoft KQL only if you already forward them. The hunts below use endpoint telemetry from 2026-11-11T07:00:00Z, midnight November 11 Arizona time (MST). YOUR_ names are placeholders, and an empty result can mean no data, because scheduled-task and file events can be sampled or missing.

 

Run every hunt across your managed endpoints, and run it again on any technician workstation that connected to a host that later showed these indicators.

 

Hunt 1: curl, msiexec and PowerShell that fetch the second client

 

This catches the process chain Sophos published: curl.exe retrieving the installer, msiexec installing it, and PowerShell retrieving the toolkit script. A planned install from your own instance should not pull ScreenConnect.ClientSetup.msi from that address or write C:\Windows\TEMP\sc.msi.

 

Field mapping: All four read the process name and command line, and Falcon's FileName is the base name.

 

Splunk

 

index=YOUR_SYSMON_INDEX sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1 earliest=1794380400 latest=now
| eval pname=lower(Image), cmd=lower(CommandLine)
| where (match(pname, "curl\\.exe$") AND match(cmd, "screenconnect\\.clientsetup\\.msi|sc\\.msi|185\\.99\\.255\\.28")) OR (match(pname, "msiexec\\.exe$") AND match(cmd, "screenconnect\\.clientsetup\\.msi|sc\\.msi|185\\.99\\.255\\.28")) OR (match(pname, "powershell\\.exe$") AND match(cmd, "ns\\.mo7sen\\.dev|eadf\\.php"))
| stats count AS runs min(_time) AS first_seen max(_time) AS last_seen values(CommandLine) AS commands BY host Image User

 

Elastic

 

FROM logs-endpoint.events.process-*
| WHERE @timestamp >= TO_DATETIME("2026-11-11T07:00:00Z")
| EVAL pname = TO_LOWER(COALESCE(`process.name`, "")), cmd = TO_LOWER(COALESCE(`process.command_line`, ""))
| WHERE (pname == "curl.exe" AND (cmd LIKE "*screenconnect.clientsetup.msi*" OR cmd LIKE "*sc.msi*" OR cmd LIKE "*185.99.255.28*")) OR (pname == "msiexec.exe" AND (cmd LIKE "*screenconnect.clientsetup.msi*" OR cmd LIKE "*sc.msi*" OR cmd LIKE "*185.99.255.28*")) OR (pname == "powershell.exe" AND (cmd LIKE "*ns.mo7sen.dev*" OR cmd LIKE "*eadf.php*"))
| STATS runs = COUNT(*), first_seen = MIN(@timestamp), last_seen = MAX(@timestamp), commands = VALUES(`process.command_line`) BY `host.name`, pname, `user.name`

 

CrowdStrike

 

#event_simpleName=ProcessRollup2
| test(@timestamp >= 1794380400000)
| (FileName=/^curl\.exe$/i and CommandLine=/ScreenConnect\.ClientSetup\.msi|sc\.msi|185\.99\.255\.28/i) or (FileName=/^msiexec\.exe$/i and CommandLine=/ScreenConnect\.ClientSetup\.msi|sc\.msi|185\.99\.255\.28/i) or (FileName=/^powershell\.exe$/i and CommandLine=/ns\.mo7sen\.dev|EadF\.php/i)
| default(field=UserName, value="-")
| groupBy([aid, FileName, UserName], function=[count(as=runs), min(@timestamp, as=first_seen), max(@timestamp, as=last_seen), collect([CommandLine])])

 

Microsoft KQL

 

DeviceProcessEvents
| where Timestamp >= datetime(2026-11-11T07:00:00Z)
| where (FileName =~ "curl.exe" and (ProcessCommandLine contains "ScreenConnect.ClientSetup.msi" or ProcessCommandLine contains "sc.msi" or ProcessCommandLine contains "185.99.255.28"))
    or (FileName =~ "msiexec.exe" and (ProcessCommandLine contains "ScreenConnect.ClientSetup.msi" or ProcessCommandLine contains "sc.msi" or ProcessCommandLine contains "185.99.255.28"))
    or (FileName =~ "powershell.exe" and (ProcessCommandLine contains "ns.mo7sen.dev" or ProcessCommandLine contains "EadF.php"))
| summarize Runs = count(), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Commands = make_set(ProcessCommandLine, 50) by DeviceName, FileName, AccountName

 

Hunt 2: a ScreenConnect client with a published instance ID

 

This looks for the two attacker client IDs Huntress and Sophos published, and for the three origin instance IDs Sophos called likely compromised. The first two are the high-confidence hits. An empty result on the origin list does not mean you are clean.

 

Field mapping: All four search process paths, command lines and file paths for the five IDs, and CrowdStrike reads ImageFileName for processes and TargetFileName for written files.

 

Splunk

 

index=YOUR_SYSMON_INDEX sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" (EventCode=1 OR EventCode=11) earliest=1794380400 latest=now
| eval blob=lower(coalesce(Image,"") . " " . coalesce(CommandLine,"") . " " . coalesce(TargetFilename,""))
| where match(blob, "2588ef5c15954b10|ed630cc2c873b091|d905b2b1b165d0a9|4b8f4089ddaac6a9|df1f7ef0cfacd0d8")
| stats count AS events min(_time) AS first_seen max(_time) AS last_seen values(Image) AS images values(CommandLine) AS commands values(TargetFilename) AS files BY host

 

Elastic

 

FROM logs-endpoint.events.process-*, logs-endpoint.events.file-*
| WHERE @timestamp >= TO_DATETIME("2026-11-11T07:00:00Z")
| EVAL blob = TO_LOWER(CONCAT(COALESCE(`process.executable`, ""), " ", COALESCE(`process.command_line`, ""), " ", COALESCE(`file.path`, "")))
| WHERE blob LIKE "*2588ef5c15954b10*" OR blob LIKE "*ed630cc2c873b091*" OR blob LIKE "*d905b2b1b165d0a9*" OR blob LIKE "*4b8f4089ddaac6a9*" OR blob LIKE "*df1f7ef0cfacd0d8*"
| STATS events = COUNT(*), first_seen = MIN(@timestamp), last_seen = MAX(@timestamp), commands = VALUES(`process.command_line`), files = VALUES(`file.path`) BY `host.name`

 

CrowdStrike

 

#event_simpleName=/(ProcessRollup2|FileWritten|PeFileWritten)/
| test(@timestamp >= 1794380400000)
| ImageFileName=/(2588ef5c15954b10|ed630cc2c873b091|d905b2b1b165d0a9|4b8f4089ddaac6a9|df1f7ef0cfacd0d8)/i or TargetFileName=/(2588ef5c15954b10|ed630cc2c873b091|d905b2b1b165d0a9|4b8f4089ddaac6a9|df1f7ef0cfacd0d8)/i or CommandLine=/(2588ef5c15954b10|ed630cc2c873b091|d905b2b1b165d0a9|4b8f4089ddaac6a9|df1f7ef0cfacd0d8)/i or FileName=/(2588ef5c15954b10|ed630cc2c873b091|d905b2b1b165d0a9|4b8f4089ddaac6a9|df1f7ef0cfacd0d8)/i
| default(field=[FileName], value="-")
| groupBy([aid, #event_simpleName, FileName], function=[count(as=events), min(@timestamp, as=first_seen), max(@timestamp, as=last_seen), collect([ImageFileName, TargetFileName, CommandLine])])

 

Microsoft KQL

 

union DeviceProcessEvents, DeviceFileEvents
| where Timestamp >= datetime(2026-11-11T07:00:00Z)
| where FolderPath has_any ("2588ef5c15954b10", "ed630cc2c873b091", "d905b2b1b165d0a9", "4b8f4089ddaac6a9", "df1f7ef0cfacd0d8")
    or ProcessCommandLine has_any ("2588ef5c15954b10", "ed630cc2c873b091", "d905b2b1b165d0a9", "4b8f4089ddaac6a9", "df1f7ef0cfacd0d8")
    or FileName has_any ("2588ef5c15954b10", "ed630cc2c873b091", "d905b2b1b165d0a9", "4b8f4089ddaac6a9", "df1f7ef0cfacd0d8")
    or InitiatingProcessFolderPath has_any ("2588ef5c15954b10", "ed630cc2c873b091", "d905b2b1b165d0a9", "4b8f4089ddaac6a9", "df1f7ef0cfacd0d8")
    or InitiatingProcessCommandLine has_any ("2588ef5c15954b10", "ed630cc2c873b091", "d905b2b1b165d0a9", "4b8f4089ddaac6a9", "df1f7ef0cfacd0d8")
| summarize Events = count(), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Folders = make_set(FolderPath, 20), Commands = make_set(ProcessCommandLine, 20) by DeviceName

 

Hunt 3: the TrapWatch scheduled task and RelayTrap files

 

Huntress published a scheduled task named TrapWatch, running as SYSTEM every 10 seconds, a folder at C:\ProgramData\RelayTrap\, and stager files matching C:\Windows\Temp\sckit-*. A hit is incident response, and the file path is the stronger signal if task telemetry is thin.

 

Field mapping: Splunk adds Security event 4698 when present, Elastic uses file and process indexes, Falcon uses ProcessRollup2, FileWritten and ScheduledTaskRegistered, and KQL unions DeviceFileEvents, DeviceProcessEvents and DeviceEvents.

 

Splunk

 

(index=YOUR_SYSMON_INDEX sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" (EventCode=1 OR EventCode=11) earliest=1794380400 latest=now) OR (index=YOUR_SECURITY_INDEX EventCode=4698 earliest=1794380400 latest=now)
| eval blob=lower(coalesce(TargetFilename, "") . " " . coalesce(CommandLine, "") . " " . coalesce(Image, "") . " " . coalesce(TaskName, Task_Name, ""))
| where match(blob, "trapwatch|relaytrap|sckit-") OR (EventCode=4698 AND match(lower(_raw), "trapwatch"))
| stats count AS events min(_time) AS first_seen max(_time) AS last_seen values(TargetFilename) AS files values(CommandLine) AS commands values(TaskName) AS tasks BY host

 

Elastic

 

FROM logs-endpoint.events.file-*, logs-endpoint.events.process-*
| WHERE @timestamp >= TO_DATETIME("2026-11-11T07:00:00Z")
| EVAL blob = TO_LOWER(CONCAT(COALESCE(`file.path`, ""), " ", COALESCE(`process.command_line`, ""), " ", COALESCE(`process.executable`, "")))
| WHERE blob LIKE "*trapwatch*" OR blob LIKE "*relaytrap*" OR blob LIKE "*sckit-*"
| STATS events = COUNT(*), first_seen = MIN(@timestamp), last_seen = MAX(@timestamp), files = VALUES(`file.path`), commands = VALUES(`process.command_line`) BY `host.name`

 

CrowdStrike

 

#event_simpleName=/(ProcessRollup2|ScheduledTaskRegistered|FileWritten|PeFileWritten)/
| test(@timestamp >= 1794380400000)
| CommandLine=/(TrapWatch|RelayTrap|sckit-)/i or ImageFileName=/\\ProgramData\\RelayTrap\\|\\Temp\\sckit-/i or TargetFileName=/\\ProgramData\\RelayTrap\\|\\Temp\\sckit-/i or FileName=/^(TrapWatch|sckit-)/i or TaskName=/TrapWatch/i
| default(field=[FileName, TaskName], value="-")
| groupBy([aid, #event_simpleName, FileName, TaskName], function=[count(as=events), min(@timestamp, as=first_seen), max(@timestamp, as=last_seen), collect([ImageFileName, TargetFileName, CommandLine])])

 

Microsoft KQL

 

union DeviceFileEvents, DeviceProcessEvents, DeviceEvents
| where Timestamp >= datetime(2026-11-11T07:00:00Z)
| where FolderPath contains @"\ProgramData\RelayTrap" or FileName startswith "sckit-" or ProcessCommandLine contains "TrapWatch" or ProcessCommandLine contains @"\RelayTrap" or ProcessCommandLine contains "sckit-" or AdditionalFields contains "TrapWatch" or (ActionType has "ScheduledTask" and AdditionalFields contains "TrapWatch")
| summarize Events = count(), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Folders = make_set(FolderPath, 20), Commands = make_set(ProcessCommandLine, 20), Actions = make_set(ActionType, 10) by DeviceName

 

Hunt 4: connections to the published attacker servers

 

This looks for traffic to the two IP addresses and four domains in the indicator table, including the installer server and toolkit host Sophos observed. A hit warrants investigation, and a hit next to Hunt 1, 2 or 3 is incident response.

 

Field mapping: Splunk uses Sysmon EventCode 3 and 22, Elastic uses destination.ip and dns.question.name, Falcon uses NetworkConnectIP4 and DnsRequest, and KQL uses DeviceNetworkEvents RemoteIP and RemoteUrl.

 

Splunk

 

index=YOUR_SYSMON_INDEX sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" (EventCode=3 OR EventCode=22) earliest=1794380400 latest=now
| eval dip=coalesce(dest_ip, DestinationIp, dest), q=lower(coalesce(QueryName, query, DestinationHostname, ""))
| where dip IN ("185.99.255.28", "155.94.150.39") OR match(q, "newnewnew\\.gotdns\\.org|soxnewwork\\.homedns\\.org|sc\\.payproofkit\\.com|ns\\.mo7sen\\.dev")
| stats count AS events min(_time) AS first_seen max(_time) AS last_seen values(dip) AS ips values(q) AS queries BY host

 

Elastic

 

FROM logs-endpoint.events.network-*, logs-endpoint.events.dns-*
| WHERE @timestamp >= TO_DATETIME("2026-11-11T07:00:00Z")
| EVAL dip = `destination.ip`, q = TO_LOWER(COALESCE(`dns.question.name`, `destination.domain`, ""))
| WHERE dip IN (TO_IP("185.99.255.28"), TO_IP("155.94.150.39")) OR q IN ("newnewnew.gotdns.org", "soxnewwork.homedns.org", "sc.payproofkit.com", "ns.mo7sen.dev")
| STATS events = COUNT(*), first_seen = MIN(@timestamp), last_seen = MAX(@timestamp) BY `host.name`, dip, q

 

CrowdStrike

 

#event_simpleName=/(NetworkConnectIP4|DnsRequest)/
| test(@timestamp >= 1794380400000)
| RemoteAddressIP4="185.99.255.28" or RemoteAddressIP4="155.94.150.39" or DomainName=/^(newnewnew\.gotdns\.org|soxnewwork\.homedns\.org|sc\.payproofkit\.com|ns\.mo7sen\.dev)$/i
| default(field=[RemoteAddressIP4, DomainName, RemotePort], value="-")
| groupBy([aid, #event_simpleName, RemoteAddressIP4, DomainName, RemotePort], function=[count(as=events), min(@timestamp, as=first_seen), max(@timestamp, as=last_seen)])

 

Microsoft KQL

 

DeviceNetworkEvents
| where Timestamp >= datetime(2026-11-11T07:00:00Z)
| where RemoteIP in ("185.99.255.28", "155.94.150.39")
    or RemoteUrl contains "newnewnew.gotdns.org"
    or RemoteUrl contains "soxnewwork.homedns.org"
    or RemoteUrl contains "sc.payproofkit.com"
    or RemoteUrl contains "ns.mo7sen.dev"
| summarize Events = count(), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), IPs = make_set(RemoteIP, 10), Urls = make_set(RemoteUrl, 20) by DeviceName

 

Indicators published by Huntress and Sophos

 

From Huntress and Sophos only, not a complete list:

 

Indicator

Context

185.99.255.28

Installer server. Sophos saw the MSI pulled from here.

155.94.150.39

Published by Huntress.

newnewnew.gotdns.org

Published by Huntress.

soxnewwork.homedns.org

Published by Huntress.

sc.payproofkit.com

Published by Huntress.

ns.mo7sen.dev

Sophos saw PowerShell retrieve a script from this host.

2588ef5c15954b10

Attacker client ID, published by Huntress and Sophos.

ed630cc2c873b091

Attacker client ID, published by Huntress and Sophos.

d905b2b1b165d0a9

Origin instance ID published by Sophos. Not a complete list.

4b8f4089ddaac6a9

Origin instance ID published by Sophos. Not a complete list.

df1f7ef0cfacd0d8

Origin instance ID published by Sophos. Not a complete list.

TrapWatch

Scheduled task that runs as SYSTEM every 10 seconds.

C:\ProgramData\RelayTrap\

Path published by Huntress.

C:\Windows\Temp\sckit-*

Path published by Huntress.

 

What the results mean and when to escalate

 

A second client with IDs 2588ef5c15954b10 or ed630cc2c873b091, a TrapWatch task, a RelayTrap folder, or sckit files is incident response: isolate the host and any technician workstation that connected to it, preserve evidence, and do not connect with ScreenConnect. Traffic to the published servers warrants investigation on its own and is incident response when it appears with any of those findings. A Hunt 1 hit warrants investigation; escalate when the command was unauthorized or other evidence points to compromise. The absence of Sophos's three origin instance IDs is not a clean result, and a disabled extension with no endpoint hunt is unfinished work.

 

Protect the instance and every client behind it

 

If your clients run on Microsoft 365, start with a free Inception Foresight M365 Assessment.


bg-map-white.png

INCEPTION SECURITY™

A cybersecurity company with in depth knowledge of the threat landscape and security controls.

NAVIGATION

GET IN TOUCH

© 2025 All Rights Reserved by INCEPTION SECURITY™ .

bottom of page