Some ransomware attacks try to comply with Microsoft code-signing requirements. This gives malicious actors the flexibility to compile kernel modules designed for very specific tasks (usually involving defense impairing and evasion) before dropping the actual payload. Ransomware operators can do one of the following approaches:
1. Use a code-signing certificate that was either leaked, stolen from a compromised environment, or purchased from an underground market.
2. Obtain a new valid code-signing certificate by impersonating a legitimate entity and following Microsoft’s process for getting the cross-signing certificate (back when Microsoft still allowed cross signing for kernel-mode code), abusing Microsoft’s portal for issuing signed kernel modules, and purchasing valid code-signing certificates and/or Extended Validation (EV) certificates that are tied to real identities from underground markets.
In this section, we will examine a signed driver (ktgn.sys) used in the February BlackCat attacks. Figure 4 shows other examples of these new signed drivers and how they are being used as part of the BlackCat affiliate’s defense evasion routine.
The User Agent tjr.exe, which is protected via a virtual machine, drops the kernel driver to the user temporary directory C:\%User%\AppData\Local\Temp\Ktgn.sys. It then installs the dropped driver with the name ktgn and the start value = System (to start when the system restarts). From our analysis of what occurs when a user interfaces with this driver, we observed that it only uses one of the exposed Device Input and Output Control (IOCTL) code — Kill Process, which is used to kill security agent processes installed on the system.
Meanwhile the driver ktgn.sys, which is signed using a currently revoked valid digital signature from “BopSoft” (which had also been previously used by other threat actors for code signing) can successfully be loaded on a 64-bit Windows installation where signing policies are enforced. The driver is obfuscated using Safengine Protector v188.8.131.52 tool, which renders static analysis techniques unreliable. By loading the obfuscated driver and trying to build a user mode client to observe the exposed IOCTL interface, we can determine the function of each IOCTL code. Finally, we observed the same kernel driver being signed by different code-signing certificates.
Table 1. The driver variants with different signers
Since it does not register an unload callback function, the driver can only be unloaded if the service registry key is deleted or modified followed by a system restart.
A symbolic link with the name \\.\keHeperDriverLink is created that allows the user mode client to connect and communicate with it. Note that this link only allows for one connection — if more than one client tries to connect to it simultaneously, the system will crash.
This client supports ten different commands, with each command implementing a specific function that is executed from the kernel driver with the appropriate IOCTL interface exposed. Communication between the driver and the user mode client occurs using the IRP_MJ_DEVICIDE_CONROL handler via the following codes:
Table 2. Each IOCTL code and their function
Based on our analysis of the kernel driver, it seems to still be under development and testing since it is not structured well and some of its functions currently cannot be used. The following subsections provide details on the various IOCTL Interfaces.
IOCTL 222088h must be called first to activate the driver before any other operation can be performed. If this code is not called, the driver will not accept any operation and will return the message STATUS_ACCESS_DENIED. The user mode client sends this activation byte array to the driver.
The activation is a simple byte comparison against a hard coded byte array with the size 0x42 located in the driver. If the comparison passes, it will set a BOOLEAN flag, which will be checked before any operation.
IOCTL 22208Ch is called after the user mode client finishes its operation to unset the flag that was previously set in IOCTL Code 222088h. This will deactivate the driver and stop it from processing any new operation.
The client will need to pass the same byte array passed in IOCTL code 222088h for the operation to be successfully completed.
IOCTL 222094h is used to kill any user mode process (even protected ones). Tt receives the Process ID from the user agent then creates a kernel thread in the target process context. The created kernel thread calls the ZwTerminateProcess API to terminate the target process.
IOCTL 222184h is used to delete specific file paths (as shown in Figure 11).
IOCTL 222188h is used to force delete files. To do this, the kernel driver does the following:
- It tries to open all processes on the system using brute-force methods (starting from PID=0x4 to PID= 0x27FFD)
- When it successfully opens a process, it tries to reference all handles inside the process, again using a brute-force method (starting from HANDLE=0x4 to HANDLE = 0x27FFD)
- When it successfully references a handle, it uses the ObQueryNameString API to map the handle to a name. When a match is found, the kernel driver closes the handle.
This operation will ensure that all references to the file will be closed and the operation can be successfully completed without any errors stating that the file is being used by other applications.
IOCTL 22218Ch is used to copy files.
IOCTL 222190h is used to force copy files. The driver uses the same operation as the one used for force deletion (IOCTL Code: 222188h). It closes all references to the files from all processes using brute-force methods, then copies the file.
Both IOCTL 2221C4h and 2221C8h are used to register and unregister Process/Thread Notification callbacks. However, both paths are unreachable at the time of writing, which indicates that they are still under development or testing.
IOCTL 222264h Is used to reboot the system by calling the HalReturnToFirmware API.
Malicious actors that are actively seeking high-privilege access to the Windows operating system use techniques that attempt to combat the increased protection on users and processes via endpoint protection platform (EPP) and endpoint detection and response (EDR) technologies. Because of these added layers of protection, attackers tend to opt for the path of least resistance to get their malicious code running via the kernel layer (or even lower levels). This is why we believe that such threats will not disappear from threat actors’ toolkits anytime soon.
Malicious actors will continue to use rootkits to hide malicious code from security tools, impair defenses, and fly under the radar for long periods. These rootkits will see heavy use from sophisticated groups that have both the skills to reverse engineer low-level system components and the required resources to develop such tools. These malicious actors also tend to possess enough financial resources to either purchase rootkits from underground sources or to buy code-signing certificates to build a rootkit. This means that the main danger involving these kinds of rootkits lie in their ability to hide complex targeted attacks that will be used early in the kill chain, allowing an attacker to impair defenses before the actual payloads are launched in victim environments.
Code signing certificates can often be abused by threat actors since they provide an additional layer of obfuscation in their attacks. For organizations, compromised keys present not only a security risk, but can also lead to a loss of reputation and trust in the original signed software. Businesses should aim to protect their certificates by implementing best practices such as reducing access to private keys, which reduces the risk of unauthorized access to the certificate. Employing strong passwords and other authentication methods for private keys can also help protect them from being stolen or compromised by malicious actors. Furthermore, using separate test signing certificates (for prerelease code used in test environments) minimizes the chances that the actual release signing certificates are abused in an attack.
For general ransomware attack protection, organizations can implement a systematic security framework that allocates resources towards establishing a robust defense strategy. Here are some recommended guidelines:
- Take inventory of assets and data
- Identify authorized and unauthorized devices and software
- Audit event and incident logs
- Manage hardware and software configurations
- Grant admin privileges and access only when necessary
- Monitor network ports, protocols, and services
- Establish a software allowlist for legitimate applications
- Implement data protection, backup, and recovery measures
- Enable multifactor authentication (MFA)
- Deploy the latest versions of security solutions across all layers of the system
- Watch for early signs of an attack
By adopting a multifaceted approach to securing potential entry points, such as endpoints, emails, webs, and networks, organizations can detect and protect against malicious elements and suspicious activities, thereby safeguarding themselves from ransomware attacks.
A multilayered approach can help organizations guard possible entry points into their system (endpoint, email, web, and network). Security solutions can detect malicious components and suspicious behavior, which can help protect enterprises.
- Trend Vision One™ provides multilayered protection and behavior detection, which helps block questionable behavior and tools before the ransomware can do any damage.
- Trend Micro Apex One™ offers next-level automated threat detection and response against advanced concerns such as fileless threats and ransomware, ensuring the protection of endpoints.
The indicators of compromise for this entry can be found here.