In the previous installment of our Cuckoo Spear series, we introduced the Cuckoo Spear campaign and provided an overview of the APT10 threat actor’s tactics and objectives. If you missed Part 1, you can catch up here.
In this follow-up, we dive deeper into the technical aspects of the NOOPDOOR and NOOPLDR malwares that APT10 employed in the Cuckoo Spear campaign. Our analysis reveals how NOOPDOOR operates and the potential risks it poses to organizations. This breakdown will help cybersecurity professionals better understand and defend against the sophisticated strategies of this persistent adversary.
This section will mainly focus on the reverse engineering of the Cuckoo Spear tools : NOOPLDR and NOOPDOOR.
Cybereason has discovered different variants of NOOPLDR-DLL differ in how they load the malicious code, illustrated below.
The capabilities of NOOPLDR-DLL are the following:
Cybereason observed telemetry of several unsigned DLL files under C:\Windows\System32 that were loaded as part of the services started by the command svchost.exe -k netsvcs. This eventually injected a multitude of NOOPDOOR payloads into arbitrary processes. Further investigation revealed that the malicious DLL files are created by modifying a segment of legitimate DLLs. The modified section is given a randomly generated function name in the export table as shown here.
Export Table With Randomly Generated Function Names
This malicious export function is called as the service’s ServiceMain function, which is the entry point for a service that is implemented in a service DLL running within a SVCHOST instance.
ServiceMain Function
The entire DLL file, including the legitimate functions, has been heavily obfuscated with Control Flow Flattening to potentially slow down analysis efforts.
Control Flow Flattening Observed In NOOPLDR
Strings used to register the service and query the registry are XOR encoded, and are decoded with bytes that are hardcoded within the .rdata section of the binary.
XOR In NOOPLDR
Crafting a script that will decode all the strings reveals information related to the service settings, registry key path, and a command that starts a windows service and sets its security descriptor.
Software\Microsoft\SQMClient
MachineId
SOFTWARE\Microsoft\UserData
cmd /c "sc start %s && sc sdset %s D:(D;;DCLCWPDTSD;;;IU)(D;;DCLCWPDTSD;;;SU)(D;;DCLCWPDTSD;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost
netsvcs
%SystemRoot%\System32\svchost.exe -k netsvcs
SYSTEM\CurrentControlSet\Services\
Description
\Parameters
ServiceDll
ServiceMain
DaTRhAZpRFqHdgnuLZCUdP
*.exe
calc.exe
win32calc.exe
_config
-install
NOOPLDR performs WinAPI calls to obtain encrypted shellcode from several different registry keys. A list of registry key paths observed by Cybereason are the following:
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\User Preferences
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\OneSettings
HKEY_CURRENT_USER\HKCU\Software\Microsoft\OneDrive
HKEY_CURRENT_USER\Software\Microsoft\UserData
HKEY_CURRENT_USER\Software\Microsoft\F12
HKEY_CURRENT_USER\Software\Licenses
HKEY_CURRENT_USER\Software\License
HKEY_CURRENT_USER\COM3
The decryption method utilizes AES-CBC mode with an initialization vector (IV) that contains the first 16 bytes of the MachineId. It uses standard WinAPIs from advapi32.dll to derive an AES key based on a SHA1 hash. The SHA1 hash is created from the following combined data:
The MachineId value from HKEY_LOCAL_MACHINE\\Software\\Microsoft\\SQMClient
A NULL byte
Hardcoded bytes within the .text and .rdata sections
Hardcoded Bytes In NOOPLDR
As an example, the data that will be hashed would look like below:
Data Prior To Getting Hashed
Once hashed, the hash object is then passed to the CryptDeriveKey function to craft the key used for decryption.
Executables under C:\Windows\System32 are started as dummy processes to inject the decrypted NOOPDOOR code with a common WinAPI pattern of
However, Native APIs are implemented instead along with custom syscalls where the SSN (System Service Number) is loaded dynamically right before each call. Although most SSNs are consistent across many Windows versions, some are not.
The malicious code resolves the correct value for each syscall. For example, NtCreateThreadEx would be 0xBA on Windows 10 version 1709.
Custom Syscall In NOOPLDR
Since the Native APIs are being called directly from the process’s memory space, any user mode hooks on NTDLL or kernel32 will be ineffective in detecting this injection.
An interesting difference between the two DLLs is the version that used DLL Side-Loading performed local code injection as opposed to the DLL that used CreateProcess > NtWriteVirtualMemory.
It instead dynamically allocates the decrypted shellcode within its process memory, uses NtProtectVirtualMemory syscall to change protections, then executes the newly allocated NOOPDOOR code.
Decrypted NOOPDOOR In Debugger
This C# code is stored in an XML file generally stored in the C:\Windows\System32 folder. In some specific cases, that XML file was stored in other folders.
The code is highly obfuscated, but Cybereason de-obfuscated it in order to identify how it worked.
Mainly, the code is loaded using the Microsoft Windows tool msbuild.exe, which compiles and runs code in one command. The command line msbuild.exe [NOOPLDR XML FILE NAME].xml is generally built-in to the victim system through persistence mechanisms such as scheduled tasks, services or WMI consumer events, as documented in the TTPs section.
NOOPLDR-C# Execution Flow
The capabilities of NOOPLDR-C# are the following:
Each NOOPLDR-C# sample Cybereason analyzed was different depending on the machine. Some loaders included different organization of the functions, and loaded the shellcode from a different registry hive (some loaded from HKCU, and other loaded from HKLM).
Each item is using the Project File format by Microsoft, in order to be interpreted by the LOLBin msbuild.exe. That binary takes a .csproj file (here renamed XML) which is then compiled and ran:
Source : https://lolbas-project.github.io/lolbas/Binaries/Msbuild/
That XML file, our starting point, begins with the following code:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="MFYMF[...]BppIs">
<jIN[...]LLE />
</Target>
<UsingTask TaskName="jINp0k6v[...]LLLE" TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.dx3gN2[...]QQApv4.0.dll">
<Task>
[C# CODE]
</Task>
</UsingTask>
</Project>
The C# Code mentioned above is extremely obfuscated to complicate analysis.
To start with this analysis, one needs to deobfuscate the loader. The original file looks like this at first:
Obfuscated Code
Using a simple IDE, one can already use C# code heuristic to indent code properly:
Extract From The Code Once Indented By VSCode
The next phase is the renaming of each variable and parameter. Since WinAPI functions are being called from the C# code, it’s possible to map each randomly named variable with a properly named one:
static extern IntPtr OpenProcess(UInt32 INVCZKPHO5c4XALHLbgGfKXOlHbWSLY8uWyUlcEMwjMstIN2gHMEGy08Zgq, Int32 J2ZcgdKac1vIGDj58F, UInt32 N4iPk9uC65A5fVmdDuJlp8);
[DllImport("...")]
static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);
The following phase is a bit manual as the goal is to infer the original variable name by trying to understand how the code works.
In the end, this allows for an easier code to understand:
Extract From The Un-Obfuscated Code
The first function that is called is Execute():
First Function Called And Beginning Of The C# Code Flow
The ClInI function’s goal is to obtain the shellcode from that DAT file passed as a parameter or from the Windows registry if the shellcode is already stored there.
First Part Of The Function
That function will first create a hash based on the machine name and a salt. Cybereason noticed this hash is not always present in different iterations of NOOPLDR-C#.
Also, the value MachineId is obtained from the key HKLM\Software\Microsoft\SQMClient.
Both the machine name and SALT are concatenated and a SHA256 is calculated from them.
This allows to calculate the name of the key (just the first 16 bytes will be used) that the program will try to obtain from the registry: HKLM\Software\License\{SHA256(MachineName+SALT)}
Then, the program attempts to load the .dat file passed as a function parameter:
Cybereason estimated that this measure was meant to initially inject the shellcode into the registry. This was confirmed after further reading of the code:
This enables the Threat Actor to function without the shellcode stored on the disk, apart from the registry which is a more complicated place to look for. Storing shellcode in the Windows registry provides attackers with a stealthy, persistent, and potentially privileged way to execute malicious code on a system, making it an attractive option.
At this point, the encrypted shellcode content is obtained in memory and ready to be used.
The code will first compare the SHA256 hash of the shellcode (minus the first 32 bytes) with a value stored at the very beginning of the file/registry key:
Integrity Check
The code will continue only if, at some point in the file/registry, the SHA256 checksum, which is commonly called an integrity check, matches the computed SHA256 hash of the shellcode itself. The code will then calculate the key to decrypt the shellcode.
The key is a SHA384 hash of the MachineID value from the registry and the machine name salt calculated in the previous step. It then decrypts the shellcode content with a classic AES routine, using the first 32 bytes of the SHA384 as key and the last 16 bytes as an initialization vector (IV).
Decryption Routine
Finally, it will take the first 10 bytes of the decrypted content and store it in 3 variables :
Store In Variables
The logic will execute if the shellcode was loaded through a different offset than 0:
Once that is finished, the code proceeds to inject that decoded shellcode into another process’ memory.
The next part of the code injects the decrypted shellcode into the memory of a newly spawned process. The code corresponding to this part is the following:
Remote Injection Function
The injection process following:
In order to extract the configuration for each iteration of NOOPLDR, one has to obtain the following elements
Cybereason wrote a short python script to decrypt the shellcode using parameters such as the registry key name ({XXXX-XXXX-XXXX}) and the MachineId value.
This next section will describe the analysis of the shellcode injected from the above methods. Cybereason attributes this malware to NOOPDOOR as it has been so recently unearthed in JSAC 2024. Cybereason has discovered the existence of several variants of NOOPDOOR that differ in C2 urls and functionality, but they can mostly be categorized as one of the following.
Cybereason also observed single shellcode binaries that contain both of these two capabilities. But in most cases, the client and server shellcode were separated.
C2 Client NOOPDOOR Analysis
The capabilities of the Client code of NOOPDOOR are the following:
Each code shared a similar Windows API hashing function that performs a rotate right instruction against the function names and an XOR instruction against hardcoded bytes.
Dynamic WinAPI Resolution Logic
The hardcoded bytes differ from each sample, but it will nevertheless create a large structure of around 250 API functions.
Loaded WinAPI Struct Example
This struct is then used to call the appropriate APIs within the code. The Cybereason IR team has created a script to resolve this API hashing function to speed up analysis.
As a means of Anti-Detection, the functions related to resolving the APIs will be overwritten with garbage bytes such as 0x00, 0x20, 0x90. Any signatures scanned in memory that explicitly looks for this part of the code will not be able to detect it.
Dumped From memory Before Execution
Code Difference In Memory
A widespread number of process names used in malware analysis are stored as stack strings. These processes are obtained via the CreateToolhelp32Snapshot API and are verified before the code’s main routine will run.
・x32dbg
・x64dbg
・ollydbg
・windbg
・ida
・idaq
・ImmunityDebugger
・loaddll
・ProcessHacker
・StudioPE
・PE Explorer
・Autoruns
・Process Explorer
・Procmon
・TcpView
・010Editor
・WinHex
・Wireshark
・zenmap
・ProcessHacker
・vmmap
・load_sc
・HttpAnalyzerStd
・Fiddler
C2 Domain names are generated based on a URL string where the integer after the “#” acts as the number of days before the domain changes to its next iteration. Cybereason has observed code that generates the domains for multiple days such as 60, 90, 180, 364, 365 days. Note that the C2 URL string contains “http”, but this is just used to create hashes for the DGA, and actual communication is done over a custom TCP protocol.
C2 URLs Before DGA Resolution
The algorithm to generate the domain is as follows.
Perform a check to see if the current date/time is between
Monday 10 am to 11 am (LocalTime)
Obtain SystemTime structure, converting it to a FileTime, then to EpochTime based on the year/month/day
If the URL has an integer after “#”, use it to perform arithmetic against the time
If the URL has “[]”, insert the hostname
Create a SHA256 from the modified FileTime
Create a SHA512 from the un-resolved C2 URL string in (4)
Create a SHA512 from created SHA256 and un-resolved C2 URL string in (4)
Obtain Base64 from concatenated SHA512 hash from (6) and (7), then obtain the first 17 bytes
Remove special chars, lowercase chars, and numbers from base64 string
Replace the “$a” part of un-resolved C2 URL with cleaned base64 string
The C2 URL could also be a subdomain as illustrated below. In this case, Cybereason observed a slightly different algorithm. In this case, the check for Monday is not present and if it does not have the number of days after “#”, the domain will change everyday based on the system time.
C2 urls before resolution
The Cybereason team has created a script to resolve the DGA URLs and generated a list of domains from 2023 to 2025. From an IR perspective, subdomains like ocouomors[.]com are easier to block than www.*.com. To prevent the list from being too long, Cybereason only included the latter. The script can be used to block any other possible NOOPDOOR domains that could be generated within your organization..
It has the functionality to exfiltrate data to the generated domain as well as additional C2 capabilities. ESET Security’s presentation at JSAC 2024 documents this functionality well.
Source: https://jsac.jpcert.or.jp/archive/2024/pdf/JSAC2024_2_8_Breitenbacher_en.pdf
A variant of the loaded payload contained code for a possible internal C2 server. Cybereason suspects this server was used by the Threat Actor as a means of aggregating information and pivoting within the network.
The capabilities of the C2 Server of NOOPDOOR are the following:
It adds a new firewall rule under the rule name “Cortana” by utilizing the firewall COM object, or the netsh command.
Firewall Name
The Windows Firewall API is loaded by CoCreateInstance where the COM Firewall CLSID {304CE942-6E39-40D8-943A-B913C40C9CD4} is used as the Interface ID, and the INetFwMgr Interface CLSID {F7898AF5-CAC4-4632-A2EC-DA06E5111AF2} as the rclsid parameter.
Loading Firewall API
If the COM object method of loading the Firewall API fails, it executes the below netsh command instead.
cmd /c netsh firewall delete port opening TCP 5984 & netsh firewall add port opening TCP 5984 TCP
Uses Windows Socket APIs to listen on port for incoming connections.
Listening Port
Cybereason have observed samples that listen on different ports:
Based on the received commands, it will perform one of the following functions.
Server Functionality
Due to the widespread identification of Cuckoo Spear in Japan organizations, Cybereason decided to publish this Threat Analysis Report to better identify their activity and allow threat hunters to potentially identify them in their networks.
Cybereason provided descriptions of queries to identify Cuckoo Spear presence in the network and has shared Indicators of Compromise (IOCs) to better detect them and potentially block Cuckoo Spear activity.
Due to the potential complexity of the containment, eradication and recovery process, it is highly recommended to hire a dedicated Incident Response team upon discovery of this Threat Actor being on the network.
In many APT related cases, the Threat Actor has already gained network access for several months or years before any investigation has started. Eradication of this Threat Actor requires in-depth preparation and effective security measures so the attacker cannot return. Although remediation actions will differ for each organization, Cybereason Security Services suggest, in general, to conduct a organization scale remediation day where the following actions are implemented:
Prepare a clean uncompromised network
Disabled all internet access to and from the internet
Block all NOOPDOOR related C2 domains and IPs
Reset all user passwords
Rebuild infected machines
Connect rebuilt machines to the clean network
To detect if a NOOPLDR/NOOPDOOR has been exploited in your environment, run the following hunting query in your EDR or monitoring platform.
IOC* |
Explanation |
3utilities[.]com |
NOOPDOOR subdomain |
foeake[.]org |
NOOPDOOR subdomain |
ftp[.]sh |
NOOPDOOR subdomain |
inbullar[.]com |
NOOPDOOR subdomain |
mangoaiml[.]com |
NOOPDOOR subdomain |
ocouomors[.]com |
NOOPDOOR subdomain |
onthewifi[.]com |
NOOPDOOR subdomain |
paunsonaz[.]com |
NOOPDOOR subdomain |
redirectme[.]net |
NOOPDOOR subdomain |
saraosting[.]com |
NOOPDOOR subdomain |
serveblog[.]net |
NOOPDOOR subdomain |
temmans[.]com |
NOOPDOOR subdomain |
torefrog[.]com |
NOOPDOOR subdomain |
ea474e87f23ce6575057e76108665ffb |
NOOPLDR-DLL |
e0a8048c7f69da35bbb2cd35d86c2dc8 |
NOOPLDR-DLL |
6b3148e824fd84f54592fe5d2e766740 |
NOOPLDR-DLL |
c76b1ed6d094edbad887f68093ef6bf9 |
NOOPLDR-DLL |
d6d59b1ff85bf971286782f8f43d6326 |
NOOPLDR-DLL |
deedb32bf51dc8f3399614c8a9718e75 |
NOOPLDR-DLL |
c39b02c9771c6be9610977408ebb509f |
NOOPLDR-DLL |
9eef43edc87ab1f301ec8730113535ee |
NOOPLDR-DLL |
73a904ba602e1bf068f5d217403fa41f |
NOOPLDR-DLL |
fe36fd0f09aadd3e7ddd7b66f18d5e93 |
NOOPLDR-C# |
f12873d8b69624d972b3c6fa55e52483 |
NOOPLDR-C# |
b5228638d5de18e59ebbddc13c120879 |
NOOPLDR-C# |
4f1c68d2fe3b0255e706e4c7de0a739f |
NOOPLDR-C# |
3b07fbaa8b9c5a53658abe3ac9f66e60 |
NOOPLDR-C# |
0dbaff93ec6243035275364d5c1c26c9 |
NOOPLDR-C# |
KEY_CURRENT_USER\Software\Microsoft\Internet Explorer\User PreferencesH |
NOOPDOOR registry key path |
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\OneSettings |
NOOPDOOR registry key path |
HKEY_CURRENT_USER\HKCU\Software\Microsoft\OneDrive |
NOOPDOOR registry key path |
HKEY_CURRENT_USER\Software\Microsoft\UserData |
NOOPDOOR registry key path |
HKEY_CURRENT_USER\Software\Microsoft\F12 |
NOOPDOOR registry key path |
HKEY_CURRENT_USER\Software\Licenses |
NOOPDOOR registry key path |
HKEY_CURRENT_USER\Software\License |
NOOPDOOR registry key path |
HKEY_CURRENT_USER\COM3 |
NOOPDOOR registry key path |
HKEY_LOCAL_MACHINE\Software\License |
NOOPDOOR registry key path |
* NOOPDOOR shellcode hashes have been omitted from this list,
as the hashes differ for every NOOPDOOR sample Cybereason has observed.
Tactic |
Techniques / Sub-Techniques |
TA0001: Initial Access |
T1190: Exploit Public-Facing Application |
TA0001: Initial Access |
T1566: Phishing |
TA0002: Execution |
T1053.005: Scheduled Task |
TA0002: Execution |
T1569.002: Service Execution |
TA0002: Execution |
T1047; Windows Management Instrumentation |
TA0003: Persistence |
T1053.005: Scheduled Task |
TA0003: Persistence |
T1543.003: Windows Service |
TA0003: Persistence |
T1546.003.: Windows Management Instrumentation Event Subscription |
TA0003: Persistence |
T1574.002: DLL Side-Loading |
TA005: Defense Evasion |
T1070.001: Clear Windows Event Logs |
TA005: Defense Evasion |
T1055: Process Injection |
TA005: Defense Evasion |
T1070.004: File Deletion |
TA005: Defense Evasion |
T1070.006: Timestomp |
TA005: Defense Evasion |
T1112: Modify Registry |
TA005: Defense Evasion |
T1127.001: MsBuild |
TA005: Defense Evasion |
T1140: Deobfuscate/Decode Files or Information |
TA005: Defense Evasion |
T1562.004: Disable or Modify System Firewall |
TA005: Defense Evasion |
T1622: Debugger Evasion |
TA0011: Command and Control |
T1071: Application Layer Protocol |
TA0011: Command and Control |
T1568.002: Domain Generation Algorithms |
TA0011: Command and Control |
T1573: Encrypted Channel |
Jin Ito, Incident Response Engineer, Cybereason IR Team
Jin Ito is an Incident Response Engineer with the Cybereason Incident Response team. Formerly an Incident Response Engineer at Fujitsu, he holds several cybersecurity certificates such as GREM, GCFA, and OSCP. Aside from his digital forensic responsibilities, he loves creating and reverse engineering malware.
Loïc Castel, Incident Response Investigator, Cybereason IR Team
Loïc Castel is an Investigator with the Cybereason IR team. Loïc analyses and researches critical incidents and cybercriminals, in order to better detect compromises. In his career, Loïc worked as a security auditor in well-known organizations such as ANSSI (French National Agency for the Security of Information Systems) and as Lead Digital Forensics & Incident Response at Atos. Loïc loves digital forensics and incident response, but is also interested in offensive aspects such as vulnerability research.
Kotaro Ogino, CTI Analyst, Cybereason Security Operations Team
Kotaro is a CTI Analyst with the Cybereason Security Operations team. He is involved in threat hunting, threat intelligence enhancements and Extended Detection and Response (XDR). Kotaro has a bachelor of science degree in information and computer science.