Endpoint Security

ClickFix Now Starts Inside ChatGPT: A Fake Custom GPT, a Google Sites CAPTCHA, and a Canon-Signed Loader

Dark cyberpunk illustration of a cyan-lit archway standing in an empty black hall, opening onto a completely different amber-lit corridor, with a thin orange trail of light running from the doorway across a wet reflective floor toward the viewer.

Huntress traced at least forty intrusions this month back to one Google Sites page. Two of them started inside ChatGPT.

The lure was a custom GPT called "Plus 5.6", published to look like a newer model. Victims did not find it by browsing the GPT store. They found it through a sponsored Google ad that sat above the organic results, and the link it served pointed at chatgpt.com, because a custom GPT lives on OpenAI's own domain behind OpenAI's own certificate. Ask the GPT anything and it returns one scripted answer: availability on the primary domain is limited, continue on the backup link. The backup link is a Google Sites page dressed as a Cloudflare check, and the check asks the visitor to copy a command and paste it into Windows.

Huntress published the full chain on September 28, and BleepingComputer reported it the next day. There is no CVE here, no unpatched product, and no exploit. The user performs every privileged action, and each stage before the payload runs on a domain your proxy already allows.

Who this reaches, and who can stop reading

Stop reading if your users run macOS or ChromeOS. The lure renders anywhere, but the payload is a Windows MSI carrying a .NET 5 runtime, a signed Windows executable and four sideloaded DLLs. On a Mac the paste fails at the first line.

Stop reading, mostly, if PowerShell on your endpoints runs in Constrained Language Mode under an enforced App Control or WDAC policy. The first stage calls irm against a numeric host and writes a script to %TEMP%. Constrained Language Mode kills that line before it resolves anything.

Everyone else is in scope, and that covers most small-business fleets and most of what a managed service provider looks after. The requirements are a Windows endpoint, a user who can open the Run box or a terminal, outbound access to chatgpt.com and sites.google.com, and Microsoft Defender in its shipped configuration. If you allow the first two domains and have never turned on an attack surface reduction rule, the chain runs end to end as the logged-on user.

One correction worth making before somebody files a ticket with OpenAI. A custom GPT is a document, not code. Publishing one that repeats an attacker's instructions is the same act as publishing a web page that does, and MITRE has tracked the paste itself as T1204.004, Malicious Copy and Paste, since the ClickFix wave of last year. OpenAI removed the first GPT and plans to retire custom GPTs entirely on December 11. That closes one storefront. The technique moves to the next platform that hosts user-authored content on a domain nobody blocks.

What the forty incidents have in common, and what they do not

The timeline is short and it is the most useful part of the report. Huntress reported the first GPT to OpenAI on September 25 and it came down. On September 27 a second, linked GPT was live under the same "Plus 5.6" name. Later samples swapped the entire file-level identity of the payload: the legitimate Canon-signed COTFileReadApp.exe became Stardock's DeElevate64.exe, the patched loader DLL ceiinfolog.dll became DeElevator64.dll, the MSI renamed itself from ISOSimple.msi to IconEdit2Turb.msi, and the shellcode moved out of a WAV file and into a NuGet package's Build.dat. The persistence entry stopped calling itself "Canon Configuration Reader" and started calling itself "Stardock DeElevation Tool".

Two days. Every hash in the first writeup was stale within two days of publication, which is the honest reason I am not going to hand you an indicator list to paste into a blocklist. Take the hashes for retrospective sweeps and nothing else.

What survived the rewrite is the shape of the install:

  • PowerShell fetches from a numeric host. The observed command used the decimal form of an IP address, irm 1614733393/12, which resolves to 96.62.224[.]81. Decimal notation defeats every regex that looks for four dotted octets.
  • The installer is msiexec, launched from %TEMP%. The decoded script ran msiexec /qn /norestart against a file named with 32 hexadecimal characters plus a product name.
  • The MSI hides itself. It sets ARPSYSTEMCOMPONENT=1, so the package never appears in Apps and Features, and a custom action starts the payload immediately instead of waiting for a reboot.
  • The executable that runs is genuinely signed. Canon and Stardock both signed the host binaries. The malicious code sits in an unsigned DLL dropped beside it, patched to import the next stage.
  • Persistence is a matched pair. A HKCU Run value and a scheduled task carry the same name and point at the same binary. A watchdog rechecks the Run key every 150 seconds and the task every 875 seconds, and recreates whichever one you deleted.

The last point is the one that catches small teams. Removing a Run key and calling the host clean gives the operator a fresh Run key inside three minutes.

Between the signed executable and the payload sit two more layers built to keep anything readable off disk. The unsigned DLL pulls its shellcode out of what looks like an ordinary audio file, valid WAV header and all, with the encrypted data starting well past the header and unwrapped by a rolling single-byte XOR. That shellcode then mounts a custom encrypted container holding several hundred folders. A file scanner that walks the install directory sees a media asset and a data blob.

Scope the incident on what the final payload can do rather than on what it dropped. Huntress describes a remote access tool that enumerates installed antivirus through WMI, reads domain and domain-controller details, lists adapters and open ports, inventories installed software, fingerprints the hardware, opens remote desktop sessions, broadcasts the screen, captures camera, microphone and system audio, detects seventeen browsers, and runs further payloads supplied as EXE, DLL, MSI, PowerShell, batch, VBScript, JScript or ZIP. On a domain-joined workstation that is a credential-theft and lateral-movement platform. Reimaging the host closes the smaller half of the job. The half that matters is the account list: who signed in after the install date, and whether those passwords, tokens and session cookies have been rotated since.

Hunt the shape, not the file names

Three questions answer whether this chain ran on a host, and none of them need an indicator feed. If you have Defender for Endpoint, these run in advanced hunting now.

// 1. A browser or a terminal starts PowerShell that downloads and runs a script
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName =~ "powershell.exe"
| where ProcessCommandLine has_any ("irm ", "iwr ", "Invoke-RestMethod", "Invoke-WebRequest")
| where ProcessCommandLine has_any ("Out-File", "$env:temp", "ExecutionPolicy Bypass")
| where InitiatingProcessFileName in~ ("chrome.exe","msedge.exe","firefox.exe",
                                       "explorer.exe","cmd.exe","wscript.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, ProcessCommandLine

// 2. msiexec installing a randomly named package out of the temp directory
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName =~ "msiexec.exe"
| where ProcessCommandLine matches regex @"(?i)\\Temp\\[0-9a-f]{16,}.*\.msi"
   or (ProcessCommandLine has "/qn" and ProcessCommandLine has "\\Temp\\")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
          InitiatingProcessFileName

// 3. A signed vendor binary running from a per-user install path,
//    started by the installer rather than by a person
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FolderPath contains @"\AppData\Local\Programs\"
| where InitiatingProcessFileName =~ "msiexec.exe"
| summarize Runs = count(), FirstSeen = min(Timestamp)
        by DeviceName, FolderPath, FileName
| order by FirstSeen desc

If you have no hunting console

Most of the fleets this campaign landed on are managed with a remote monitoring tool and no query language. The same three questions answer from PowerShell, and the run against the Run keys is the fastest of the three. The registry also keeps the receipt for the paste itself: RunMRU stores what a user typed or pasted into the Run box, and a ClickFix victim's entry is usually still sitting there.

# What was pasted into the Run box, newest first
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU' |
  Select-Object -Property MRUList, a, b, c, d, e

# Run keys and scheduled tasks that point into a per-user install directory
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' |
  Select-Object * -Exclude PS* |
  Format-List

Get-ScheduledTask | ForEach-Object {
  $exec = $_.Actions | Where-Object { $_.Execute -like '*\AppData\Local\Programs\*' }
  if ($exec) { [pscustomobject]@{ Task = $_.TaskName; Runs = $exec.Execute } }
}

# Unsigned DLLs sitting next to a validly signed executable
Get-ChildItem "$env:LOCALAPPDATA\Programs" -Recurse -Include *.dll -ErrorAction SilentlyContinue |
  Get-AuthenticodeSignature |
  Where-Object Status -ne 'Valid' |
  Select-Object Status, Path

A host that returns a Run value and a scheduled task with matching names, both pointing under %LOCALAPPDATA%\Programs, is compromised. Isolate it before you delete either entry, because the watchdog will rewrite them and you will lose the timestamps.

Three settings that end the chain before the loader

Ordered by how much they cost you to turn on.

1. Remove the Run box from users who never use it

The Windows policy is NoRun, documented by Microsoft as Remove Run menu from Start Menu. It removes the Run command, removes New Task from Task Manager, and disables the Windows+R shortcut. A determined user can still launch a program another way. The lure cannot, because its instructions name that exact dialog and a victim following them reaches a keyboard shortcut that does nothing.

# Per-user policy: no Run dialog, no Windows+R
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer" ^
    /v NoRun /t REG_DWORD /d 1 /f
# Intune / MDM equivalent:
# ./User/Vendor/MSFT/Policy/Config/ADMX_StartMenu/NoRun

2. Two attack surface reduction rules, in audit first

Both of these sit in Microsoft's ASR reference and both need cloud-delivered protection enabled. Deploy them as Audit (2), read a week of telemetry, then move to Block (1). The prevalence rule is the noisy one in shops that build their own tooling, and it is also the one that would have stopped the first-run executable in this chain.

# Block execution of potentially obfuscated scripts
Add-MpPreference -AttackSurfaceReductionRules_Ids 5beb7efe-fd9a-4556-801d-275e5ffc04cc `
                 -AttackSurfaceReductionRules_Actions AuditMode

# Block executable files from running unless they meet a prevalence,
# age, or trusted list criterion
Add-MpPreference -AttackSurfaceReductionRules_Ids 01443614-cd74-433a-b99e-2ecdc07bfc25 `
                 -AttackSurfaceReductionRules_Actions AuditMode

Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionRules_Ids

3. Decide where DNS-over-HTTPS is allowed to go

The RAT resolves its command and control through public DoH endpoints at Cloudflare, Google and Quad9, then talks over ordinary HTTPS. If your endpoints are allowed to resolve names through anybody's DoH service, your DNS logs are not a source of truth about what those endpoints looked up. Point managed devices at your own resolver, block the published DoH endpoints outbound, and treat a workstation that reaches one directly as an alert rather than a preference.

Take the Run box away from the people who never use it

Pick one group this week. Finance, reception, sales, anyone whose job has never required typing a command. Push NoRun to them, put the two ASR rules in audit mode across the fleet, and run the RunMRU sweep once so you know whether anybody has already pasted something. Those three actions take an afternoon and they remove the step this entire campaign depends on, along with every future campaign that ends with "paste this to continue".

Then write down what you would do on the host that comes back dirty. The pair of persistence entries, the watchdog interval and the signed host binary are all decisions you do not want to make live. If your incident response plan for a user-executed loader is currently a conversation rather than a document, that is the gap worth closing before the next storefront opens.

Need an incident response plan before the next attack?

We help organizations build and test incident response playbooks, including the user-executed loader case this campaign keeps producing. Book a session with our team.