Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

Yet another DCOM object for lateral movement

19 December 2025 at 03:00

Introduction

If you’re a penetration tester, you know that lateral movement is becoming increasingly difficult, especially in well-defended environments. One common technique for remote command execution has been the use of DCOM objects.

Over the years, many different DCOM objects have been discovered. Some rely on native Windows components, others depend on third-party software such as Microsoft Office, and some are undocumented objects found through reverse engineering. While certain objects still work, others no longer function in newer versions of Windows.

This research presents a previously undescribed DCOM object that can be used for both command execution and potential persistence. This new technique abuses older initial access and persistence methods through Control Panel items.

First, we will discuss COM technology. After that, we will review the current state of the Impacket dcomexec script, focusing on objects that still function, and discuss potential fixes and improvements, then move on to techniques for enumerating objects on the system. Next, we will examine Control Panel items, how adversaries have used them for initial access and persistence, and how these items can be leveraged through a DCOM object to achieve command execution.

Finally, we will cover detection strategies to identify and respond to this type of activity.

COM/DCOM technology

What is COM?

COM stands for Component Object Model, a Microsoft technology that defines a binary standard for interoperability. It enables the creation of reusable software components that can interact at runtime without the need to compile COM libraries directly into an application.

These software components operate in a client–server model. A COM object exposes its functionality through one or more interfaces. An interface is essentially a collection of related member functions (methods).

COM also enables communication between processes running on the same machine by using local RPC (Remote Procedure Call) to handle cross-process communication.

Terms

To ensure a better understanding of its structure and functionality, let’s revise COM-related terminology.

  1. COM interface
    A COM interface defines the functionality that a COM object exposes. Each COM interface is identified by a unique GUID known as the IID (Interface ID). All COM interfaces can be found in the Windows Registry under HKEY_CLASSES_ROOT\Interface, where they are organized by GUID.
  2. COM class (COM CoClass)
    A COM class is the actual implementation of one or more COM interfaces. Like COM interfaces, classes are identified by unique GUIDs, but in this case the GUID is called the CLSID (Class ID). This GUID is used to locate the COM server and activate the corresponding COM class.

    All COM classes must be registered in the registry under HKEY_CLASSES_ROOT\CLSID, where each class’s GUID is stored. Under each GUID, you may find multiple subkeys that serve different purposes, such as:

    • InprocServer32/LocalServer32: Specifies the system path of the COM server where the class is defined. InprocServer32 is used for in-process servers (DLLs), while LocalServer32 is used for out-of-process servers (EXEs). We’ll describe this in more detail later.
    • ProgID: A human-readable name assigned to the COM class.
    • TypeLib: A binary description of the COM class (essentially documentation for the class).
    • AppID: Used to describe security configuration for the class.
  3. COM server
    A COM is the module where a COM class is defined. The server can be implemented as an EXE, in which case it is called an out-of-process server, or as a DLL, in which case it is called an in-process server. Each COM server has a unique file path or location in the system. Information about COM servers is stored in the Windows Registry. The COM runtime uses the registry to locate the server and perform further actions. Registry entries for COM servers are located under the HKEY_CLASSES_ROOT root key for both 32- and 64-bit servers.
Component Object Model implementation

Component Object Model implementation

Client–server model

  1. In-process server
    In the case of an in-process server, the server is implemented as a DLL. The client loads this DLL into its own address space and directly executes functions exposed by the COM object. This approach is efficient since both client and server run within the same process.
    In-process COM server

    In-process COM server

  2. Out-of-process server
    Here, the server is implemented and compiled as an executable (EXE). Since the client cannot load an EXE into its address space, the server runs in its own process, separate from the client. Communication between the two processes is handled via ALPC (Advanced Local Procedure Call) ports, which serve as the RPC transport layer for COM.
Out-of-process COM server

Out-of-process COM server

What is DCOM?

DCOM is an extension of COM where the D stands for Distributed. It enables the client and server to reside on different machines. From the user’s perspective, there is no difference: DCOM provides an abstraction layer that makes both the client and the server appear as if they are on the same machine.

Under the hood, however, COM uses TCP as the RPC transport layer to enable communication across machines.

Distributed COM implementation

Distributed COM implementation

Certain requirements must be met to extend a COM object into a DCOM object. The most important one for our research is the presence of the AppID subkey in the registry, located under the COM CLSID entry.

The AppID value contains a GUID that maps to a corresponding key under HKEY_CLASSES_ROOT\AppID. Several subkeys may exist under this GUID. Two critical ones are:

  • AccessPermission: controls access permissions.
  • LaunchPermission: controls activation permissions.

These registry settings grant remote clients permissions to activate and interact with DCOM objects.

Lateral movement via DCOM

After attackers compromise a host, their next objective is often to compromise additional machines. This is what we call lateral movement. One common lateral movement technique is to achieve remote command execution on a target machine. There are many ways to do this, one of which involves abusing DCOM objects.

In recent years, many DCOM objects have been discovered. This research focuses on the objects exposed by the Impacket script dcomexec.py that can be used for command execution. More specifically, three exposed objects are used: ShellWindows, ShellBrowserWindow and MMC20.

  1. ShellWindows
    ShellWindows was one of the first DCOM objects to be identified. It represents a collection of open shell windows and is hosted by explorer.exe, meaning any COM client communicates with that process.

    In Impacket’s dcomexec.py, once an instance of this COM object is created on a remote machine, the script provides a semi-interactive shell.

    Each time a user enters a command, the function exposed by the COM object is called. The command output is redirected to a file, which the script retrieves via SMB and displays back to simulate a regular shell.

    Internally, the script runs this command when connecting:

    cmd.exe /Q /c cd \ 1> \\127.0.0.1\ADMIN$\__17602 2>&1

    This sets the working directory to C:\ and redirects the output to the ADMIN$ share under the filename __17602. After that, the script checks whether the file exists; if it does, execution is considered successful and the output appears as if in a shell.

    When running dcomexec.py against Windows 10 and 11 using the ShellWindows object, the script hangs after confirming SMB connection initialization and printing the SMB banner. As I mentioned in my personal blog post, it appears that this DCOM object no longer has permission to write to the ADMIN$ share. A simple fix is to redirect the output to a directory the DCOM object can write to, such as the Temp folder. The Temp folder can then be accessed under the same ADMIN$ share. A small change in the code resolves the issue. For example:

    OUTPUT_FILENAME = 'Temp\\__' + str(time.time())[:5]

  2. ShellBrowserWindow
    The ShellBrowserWindow object behaves almost identically to ShellWindows and exhibits the same behavior on Windows 10. The same workaround that we used for ShellWindows applies in this case. However, on Windows 11, this object no longer works for command execution.
  3. MMC20
    The MMC20.Application COM object is the automation interface for Microsoft Management Console (MMC). It exposes methods and properties that allow MMC snap-ins to be automated.

    This object has historically worked across all Windows versions. Starting with Windows Server 2025, however, attempting to use it triggers a Defender alert, and execution is blocked.

    As shown in earlier examples, the dcomexec.py script writes the command output to a file under ADMIN$, with a filename that begins with __:

    OUTPUT_FILENAME = '__' + str(time.time())[:5]

    Defender appears to check for files written under ADMIN$ that start with __, and when it detects one, it blocks the process and alerts the user. A quick fix is to simply remove the double underscores from the output filename.

    Another way to bypass this issue is to use the same workaround used for ShellWindows – redirecting the output to the Temp folder. The table below outlines the status of these objects across different Windows versions.

    Windows Server 2025 Windows Server 2022 Windows 11 Windows 10
    ShellWindows Doesn’t work Doesn’t work Works but needs a fix Works but needs a fix
    ShellBrowserWindow Doesn’t work Doesn’t work Doesn’t work Works but needs a fix
    MMC20 Detected by Defender Works Works Works

Enumerating COM/DCOM objects

The first step to identifying which DCOM objects could be used for lateral movement is to enumerate them. By enumerating, I don’t just mean listing the objects. Enumeration involves:

  • Finding objects and filtering specifically for DCOM objects.
  • Identifying their interfaces.
  • Inspecting the exposed functions.

Automating enumeration is difficult because most COM objects lack a type library (TypeLib). A TypeLib acts as documentation for an object: which interfaces it supports, which functions are exposed, and the definitions of those functions. Even when TypeLibs are available, manual inspection is often still required, as we will explain later.

There are several approaches to enumerating COM objects depending on their use cases. Next, we’ll describe the methods I used while conducting this research, taking into account both automated and manual methods.

  1. Automation using PowerShell
    In PowerShell, you can use .NET to create and interact with DCOM objects. Objects can be created using either their ProgID or CLSID, after which you can call their functions (as shown in the figure below).
    Shell.Application COM object function list in PowerShell

    Shell.Application COM object function list in PowerShell

    Under the hood, PowerShell checks whether the COM object has a TypeLib and implements the IDispatch interface. IDispatch enables late binding, which allows runtime dynamic object creation and function invocation. With these two conditions met, PowerShell can dynamically interact with COM objects at runtime.

    Our strategy looks like this:

    As you can see in the last box, we perform manual inspection to look for functions with names that could be of interest, such as Execute, Exec, Shell, etc. These names often indicate potential command execution capabilities.

    However, this approach has several limitations:

    • TypeLib requirement: Not all COM objects have a TypeLib, so many objects cannot be enumerated this way.
    • IDispatch requirement: Not all COM objects implement the IDispatch interface, which is required for PowerShell interaction.
    • Interface control: When you instantiate an object in PowerShell, you cannot choose which interface the instance will be tied to. If a COM class implements multiple interfaces, PowerShell will automatically select the one marked as [default] in the TypeLib. This means that other non-default interfaces, which may contain additional relevant functionality, such as command execution, could be overlooked.
  2. Automation using C++
    As you might expect, C++ is one of the languages that natively supports COM clients. Using C++, you can create instances of COM objects and call their functions via header files that define the interfaces.However, with this approach, we are not necessarily interested in calling functions directly. Instead, the goal is to check whether a specific COM object supports certain interfaces. The reasoning is that many interfaces have been found to contain functions that can be abused for command execution or other purposes.

    This strategy primarily relies on an interface called IUnknown. All COM interfaces should inherit from this interface, and all COM classes should implement it.The IUnknown interface exposes three main functions. The most important is QueryInterface(), which is used to ask a COM object for a pointer to one of its interfaces.So, the strategy is to:

    • Enumerate COM classes in the system by reading CLSIDs under the HKEY_CLASSES_ROOT\CLSID key.
    • Check whether they support any known valuable interfaces. If they do, those classes may be leveraged for command execution or other useful functionality.

    This method has several advantages:

    • No TypeLib dependency: Unlike PowerShell, this approach does not require the COM object to have a TypeLib.
    • Use of IUnknown: In C++, you can use the QueryInterface function from the base IUnknown interface to check if a particular interface is supported by a COM class.
    • No need for interface definitions: Even without knowing the exact interface structure, you can obtain a pointer to its virtual function table (vtable), typically cast as a void*. This is enough to confirm the existence of the interface and potentially inspect it further.

    The figure below illustrates this strategy:

    This approach is good in terms of automation because it eliminates the need for manual inspection. However, we are still only checking well-known interfaces commonly used for lateral movement, while potentially missing others.

  3. Manual inspection using open-source tools

    As you can see, automation can be difficult since it requires several prerequisites and, in many cases, still ends with a manual inspection. An alternative approach is manual inspection using a tool called OleViewDotNet, developed by James Forshaw. This tool allows you to:
    • List all COM classes in the system.
    • Create instances of those classes.
    • Check their supported interfaces.
    • Call specific functions.
    • Apply various filters for easier analysis.
    • Perform other inspection tasks.
    Open-source tool for inspecting COM interfaces

    Open-source tool for inspecting COM interfaces

    One of the most valuable features of this tool is its naming visibility. OleViewDotNet extracts the names of interfaces and classes (when available) from the Windows Registry and displays them, along with any associated type libraries.

    This makes manual inspection easier, since you can analyze the names of classes, interfaces, or type libraries and correlate them with potentially interesting functionality, for example, functions that could lead to command execution or persistence techniques.

Control Panel items as attack surfaces

Control Panel items allow users to view and adjust their computer settings. These items are implemented as DLLs that export the CPlApplet function and typically have the .cpl extension. Control Panel items can also be executables, but our research will focus on DLLs only.

Control Panel items

Control Panel items

Attackers can abuse CPL files for initial access. When a user executes a malicious .cpl file (e.g., delivered via phishing), the system may be compromised – a technique mapped to MITRE ATT&CK T1218.002.

Adversaries may also modify the extensions of malicious DLLs to .cpl and register them in the corresponding locations in the registry.

  • Under HKEY_CURRENT_USER:
    HKCU\Software\Microsoft\Windows\CurrentVersion\Control Panel\Cpls
  • Under HKEY_LOCAL_MACHINE:
    • For 64-bit DLLs:
      HKLM\Software\Microsoft\Windows\CurrentVersion\Control Panel\Cpls
    • For 32-bit DLLs:
      HKLM\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Control Panel\Cpls

These locations are important when Control Panel DLLs need to be available to the current logged-in user or to all users on the machine. However, the “Control Panel” subkey and its “Cpls” subkey under HKCU should be created manually, unlike the “Control Panel” and “Cpls” subkeys under HKLM, which are created automatically by the operating system.

Once registered, the DLL (CPL file) will load every time the Control Panel is opened, enabling persistence on the victim’s system.

It’s worth noting that even DLLs that do not comply with the CPL specification, do not export CPlApplet, or do not have the .cpl extension can still be executed via their DllEntryPoint function if they are registered under the registry keys listed above.

There are multiple ways to execute Control Panel items:

  • From cmd: control.exe [filename].cpl
  • By double-clicking the .cpl file.

Both methods use rundll32.exe under the hood:

rundll32.exe shell32.dll,Control_RunDLL [filename].cpl

This calls the Control_RunDLL function from shell32.dll, passing the CPL file as an argument. Everything inside the CPlApplet function will then be executed.

However, if the CPL file has been registered in the registry as shown earlier, then every time the Control Panel is opened, the file is loaded into memory through the COM Surrogate process (dllhost.exe):

COM Surrogate process loading the CPL file

COM Surrogate process loading the CPL file

What happened was that a Control Panel with a COM client used a COM object to load these CPL files. We will talk about this COM object in more detail later.

The COM Surrogate process was designed to host COM server DLLs in a separate process rather than loading them directly into the client process’s address space. This isolation improves stability for the in-process server model. This hosting behavior can be configured for a COM object in the registry if you want a COM server DLL to run inside a separate process because, by default, it is loaded in the same process.

‘DCOMing’ through Control Panel items

While following the manual approach of enumerating COM/DCOM objects that could be useful for lateral movement, I came across a COM object called COpenControlPanel, which is exposed through shell32.dll and has the CLSID {06622D85-6856-4460-8DE1-A81921B41C4B}. This object exposes multiple interfaces, one of which is IOpenControlPanel with IID {D11AD862-66DE-4DF4-BF6C-1F5621996AF1}.

IOpenControlPanel interface in the OleViewDotNet output

IOpenControlPanel interface in the OleViewDotNet output

I immediately thought of its potential to compromise Control Panel items, so I wanted to check which functions were exposed by this interface. Unfortunately, neither the interface nor the COM class has a type library.

COpenControlPanel interfaces without TypeLib

COpenControlPanel interfaces without TypeLib

Normally, checking the interface definition would require reverse engineering, so at first, it looked like we needed to take a different research path. However, it turned out that the IOpenControlPanel interface is documented on MSDN, and according to the documentation, it exposes several functions. One of them, called Open, allows a specified Control Panel item to be opened using its name as the first argument.

Full type and function definitions are provided in the shobjidl_core.h Windows header file.

Open function exposed by IOpenControlPanel interface

Open function exposed by IOpenControlPanel interface

It’s worth noting that in newer versions of Windows (e.g., Windows Server 2025 and Windows 11), Microsoft has removed interface names from the registry, which means they can no longer be identified through OleViewDotNet.

COpenControlPanel interfaces without names

COpenControlPanel interfaces without names

Returning to the COpenControlPanel COM object, I found that the Open function can trigger a DLL to be loaded into memory if it has been registered in the registry. For the purposes of this research, I created a DLL that basically just spawns a message box which is defined under the DllEntryPoint function. I registered it under HKCU\Software\Microsoft\Windows\CurrentVersion\Control Panel\Cpls and then created a simple C++ COM client to call the Open function on this interface.

As expected, the DLL was loaded into memory. It was hosted in the same way that it would be if the Control Panel itself was opened: through the COM Surrogate process (dllhost.exe). Using Process Explorer, it was clear that dllhost.exe loaded my DLL while simultaneously hosting the COpenControlPanel object along with other COM objects.

COM Surrogate loading a custom DLL and hosting the COpenControlPanel object

COM Surrogate loading a custom DLL and hosting the COpenControlPanel object

Based on my testing, I made the following observations:

  1. The DLL that needs to be registered does not necessarily have to be a .cpl file; any DLL with a valid entry point will be loaded.
  2. The Open() function accepts the name of a Control Panel item as its first argument. However, it appears that even if a random string is supplied, it still causes all DLLs registered in the relevant registry location to be loaded into memory.

Now, what if we could trigger this COM object remotely? In other words, what if it is not just a COM object but also a DCOM object? To verify this, we checked the AppID of the COpenControlPanel object using OleViewDotNet.

COpenControlPanel object in OleViewDotNet

COpenControlPanel object in OleViewDotNet

Both the launch and access permissions are empty, which means the object will follow the system’s default DCOM security policy. By default, members of the Administrators group are allowed to launch and access the DCOM object.

Based on this, we can build a remote strategy. First, upload the “malicious” DLL, then use the Remote Registry service to register it in the appropriate registry location. Finally, use a trigger acting as a DCOM client to remotely invoke the Open() function, causing our DLL to be loaded. The diagram below illustrates the flow of this approach.

Malicious DLL loading using DCOM

Malicious DLL loading using DCOM

The trigger can be written in either C++ or Python, for example, using Impacket. I chose Python because of its flexibility. The trigger itself is straightforward: we define the DCOM class, the interface, and the function to call. The full code example can be found here.

Once the trigger runs, the behavior will be the same as when executing the COM client locally: our DLL will be loaded through the COM Surrogate process (dllhost.exe).

As you can see, this technique not only achieves command execution but also provides persistence. It can be triggered in two ways: when a user opens the Control Panel or remotely at any time via DCOM.

Detection

The first step in detecting such activity is to check whether any Control Panel items have been registered under the following registry paths:

  • HKCU\Software\Microsoft\Windows\CurrentVersion\Control Panel\Cpls
  • HKLM\Software\Microsoft\Windows\CurrentVersion\Control Panel\Cpls
  • HKLM\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Control Panel\Cpls

Although commonly known best practices and research papers regarding Windows security advise monitoring only the first subkey, for thorough coverage it is important to monitor all of the above.

In addition, monitoring dllhost.exe (COM Surrogate) for unusual COM objects such as COpenControlPanel can provide indicators of malicious activity.
Finally, it is always recommended to monitor Remote Registry usage because it is commonly abused in many types of attacks, not just in this scenario.

Conclusion

In conclusion, I hope this research has clarified yet another attack vector and emphasized the importance of implementing hardening practices. Below are a few closing points for security researchers to take into account:

  • As shown, DCOM represents a large attack surface. Windows exposes many DCOM classes, a significant number of which lack type libraries – meaning reverse engineering can reveal additional classes that may be abused for lateral movement.
  • Changing registry values to register malicious CPLs is not good practice from a red teaming ethics perspective. Defender products tend to monitor common persistence paths, but Control Panel applets can be registered in multiple registry locations, so there is always a gap that can be exploited.
  • Bitness also matters. On x64 systems, loading a 32-bit DLL will spawn a 32-bit COM Surrogate process (dllhost.exe *32). This is unusual on 64-bit hosts and therefore serves as a useful detection signal for defenders and an interesting red flag for red teamers to consider.

Hunting for Mythic in network traffic

11 December 2025 at 07:00

Post-exploitation frameworks

Threat actors frequently employ post-exploitation frameworks in cyberattacks to maintain control over compromised hosts and move laterally within the organization’s network. While they once favored closed-source frameworks, such as Cobalt Strike and Brute Ratel C4, open-source projects like Mythic, Sliver, and Havoc have surged in popularity in recent years. Malicious actors are also quick to adopt relatively new frameworks, such as Adaptix C2.

Analysis of popular frameworks revealed that their development focuses heavily on evading detection by antivirus and EDR solutions, often at the expense of stealth against systems that analyze network traffic. While obfuscating an agent’s network activity is inherently challenging, agents must inevitably communicate with their command-and-control servers. Consequently, an agent’s presence in the system and its malicious actions can be detected with the help of various network-based intrusion detection systems (IDS) and, of course, Network Detection and Response (NDR) solutions.

This article examines methods for detecting the Mythic framework within an infrastructure by analyzing network traffic. This framework has gained significant traction among various threat actors, including Mythic Likho (Arcane Wolf) и GOFFEE (Paper Werewolf), and continues to be used in APT and other attacks.

The Mythic framework

Mythic C2 is a multi-user command and control (C&C, or C2) platform designed for managing malicious agents during complex cyberattacks. Mythic is built on a Docker container architecture, with its core components – the server, agents, and transport modules – written in Python. This architecture allows operators to add new agents, communication channels, and custom modifications on the fly.

Since Mythic is a versatile tool for the attacker, from the defender’s perspective, its use can align with multiple stages of the Unified Kill Chain, as well as a large number of tactics, techniques, and procedures in the MITRE ATT&CK® framework.

  • Pivoting is a tactic where the attacker uses an already compromised system as a pivot point to gain access to other systems within the network. In this way, they gradually expand their presence within the organization’s infrastructure, bypassing firewalls, network segmentation, and other security controls.
  • Collection (TA0009) is a tactic focused on gathering and aggregating information of value to the attacker: files, credentials, screenshots, and system logs. In the context of network operations, collection is often performed locally on compromised hosts, with data then packaged for transfer. Tools like Mythic automate the discovery and selection of data sought by the adversary.
  • Exfiltration (TA0010) is the process of moving collected information out of the secured network via legitimate or covert channels, such as HTTP(s), DNS, or SMB, etc. Attackers may use resident agents or intermediate relays (pivot hosts) to conceal the exfiltration source and route.
  • Command and Control (TA0011) encompasses the mechanisms for establishing and maintaining a communication channel between the operator and compromised hosts to transmit commands and receive status updates. This includes direct connections, relaying through pivot hosts, and the use of covert protocols. Frameworks like Mythic provide advanced C2 capabilities, such as scheduled command execution, tunneling, and multi-channel communication, which complicate the detection and blocking of their activity.

This article focuses exclusively on the Command and Control (TA0011) tactic, whose techniques can be effectively detected within the network traffic of Mythic agents.

Detecting Mythic agent activity in network traffic

At the time of writing, Mythic supports data transfer over HTTP/S, WebSocket, TCP, SMB, DNS, and MQTT. The platform also boasts over a dozen different agents, written in Go, Python, and C#, designed for Windows, macOS, and Linux.

Mythic employs two primary architectures for its command network:

  • In this model, agents communicate with adjacent agents forming a chain of connections which eventually leads to a node communicating directly with the Mythic C2 server. For this purpose, agents utilize TCP and SMB.
  • In this model, agents communicate directly with the C2 server via HTTP/S, WebSocket, MQTT, or DNS.

P2P communication

Mythic provides pivoting capabilities via named SMB pipes and TCP sockets. To detect Mythic agent activity in P2P mode, we will examine their network traffic and create corresponding Suricata detection rules (signatures).

P2P communication via SMB

When managing agents via the SMB protocol, a named pipe is used by default for communication, with its name matching the agent’s UUID.

Although this parameter can be changed, it serves as a reliable indicator and can be easily described with a regular expression. Example:
[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}

For SMB communication, agents encode and encrypt data according to the pattern: base64(UUID+AES256(JSON)). This data is then split into blocks and transmitted over the network. The screenshot below illustrates what a network session for establishing a connection between agents looks like in Wireshark.

Commands and their responses are packaged within the MythicMessage data structure. This structure contains three header fields, as well as the commands themselves or the corresponding responses:

  • Total size (4 bytes)
  • Number of data blocks (4 bytes)
  • Current block number (4 bytes)
  • Base64-encoded data

The screenshot below shows an example of SMB communication between agents.

The agent (10.63.101.164) sends a command to another agent in the MythicMessage format. The first three Write Requests transmit the total message size, total number of blocks, and current block number. The fourth request transmits the Base64-encoded data. This is followed by a sequence of Read Requests, which are also transmitted in the MythicMessage format.

Below are the data transmitted in the fourth field of the MythicMessage structure.

The content is encoded in Base64. Upon decoding, the structure of the transmitted information becomes visible: it begins with the UUID of the infected host, followed by a data block encrypted using AES-256.

The fact that the data starts with a UUID string can be leveraged to create a signature-based detection rule that searches network packets for the identifier pattern.

To search for packets containing a UUID, the following signature can be applied. It uses specific request types and protocol flags as filters (Command: Ioctl (11), Function: FSCTL_PIPE_WAIT (0x00110018)), followed by a check to see if the pipe name matches the UUID pattern.

alert tcp any any -> any [139, 445] (msg: "Trojan.Mythic.SMB.C&C"; flow: to_server, established; content: "|fe|SMB"; offset: 4; depth: 4; content: "|0b 00|"; distance: 8; within: 2; content: "|18 00 11 00|"; distance: 48; within: 12; pcre: "/\x48\x00\x00\x00[\x00-\xFF]{2}([a-z0-9]\x00){8}\-\x00([a-z0-9]\x00){4}\-\x00([a-z0-9]\x00){4}\-\x00([a-z0-9]\x00){4}\-\x00([a-z0-9]\x00){12}$/R"; threshold: type both, track by_src, count 1, seconds 60; reference: url, https://github.com/MythicC2Profiles/smb; classtype: ndr1; sid: 9000101; rev: 1;)

Agent activity can also be detected by analyzing data transmitted in SMB WriteRequest packets with the protocol flag Command: Write (9) and a distinct packet structure where the BlobOffset and BlobLen fields are set to zero. If the Data field is Base64-encoded and, after decoding, begins with a UUID-formatted string, this indicates a command-and-control channel.

alert tcp any any -> any [139, 445] (msg: "Trojan.Mythic.SMB.C&C"; flow: to_server, established; dsize: > 360; content: "|fe|SMB"; offset: 4; depth: 4; content: "|09 00|"; distance: 8; within: 2; content: "|00 00 00 00 00 00 00 00 00 00 00 00|"; distance: 86; within: 12; base64_decode: bytes 64, offset 0, relative; base64_data; pcre: "/^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}/"; threshold: type both, track by_src, count 1, seconds 60; reference: url, https://github.com/MythicC2Profiles/smb; classtype: ndr1; sid: 9000102; rev: 1;)

Below is the KATA NDR user interface displaying an alert about detecting a Mythic agent operating in P2P mode over SMB. In this instance, the first rule – which checks the request type, protocol flags, and the UUID pattern – was triggered.

It should be noted that these signatures have a limitation. If the SMBv3 protocol with encryption enabled is used, Mythic agent activity cannot be detected with signature-based methods. A possible alternative is behavioral analysis. However, in this context, it suffers from low accuracy and a high false-positive rate. The SMB protocol is widely used by organizations for various legitimate purposes, making it difficult to isolate behavioral patterns that definitively indicate malicious activity.

P2P communication via TCP

Mythic also supports P2P communications via TCP. The connection initialization process appears in network traffic as follows:

As with SMB, the MythicMessage structure is used for transmitting and receiving data. First, the data length (4 bytes) is sent as a big-endian DWORD in a separate packet. Subsequent packets transmit the number of data blocks, the current block number, and the data itself. However, unlike SMB packets, the value of the current block number field is always 0x00000000, due to TCP’s built-in packet fragmentation support.

The data encoding scheme is also analogous to what we observed with SMB and appears as follows: base64(UUID+AES256(JSON)). Below is an example of a network packet containing Mythic data.

The decoded data appears as follows:

Similar to communication via SMB, signature-based detection rules can be created for TCP traffic to identify Mythic agent activity by searching for packets containing UUID-formatted strings. Below are two Suricata detection rules. The first rule is a utility rule. It does not generate security alerts but instead tags the TCP session with an internal flag, which is then checked by another rule. The second rule verifies the flag and applies filters to confirm that the current packet is being analyzed at the beginning of a network session. It then decodes the Base64 data and searches the resulting content for a UUID-formatted string.

alert tcp any any -> any any (msg: "Trojan.Mythic.TCP.C&C"; flow: from_server, established; dsize: 4; stream_size: server, <, 6; stream_size: client, <, 3; content: "|00 00|"; depth: 2; pcre: "/^\x00\x00[\x00-\x5C]{1}[\x00-\xFF]{1}$/"; flowbits: set, mythic_tcp_p2p_msg_len; flowbits: noalert; threshold: type both, track by_src, count 1, seconds 60; reference: url, https://github.com/MythicC2Profiles/tcp; classtype: ndr1; sid: 9000103; rev: 1;)

alert tcp any any -> any any (msg: "Trojan.Mythic.TCP.C&C"; flow: from_server, established; dsize: > 300; stream_size: server, <, 6000; stream_size: client, <, 6000; flowbits: isset, mythic_tcp_p2p_msg_len; content: "|00 00 00|"; depth: 3; content: "|00 00 00 00|"; distance: 1; within: 4; base64_decode: bytes 64, offset 0, relative; base64_data; pcre: "/^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}/"; threshold: type both, track by_src, count 1, seconds 60; reference: url, https://github.com/MythicC2Profiles/tcp; classtype: ndr1; sid: 9000104; rev: 1;)

Below is the NDR interface displaying an example of the two rules detecting a Mythic agent operating in P2P mode over TCP.

Egress transport modules

Covert Egress communication

For stealthy operations, Mythic allows agents to be managed through popular services. This makes its activity less conspicuous within network traffic. Mythic includes transport modules based on the following services:

  • Discord
  • GitHub
  • Slack

Of these, only the first two remain relevant at the time of writing. Communication via Slack (the Slack C2 Profile transport module) is no longer supported by the developers and is considered deprecated, so we will not examine it further.

The Discord C2 Profile transport module

The use of the Discord service as a mediator for C2 communication within the Mythic framework has been gaining popularity recently. In this scenario, agent traffic is indistinguishable from normal Discord activity, with commands and their execution results masquerading as messages and file attachments. Communication with the server occurs over HTTPS and is encrypted with TLS. Therefore, detecting Mythic traffic requires decrypting this.

Analyzing decrypted TLS traffic

Let’s assume we are using an NDR platform in conjunction with a network traffic decryption (TLS inspection) system to detect suspicious network activity. In this case, we operate under the assumption that we can decrypt all TLS traffic. Let’s examine possible detection rules for that scenario.

Agent and server communication occurs via Discord API calls to send messages to a specific channel. Communication between the agent and Mythic uses the MythicMessageWrapper structure, which contains the following fields:

  • message: the transmitted data
  • sender_id: a GUID generated by the agent, included in every message
  • to_server: a direction flag – a message intended for the server or the agent
  • id: not used
  • final: not used

Of particular interest to us is the message field, which contains the transmitted data encoded in Base64. The MythicMessageWrapper message is transmitted in plaintext, making it accessible to anyone with read permissions for messages on the Discord server.

Below is an example of data transmission via messages in a Discord channel.

To establish a connection, the agent authenticates to the Discord server via the API call /api/v10/gateway/bot. We observe the following data in the network traffic:

After successful initialization, the agent gains the ability to receive and respond to commands. To create a message in the channel, the agent makes a POST request to the API endpoint /channels/<channel.id>/messages. The network traffic for this call is shown in the screenshot below.

After decoding the Base64, the content of the message field appears as follows:

A structure characteristic of a UUID is visible at the beginning of the packet.

After processing the message, the agent deletes it from the channel via a DELETE request to the API endpoint /channels/{channel.id}/messages/{message.id}.

Below is a Suricata rule that detects the agent’s Discord-based communication activity. It checks the API activity for creating HTTP messages for the presence of Base64-encoded data containing the agent’s UUID.

alert tcp any any -> any any (msg: "Trojan.Mythic.HTTP.C&C"; flow: to_server, established; content: "POST"; http_method; content: "/api/"; http_uri; content: "/channels/"; distance: 0; http_uri; pcre: "/\/messages$/U"; content: "|7b 22|content|22|"; depth: 20; http_client_body; content: "|22|sender_id"; depth: 1500; http_client_body; pcre: "/\x22sender_id\x5c\x22\x3a\x5c\x22[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}/"; threshold: type both, track by_src, count 1, seconds 60; reference: url, https://github.com/MythicC2Profiles/discord; classtype: ndr1; sid: 9000105; rev: 1;)

Below is the NDR user interface displaying an example of detecting the activity of the Discord C2 Profile transport module for a Mythic agent within decrypted HTTP traffic.

Analyzing encrypted TLS traffic

If Discord usage is permitted on the network and there is no capability to decrypt traffic, it becomes nearly impossible to detect agent activity. In this scenario, behavioral analysis of requests to the Discord server may prove useful. Below is network traffic showing frequent TLS connections to the Discord server, which could indicate commands being sent to an agent.

In this case, we can use a Suricata rule to detect the frequent TLS sessions with Discord servers:

alert tcp any any -> any any (msg: "NetTool.PossibleMythicDiscordEgress.TLS.C&C"; flow: to_server, established; tls_sni; content: "discord.com"; nocase; threshold: type both, track by_src, count 4, seconds 420; reference: url, https://github.com/MythicC2Profiles/discord; classtype: ndr3; sid: 9000106; rev: 1;)

Another method for detecting these communications involves tracking multiple DNS queries to the discord.com domain.

The following rule can be applied to detect these:

alert udp any any -> any 53 (msg: "NetTool.PossibleMythicDiscordEgress.DNS.C&C"; content: "|01 00 00 01 00 00 00 00 00 00|"; depth: 10; offset: 2; content: "|07|discord|03|com|00|"; nocase; distance: 0; threshold: type both, track by_src, count 4, seconds 60; reference: url, https://github.com/MythicC2Profiles/discord; classtype: ndr3; sid: 9000107; rev: 1;)

Below is the NDR user interface showing an example of a custom rule in operation, detecting the activity of the Discord C2 Profile transport module for a Mythic agent within encrypted traffic based on characteristic DNS queries.

The proposed rule options have low accuracy and can generate a high number of false positives. Therefore, they must be adapted to the specific characteristics of the infrastructure in which they will run. Threshold and count parameters, which control the triggering frequency and time window, require tuning.

GitHub C2 Profile transport module

GitHub’s popularity has made it an attractive choice as a mediator for managing Mythic agents. The core concept is the same as in other covert Egress communication transport modules. Communication with GitHub utilizes HTTPS. Successful operation requires an account on the target platform and the ability to communicate via API calls. The transport module utilizes the GitHub API to send comments to pre-created Issues and to commit files to a branch within a repository controlled by the attackers. In this model, the agent interacts only with GitHub: it creates and reads comments, uploads files, and manages branches. It does not communicate with any other servers. The communication algorithm via GitHub is as follows:

  1. The agent posts a comment (check-in) to a designated Issue on GitHub, intended for agents to report their results.
  2. The Mythic server validates the comment, deletes it, and posts a reply in an issue designated for server use.
  3. The agent creates a branch with a name matching its UUID and writes a get_tasking file to it (performs a push request).
  4. The Mythic server reads the file and writes a response file to the same branch.
  5. The agent reads the response file, deletes the branch, pauses, and repeats the cycle.
Analyzing decrypted TLS traffic

Let’s consider an approach to detecting agent activity when traffic decryption is possible.

Agent communication with the server utilizes API calls to GitHub. The payload is encoded in Base64 and published in plaintext; therefore, anyone who can view the repository or analyze the traffic contents can decode it.

Analysis of agent communication revealed that the most useful traffic for creating detection rules is associated with publishing check-in comments, creating a branch, and publishing a file.

During the check-in phase, the agent posts a comment to register a new agent and establish communication.

The transmitted data is encoded in Base64 and contains the agent’s UUID and the portion of the message encrypted using AES-256.

This allows for a signature that detects UUID-formatted substrings within GitHub comment creation requests.

alert tcp any any -> any any (msg: "Trojan.Mythic.HTTP.C&C"; flow: to_server, established; content: "POST"; http_method; content: "api.github.com"; http_host; content: "/repos/"; depth: 8; http_uri; pcre: "/\/comments$/U"; content: "|22|body|22|"; depth: 8; http_client_body; base64_decode: bytes 300, offset 2, relative; base64_data; pcre: "/^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}/"; threshold: type both, track by_src, count 1, seconds 60; reference: url, https://github.com/MythicC2Profiles/github; classtype: ndr1; sid: 9000108; rev: 1;)

Another stage suitable for detection is when the agent creates a separate branch with its UUID as the name. All subsequent relevant communication with the server will occur within this branch. Here is an example of a branch creation request:

Therefore, we can create a detection rule to identify UUID-formatted strings within branch creation requests.

alert tcp any any -> any any (msg: "Trojan.Mythic.HTTP.C&C"; flow: to_server, established; content: "POST"; http_method; content: "api.github.com"; http_host; content: "/repos/"; depth: 100; http_uri; content: "/git/refs"; distance: 0; http_uri; content: "|22|ref|22 3a|"; depth: 10; http_client_body; content: "refs/heads/"; distance: 0; within: 50; http_client_body; pcre: "/refs\/heads\/[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}\x22/"; threshold: type both, track by_src, count 1, seconds 60; reference: url, https://github.com/MythicC2Profiles/github; classtype: ndr1; sid: 9000109; rev: 1;)

After creating the branch, the agent writes a file to it (sends a push request), which contains Base64-encoded data.

Therefore, we can create a rule to trigger on file publication requests to a branch whose name matches the UUID pattern.

alert tcp any any -> any any (msg: "Trojan.Mythic.HTTP.C&C"; flow: to_server, established; content: "PUT"; http_method; content: "api.github.com"; http_host; content: "/repos/"; depth:8; http_uri; content: "/contents/"; distance: 0; http_uri; content: "|22|content|22|"; depth: 100; http_client_body; pcre: "/\x22message\x22\x3a\x22[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}\x22/"; threshold: type both, track by_src, count 1, seconds 60; reference: url, https://github.com/MythicC2Profiles/github; classtype: ndr1; sid: 9000110; rev: 1;)

The screenshot below shows how the NDR solution logs all suspicious communications using the GitHub API and subsequently identifies the Mythic agent’s activity. The result is an alert with the verdict Trojan.Mythic.HTTP.C&C.

Analyzing encrypted TLS traffic

Communication with GitHub occurs over HTTPS; therefore, in the absence of traffic decryption capability, signature-based methods for detecting agent activity cannot be applied. Let’s consider a behavioral agent activity detection approach.

For instance, it is possible to detect connections to GitHub servers that are atypical in frequency and purpose, originating from network segments where this activity is not expected. The screenshot below shows an example of an agent’s multiple TLS sessions. The traffic reflects the execution of several commands, as well as idle time, manifested as constant polling of the server while awaiting new tasks.

Multiple TLS sessions with the GitHub service from uncharacteristic network segments can be detected using the rule presented below:

alert tcp any any -> any any (msg:"NetTool.PossibleMythicGitHubEgress.TLS.C&C"; flow: to_server, established; tls_sni; content: "api.github.com"; nocase; threshold: type both, track by_src, count 4, seconds 60; reference: url, https://github.com/MythicC2Profiles/github; classtype: ndr3; sid: 9000111; rev: 1;)

Additionally, multiple DNS queries to the service can be logged in the traffic.

This activity is detected with the help of the following rule:

alert udp any any -> any 53 (msg: "NetTool.PossibleMythicGitHubEgress.DNS.C&C"; content: "|01 00 00 01 00 00 00 00 00 00|"; depth: 10; offset: 2; content: "|03|api|06|github|03|com|00|"; nocase; distance: 0; threshold: type both, track by_src, count 12, seconds 180; reference: url, https://github.com/MythicC2Profiles/github; classtype: ndr3; sid: 9000112; rev: 1;)

The screenshot below shows the NDR interface with an example of the first rule in action, detecting traces of the GitHub profile activity for a Mythic agent within encrypted TLS traffic.

The suggested rule options can produce false positives, so to improve their effectiveness, they must be adapted to the specific characteristics of the infrastructure in which they will run. The parameters of the threshold keyword – specifically the count and seconds values, which control the number of events required to generate an alert and the time window for their occurrence in NDR – must be configured.

Direct Egress communication

The Egress communication model allows agents to interact directly with the C2 server via the following protocols:

  • HTTP(S)
  • WebSocket
  • MQTT
  • DNS

The first two protocols are the most prevalent. The DNS-based transport module is still under development, and the module based on MQTT sees little use among operators. We will not examine them within the scope of this article.

Communication via HTTP

HTTP is the most common protocol for building a Mythic agent control network. The HTTP transport container acts as a proxy between the agents and the Mythic server. It allows data to be transmitted in both plaintext and encrypted form. Crucially, the metadata is not encrypted, which enables the creation of signature-based detection rules.

Below is an example of unencrypted Mythic network traffic over HTTP. During a GET request, data encoded in Base64 is passed in the value of the query parameter.

After decoding, the agent’s UUID – generated according to a specific pattern – becomes visible. This identifier is followed by a JSON object containing the key parameters of the host, collected by the agent.

If data encryption is applied, the network traffic for agent communication appears as shown in the screenshot below.

After decrypting the traffic and decoding from Base64, the communication data reveals the familiar structure: UUID+AES256(JSON).

Therefore, to create a detection signature for this case, we can also rely on the presence of a UUID within the Base64-encoded data in POST requests.

alert tcp any any -> any any (msg: "Trojan.Mythic.HTTP.C&C"; flow: to_server, established; content: "POST"; http_method; content: "|0D 0A 0D 0A|"; base64_decode: bytes 80, offset 0, relative; base64_data; content: "-"; offset: 8; depth: 1; content: "-"; distance: 4; within: 1; content: "-"; distance: 4; within: 1; content: "-"; distance: 4; within: 1; pcre: "/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/"; threshold: type both, track by_src, count 1, seconds 180; reference: md5, 6ef89ccee639b4df42eaf273af8b5ffd; classtype: trojan1; sid: 9000113; rev: 2;)

The screenshot below shows how the NDR platform detects agent communication with the server over HTTP, generating an alert with the name Trojan.Mythic.HTTP.C&C.

Communication via HTTPS

Mythic agents can communicate with the server via HTTPS using the corresponding transport module. In this case, data is encrypted with TLS and is not amenable to signature-based analysis. However, the activity of Mythic agents can be detected if they use the default SSL certificate. Below is an example of network traffic from a Mythic agent with such a certificate.

For this purpose, the following signature is applied:

alert tcp any any -> any any (msg:"Trojan.Mythic.TLS.C&C"; flow:established, from_server; content:"|16 03|"; depth:2; content:"|0B|"; distance:3; within:1; content:"|55 04|"; distance:0; content:"|09|Mythic C2"; nocase; distance:0; threshold:type both,track by_src,count 1,seconds 60; reference:url,github.com/its-a-feature/Mythic; classtype:ndr1; sid:9000114; rev:1;)

WebSocket

The WebSocket protocol enables full-duplex communication between a client and a remote host. Mythic can utilize it for agent management.

The process of agent communication with the server via WebSocket is as follows:

  1. The agent sends a request to the WebSocket container to change the protocol for the HTTP(S) connection.
  2. The agent and the WebSocket container switch to WebSocket to send and receive messages.
  3. The agent sends a message to the WebSocket container requesting tasks from the Mythic container.
  4. The WebSocket container forwards the request to the Mythic container.
  5. The Mythic container returns the tasks to the WebSocket container.
  6. The WebSocket container forwards these tasks to the agent.

It is worth mentioning that in this communication model, both the WebSocket container and the Mythic container reside on the Mythic server. Below is a screenshot of the initial agent connection to the server.

An analysis of the TCP session shows that the actual data is transmitted in the data field in Base64 encoding.

Decoding reveals the familiar data structure: UUID+AES256(JSON).

Therefore, we can use an approach similar to those discussed above to detect agent activity. The signature should rely on the UUID string at the beginning of the data field. The rule first verifies that the session data matches the data:base64 format, then decodes the data field and searches for a string matching the UUID pattern.

alert tcp any any -> any any (msg: "Trojan.Mythic.WebSocket.C&C"; flow: established, from_server; content: "|7B 22|data|22 3a 22|"; depth: 14; pcre: "/^[0-9a-zA-Z\/\+]+[=]{0,2}\x22\x7D\x0A$/R"; content: "|7B 22|data|22 3a 22|"; depth: 14; base64_decode: bytes 48, offset 0, relative; base64_data; pcre: "/^[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}/i"; threshold: type both, track by_src, count 1, seconds 30; reference: url, https://github.com/MythicAgents/; classtype: ndr1; sid: 9000115; rev: 2;)

Below is the Trojan.Mythic.WebSocket.C&C signature triggering on Mythic agent communication over WebSocket.

Takeaways

The Mythic post-exploitation framework continues to gain popularity and evolve rapidly. New agents are emerging, designed for covert persistence within target infrastructures. Despite this evolution, the various implementations of network communication in Mythic share many common characteristics that remain largely consistent over time. This consistency enables IDS/NDR solutions to effectively detect the framework’s agent activity through network traffic analysis.

Mythic supports a wide array of agent management options utilizing several network protocols. Our analysis of agent communications across these protocols revealed that agent activity can be detected by searching for specific data patterns within network traffic. The primary detection criterion involves tracking UUID strings in specific positions within Base64-encoded transmitted data. However, while the general approach to detecting agent activity is similar across protocols, each requires protocol-specific filters. Consequently, creating a single, universal signature for detecting Mythic agents in network traffic is challenging; individual detection rules must be crafted for each protocol. This article has provided signatures that are included in Kaspersky NDR.

Kaspersky NDR is designed to identify current threats within network infrastructures. It enables the detection of all popular post-exploitation frameworks based on their characteristic traffic patterns. Since the network components of these frameworks change infrequently, employing an NDR solution ensures high effectiveness in agent discovery.

Kaspersky verdicts in Kaspersky solutions (Kaspersky Anti-Targeted Attack with NDR module and Kaspersky NGFW)

Trojan.Mythic.SMB.C&C
Trojan.Mythic.TCP.C&C
Trojan.Mythic.HTTP.C&C
Trojan.Mythic.TLS.C&C
Trojan.Mythic.WebSocket.C&C

Detecting DLL hijacking with machine learning: real-world cases

6 October 2025 at 04:00

Introduction

Our colleagues from the AI expertise center recently developed a machine-learning model that detects DLL-hijacking attacks. We then integrated this model into the Kaspersky Unified Monitoring and Analysis Platform SIEM system. In a separate article, our colleagues shared how the model had been created and what success they had achieved in lab environments. Here, we focus on how it operates within Kaspersky SIEM, the preparation steps taken before its release, and some real-world incidents it has already helped us uncover.

How the model works in Kaspersky SIEM

The model’s operation generally boils down to a step-by-step check of all DLL libraries loaded by processes in the system, followed by validation in the Kaspersky Security Network (KSN) cloud. This approach allows local attributes (path, process name, and file hashes) to be combined with a global knowledge base and behavioral indicators, which significantly improves detection quality and reduces the probability of false positives.

The model can run in one of two modes: on a correlator or on a collector. A correlator is a SIEM component that performs event analysis and correlation based on predefined rules or algorithms. If detection is configured on a correlator, the model checks events that have already triggered a rule. This reduces the volume of KSN queries and the model’s response time.

This is how it looks:

A collector is a software or hardware component of a SIEM platform that collects and normalizes events from various sources, and then delivers these events to the platform’s core. If detection is configured on a collector, the model processes all events associated with various processes loading libraries, provided these events meet the following conditions:

  • The path to the process file is known.
  • The path to the library is known.
  • The hashes of the file and the library are available.

This method consumes more resources, and the model’s response takes longer than it does on a correlator. However, it can be useful for retrospective threat hunting because it allows you to check all events logged by Kaspersky SIEM. The model’s workflow on a collector looks like this:

It is important to note that the model is not limited to a binary “malicious/non-malicious” assessment; it ranks its responses by confidence level. This allows it to be used as a flexible tool in SOC practice. Examples of possible verdicts:

  • 0: data is being processed.
  • 1: maliciousness not confirmed. This means the model currently does not consider the library malicious.
  • 2: suspicious library.
  • 3: maliciousness confirmed.

A Kaspersky SIEM rule for detecting DLL hijacking would look like this:

N.KL_AI_DLLHijackingCheckResult > 1

Embedding the model into the Kaspersky SIEM correlator automates the process of finding DLL-hijacking attacks, making it possible to detect them at scale without having to manually analyze hundreds or thousands of loaded libraries. Furthermore, when combined with correlation rules and telemetry sources, the model can be used not just as a standalone module but as part of a comprehensive defense against infrastructure attacks.

Incidents detected during the pilot testing of the model in the MDR service

Before being released, the model (as part of the Kaspersky SIEM platform) was tested in the MDR service, where it was trained to identify attacks on large datasets supplied by our telemetry. This step was necessary to ensure that detection works not only in lab settings but also in real client infrastructures.

During the pilot testing, we verified the model’s resilience to false positives and its ability to correctly classify behavior even in non-typical DLL-loading scenarios. As a result, several real-world incidents were successfully detected where attackers used one type of DLL hijacking — the DLL Sideloading technique — to gain persistence and execute their code in the system.

Let us take a closer look at the three most interesting of these.

Incident 1. ToddyCat trying to launch Cobalt Strike disguised as a system library

In one incident, the attackers successfully leveraged the vulnerability CVE-2021-27076 to exploit a SharePoint service that used IIS as a web server. They ran the following command:

c:\windows\system32\inetsrv\w3wp.exe -ap "SharePoint - 80" -v "v4.0" -l "webengine4.dll" -a \\.\pipe\iisipmd32ded38-e45b-423f-804d-34471928538b -h "C:\inetpub\temp\apppools\SharePoint - 80\SharePoint - 80.config" -w "" -m 0

After the exploitation, the IIS process created files that were later used to run malicious code via the DLL sideloading technique (T1574.001 Hijack Execution Flow: DLL):

C:\ProgramData\SystemSettings.exe
C:\ProgramData\SystemSettings.dll

SystemSettings.dll is the name of a library associated with the Windows Settings application (SystemSettings.exe). The original library contains code and data that the Settings application uses to manage and configure various system parameters. However, the library created by the attackers has malicious functionality and is only pretending to be a system library.

Later, to establish persistence in the system and launch a DLL sideloading attack, a scheduled task was created, disguised as a Microsoft Edge browser update. It launches a SystemSettings.exe file, which is located in the same directory as the malicious library:

Schtasks  /create  /ru "SYSTEM" /tn "\Microsoft\Windows\Edge\Edgeupdates" /sc DAILY /tr "C:\ProgramData\SystemSettings.exe" /F

The task is set to run daily.

When the SystemSettings.exe process is launched, it loads the malicious DLL. As this happened, the process and library data were sent to our model for analysis and detection of a potential attack.

Example of a SystemSettings.dll load event with a DLL Hijacking module verdict in Kaspersky SIEM

Example of a SystemSettings.dll load event with a DLL Hijacking module verdict in Kaspersky SIEM

The resulting data helped our analysts highlight a suspicious DLL and analyze it in detail. The library was found to be a Cobalt Strike implant. After loading it, the SystemSettings.exe process attempted to connect to the attackers’ command-and-control server.

DNS query: connect-microsoft[.]com
DNS query type: AAAA
DNS response: ::ffff:8.219.1[.]155;
8.219.1[.]155:8443

After establishing a connection, the attackers began host reconnaissance to gather various data to develop their attack.

C:\ProgramData\SystemSettings.exe
whoami /priv
hostname
reg query HKLM\SOFTWARE\Microsoft\Cryptography /v MachineGuid
powershell -c $psversiontable
dotnet --version
systeminfo
reg query "HKEY_LOCAL_MACHINE\SOFTWARE\VMware, Inc.\VMware Drivers"
cmdkey /list
REG query "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v PortNumber
reg query "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Servers
netsh wlan show profiles
netsh wlan show interfaces
set
net localgroup administrators
net user
net user administrator
ipconfig /all
net config workstation
net view
arp -a
route print
netstat -ano
tasklist
schtasks /query /fo LIST /v
net start
net share
net use
netsh firewall show config
netsh firewall show state
net view /domain
net time /domain
net group "domain admins" /domain
net localgroup administrators /domain
net group "domain controllers" /domain
net accounts /domain
nltest / domain_trusts
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
reg query HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce
reg query HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
reg query HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Run
reg query HKEY_CURRENT_USER\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce

Based on the attackers’ TTPs, such as loading Cobalt Strike as a DLL, using the DLL sideloading technique (1, 2), and exploiting SharePoint, we can say with a high degree of confidence that the ToddyCat APT group was behind the attack. Thanks to the prompt response of our model, we were able to respond in time and block this activity, preventing the attackers from causing damage to the organization.

Incident 2. Infostealer masquerading as a policy manager

Another example was discovered by the model after a client was connected to MDR monitoring: a legitimate system file located in an application folder attempted to load a suspicious library that was stored next to it.

C:\Program Files\Chiniks\SettingSyncHost.exe
C:\Program Files\Chiniks\policymanager.dll E83F331BD1EC115524EBFF7043795BBE

The SettingSyncHost.exe file is a system host process for synchronizing settings between one user’s different devices. Its 32-bit and 64-bit versions are usually located in C:\Windows\System32\ and C:\Windows\SysWOW64\, respectively. In this incident, the file location differed from the normal one.

Example of a policymanager.dll load event with a DLL Hijacking module verdict in Kaspersky SIEM

Example of a policymanager.dll load event with a DLL Hijacking module verdict in Kaspersky SIEM

Analysis of the library file loaded by this process showed that it was malware designed to steal information from browsers.

Graph of policymanager.dll activity in a sandbox

Graph of policymanager.dll activity in a sandbox

The file directly accesses browser files that contain user data.

C:\Users\<user>\AppData\Local\Google\Chrome\User Data\Local State

The library file is on the list of files used for DLL hijacking, as published in the HijackLibs project. The project contains a list of common processes and libraries employed in DLL-hijacking attacks, which can be used to detect these attacks.

Incident 3. Malicious loader posing as a security solution

Another incident discovered by our model occurred when a user connected a removable USB drive:

Example of a Kaspersky SIEM event where a wsc.dll library was loaded from a USB drive, with a DLL Hijacking module verdict

Example of a Kaspersky SIEM event where a wsc.dll library was loaded from a USB drive, with a DLL Hijacking module verdict

The connected drive’s directory contained hidden folders with an identically named shortcut for each of them. The shortcuts had icons typically used for folders. Since file extensions were not shown by default on the drive, the user might have mistaken the shortcut for a folder and launched it. In turn, the shortcut opened the corresponding hidden folder and ran an executable file using the following command:

"%comspec%" /q /c "RECYCLER.BIN\1\CEFHelper.exe [$DIGITS] [$DIGITS]"

CEFHelper.exe is a legitimate Avast Antivirus executable that, through DLL sideloading, loaded the wsc.dll library, which is a malicious loader.

Code snippet from the malicious file

Code snippet from the malicious file

The loader opens a file named AvastAuth.dat, which contains an encrypted backdoor. The library reads the data from the file into memory, decrypts it, and executes it. After this, the backdoor attempts to connect to a remote command-and-control server.

The library file, which contains the malicious loader, is on the list of known libraries used for DLL sideloading, as presented on the HijackLibs project website.

Conclusion

Integrating the model into the product provided the means of early and accurate detection of DLL-hijacking attempts which previously might have gone unnoticed. Even during the pilot testing, the model proved its effectiveness by identifying several incidents using this technique. Going forward, its accuracy will only increase as data accumulates and algorithms are updated in KSN, making this mechanism a reliable element of proactive protection for corporate systems.

IoC

Legitimate files used for DLL hijacking
E0E092D4EFC15F25FD9C0923C52C33D6 loads SystemSettings.dll
09CD396C8F4B4989A83ED7A1F33F5503 loads policymanager.dll
A72036F635CECF0DCB1E9C6F49A8FA5B loads wsc.dll

Malicious files
EA2882B05F8C11A285426F90859F23C6   SystemSettings.dll
E83F331BD1EC115524EBFF7043795BBE   policymanager.dll
831252E7FA9BD6FA174715647EBCE516   wsc.dll

Paths
C:\ProgramData\SystemSettings.exe
C:\ProgramData\SystemSettings.dll
C:\Program Files\Chiniks\SettingSyncHost.exe
C:\Program Files\Chiniks\policymanager.dll
D:\RECYCLER.BIN\1\CEFHelper.exe
D:\RECYCLER.BIN\1\wsc.dll

How attackers adapt to built-in macOS protection

29 August 2025 at 06:00

If a system is popular with users, you can bet it’s just as popular with cybercriminals. Although Windows still dominates, second place belongs to macOS. And this makes it a viable target for attackers.

With various built-in protection mechanisms, macOS generally provides a pretty much end-to-end security for the end user. This post looks at how some of them work, with examples of common attack vectors and ways of detecting and thwarting them.

Overview of macOS security mechanisms

Let’s start by outlining the set of security mechanisms in macOS with a brief description of each:

  1. Keychain – default password manager
  2. TCC – application access control
  3. SIP – ensures the integrity of information in directories and processes vulnerable to attacks
  4. File Quarantine – protection against launching suspicious files downloaded from the internet
  5. Gatekeeper – ensures only trusted applications are allowed to run
  6. XProtect – signature-based anti-malware protection in macOS
  7. XProtect Remediator – tool for automatic response to threats detected by XProtect

Keychain

Introduced back in 1999, the password manager for macOS remains a key component in the Apple security framework. It provides centralized and secure storage of all kinds of secrets: from certificates and encryption keys to passwords and credentials. All user accounts and passwords are stored in Keychain by default. Access to the data is protected by a master password.

Keychain files are located in the directories ~/Library/Keychains/, /Library/Keychains/ and /Network/Library/Keychains/. Besides the master password, each of them can be protected with its own key. By default, only owners of the corresponding Keychain copy and administrators have access to these files. In addition, the files are encrypted using the reliable AES-256-GCM algorithm. This guarantees a high level of protection, even in the event of physical access to the system.

However, attacks on the macOS password manager still occur. There are specialized utilities, such as Chainbreaker, designed to extract data from Keychain files. With access to the file itself and its password, Chainbreaker allows an attacker to do a local analysis and full data decryption without being tied to the victim’s device. What’s more, native macOS tools such as the Keychain Access GUI application or the /usr/bin/security command-line utility can be used for malicious purposes if the system is already compromised.

So while the Keychain architecture provides robust protection, it is still vital to control local access, protect the master password, and minimize the risk of data leakage outside the system. Below is an example of a Chainbreaker command:

python -m chainbreaker -pa test_keychain.keychain -o output

As mentioned above, the security utility can be used for command line management, specifically the following commands:

  • security list-keychains – displays all available Keychain files
Keychain files available to the user

Keychain files available to the user

  • security dump-keychain -a -d – dumps all Keychain files
Keychain file dump

Keychain file dump

  • security dump-keychain ~/Library/Keychains/login.keychain-db – dumps a specific Keychain file (a user file is shown as an example)

To detect attacks of this type, you need to configure logging of process startup events. The best way to do this is with the built-in macOS logging tool, ESF. This allows you to collect necessary events for building detection logic. Collection of necessary events using this mechanism is already implemented and configured in Kaspersky Endpoint Detection and Response (KEDR).

Among the events necessary for detecting the described activity are those containing the security dump-keychain and security list-keychains commands, since such activity is not regular for ordinary macOS users. Below is an example of an EDR triggering on a Keychain dump event, as well as an example of a detection rule.

Example of an event from Kaspersky EDR

Example of an event from Kaspersky EDR

Sigma:

title: Keychain access
description: This rule detects dumping of keychain
tags:
    - attack.credential-access
    - attack.t1555.001
logsource:
    category: process_creation
    product: macos
detection:
    selection:
		cmdline: security
		cmdline: 
			-list-keychains
			-dump-keychain
    condition: selection
falsepositives:
    - Unknow
level: medium

SIP

System Integrity Protection (SIP) is one of the most important macOS security mechanisms, which is designed to prevent unauthorized interference in critical system files and processes, even by users with administrative rights. First introduced in OS X 10.11 El Capitan, SIP marked a significant step toward strengthening security by limiting the ability to modify system components, safeguarding against potential malicious influence.

The mechanism protects files and directories by assigning special attributes that block content modification for everyone except trusted system processes, which are inaccessible to users and third-party software. In particular, this makes it difficult to inject malicious components into these files. The following directories are SIP-protected by default:

  • /System
  • /sbin
  • /bin
  • /usr (except /usr/local)
  • /Applications (preinstalled applications)
  • /Library/Application Support/com.apple.TCC

A full list of protected directories is in the configuration file /System/Library/Sandbox/rootless.conf. These are primarily system files and preinstalled applications, but SIP allows adding extra paths.

SIP provides a high level of protection for system components, but if there is physical access to the system or administrator rights are compromised, SIP can be disabled – but only by restarting the system in Recovery Mode and then running the csrutil disable command in the terminal. To check the current status of SIP, use the csrutil status command.

Output of the csrutil status command

Output of the csrutil status command

To detect this activity, you need to monitor the csrutil status command. Attackers often check the SIP status to find available options. Because they deploy csrutil disable in Recovery Mode before any monitoring solutions are loaded, this command is not logged and so there is no point in tracking its execution. Instead, you can set up SIP status monitoring, and if the status changes, send a security alert.

Example of an event from Kaspersky EDR

Example of an event from Kaspersky EDR

Sigma:

title: SIP status discovery
description: This rule detects SIP status discovery
tags:
    - attack.discovery
    - attack.t1518.001
logsource:
    category: process_creation
    product: macos
detection:
    selection:
	cmdline: csrutil status
    condition: selection
falsepositives:
    - Unknow
level: low

TCC

macOS includes the Transparency, Consent and Control (TCC) framework, which ensures transparency of applications by requiring explicit user consent to access sensitive data and system functions. TCC is structured on SQLite databases (TCC.db), located both in shared directories (/Library/Application Support/com.apple.TCC/TCC.db) and in individual user directories (/Users/<username>/Library/Application Support/com.apple.TCC/TCC.db).

Contents of a table in the TCC database

Contents of a table in the TCC database

The integrity of these databases and protection against unauthorized access are implemented using SIP, making it impossible to modify them directly. To interfere with these databases, an attacker must either disable SIP or gain access to a trusted system process. This renders TCC highly resistant to interference and manipulation.

TCC works as follows: whenever an application accesses a sensitive function (camera, microphone, geolocation, Full Disk Access, input control, etc.) for the first time, an interactive window appears with a request for user confirmation. This allows the user to control the extension of privileges.

TCC access permission window

TCC access permission window

A potential vector for bypassing this mechanism is TCC Clickjacking – a technique that superimposes a visually altered window on top of the permissions request window, hiding the true nature of the request. The unsuspecting user clicks the button and grants permissions to malware. Although this technique does not exploit TCC itself, it gives attackers access to sensitive system functions, regardless of the level of protection.

Example of a superimposed window

Example of a superimposed window

Attackers are interested in obtaining Full Disk Access or Accessibility rights, as these permissions grant virtually unlimited access to the system. Therefore, monitoring changes to TCC.db and managing sensitive privileges remain vital tasks for ensuring comprehensive macOS security.

File Quarantine

File Quarantine is a built-in macOS security feature, first introduced in OS X 10.5 Tiger. It improves system security when handling files downloaded from external sources. This mechanism is analogous to the Mark-of-the-Web feature in Windows to warn users of potential danger before running a downloaded file.

Files downloaded through a browser or other application that works with File Quarantine are assigned a special attribute (com.apple.quarantine). When running such a file for the first time, if it has a valid signature and does not arouse any suspicion of Gatekeeper (see below), the user is prompted to confirm the action. This helps prevent running malware by accident.

Example of file attributes that include the quarantine attribute

Example of file attributes that include the quarantine attribute

To get detailed information about the com.apple.quarantine attribute, use the xattr -p com.apple.quarantine <File name> command. The screenshot below shows an example of the output of this command:

  • 0083 – flag for further Gatekeeper actions
  • 689cb865 – timestamp in hexadecimal format (Mac Absolute Time)
  • Safari – browser used to download the file
  • 66EA7FA5-1F9E-4779-A5B5-9CCA2A4A98F5 – UUID attached to this file. This is needed to database a record of the file
Detailed information about the com.apple.quarantine attribute

Detailed information about the com.apple.quarantine attribute

The information returned by this command is stored in a database located at ~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2, where it can be audited.

Data in the com.apple.LaunchServices.QuarantineEventsV2 database

Data in the com.apple.LaunchServices.QuarantineEventsV2 database

To avoid having their files quarantined, attackers use various techniques to bypass File Quarantine. For example, files downloaded via curl, wget or other low-level tools that are not integrated with File Quarantine are not flagged with the quarantine attribute.

Bypassing quarantine using curl

Bypassing quarantine using curl

It is also possible to remove the attribute manually using the xattr -d com.apple.quarantine <filename> command.

Removing the quarantine attribute

Removing the quarantine attribute

If the quarantine attribute is successfully removed, no warning will be displayed when the file is run, which is useful in social engineering attacks or in cases where the attacker prefers to execute malware without the user’s knowledge.

Running a file without a File Quarantine check

Running a file without a File Quarantine check

To detect this activity, you need to monitor execution of the xattr command in conjunction with -d and com.apple.quarantine, which implies removal of the quarantine attribute. In an incident related to macOS compromise, also worth investigating is the origin of the file: if it got onto the host without being flagged by quarantine, this is an additional risk factor. Below is an example of an EDR triggering on a quarantine attribute removal event, as well as an example of a rule for detecting such events.

Example of an event from Kaspersky EDR

Example of an event from Kaspersky EDR

Sigma:

title: Quarantine attribute removal
description: This rule detects removal of the Quarantine attribute, that leads to avoid File Quarantine
tags:
    - attack.defense-evasion
    - attack.t1553.001
logsource:
    category: process_creation
    product: macos
detection:
    selection:
		cmdline: xattr -d com.apple.quarantine
    condition: selection
falsepositives:
    - Unknow
level: high

Gatekeeper

Gatekeeper is a key part of the macOS security system, designed to protect users from running potentially dangerous applications. First introduced in OS X Leopard (2012), Gatekeeper checks the digital signature of applications and, if the quarantine attribute (com.apple.quarantine) is present, restricts the launch of programs unsigned and unapproved by the user, thus reducing the risk of malicious code execution.

The spctl utility is used to manage Gatekeeper. Below is an example of calling spctl to check the validity of a signature and whether it is verified by Apple:

Spctl -a -t exec -vvvv <path to file>

Checking an untrusted file using spctl

Checking an untrusted file using spctl

Checking a trusted file using spctl

Checking a trusted file using spctl

Gatekeeper requires an application to be:

  • either signed with a valid Apple developer certificate,
  • or certified by Apple after source code verification.

If the application fails to meet these requirements, Gatekeeper by default blocks attempts to run it with a double-click. Unblocking is possible, but this requires the user to navigate through the settings. So, to carry out a successful attack, the threat actor has to not only persuade the victim to mark the application as trusted, but also explain to them how to do this. The convoluted procedure to run the software looks suspicious in itself. However, if the launch is done from the context menu (right-click → Open), the user sees a pop-up window allowing them to bypass the block with a single click by confirming their intention to use the application. This quirk is used in social engineering attacks: malware can be accompanied by instructions prompting the user to run the file from the context menu.

Example of Chropex Adware using this technique

Example of Chropex Adware using this technique

Let’s take a look at the method for running programs from the context menu, rather than double-clicking. If we double-click the icon of a program with the quarantine attribute, we get the following window.

Running a program with the quarantine attribute by double-clicking

Running a program with the quarantine attribute by double-clicking

If we run the program from the context menu (right-click → Open), we see the following.

Running a program with the quarantine attribute from the context menu

Running a program with the quarantine attribute from the context menu

Attackers with local access and administrator rights can disable Gatekeeper using the spctl –master disable or --global-disable command.

To detect this activity, you need to monitor execution of the spctl command with parameters –master disable or --global-disable, which disables Gatekeeper. Below is an example of an EDR triggering on a Gatekeeper disable event, as well as an example of a detection rule.

Example of an Kaspersky EDR event

Example of an Kaspersky EDR event

Sigma:

title: Gatekeeper disable
description: This rule detects disabling of Gatekeeper 
tags:
    - attack.defense-evasion
    - attack.t1562.001
logsource:
    category: process_creation
    product: macos
detection:
    selection:
	cmdline: spctl 
	cmdline: 
		- '--master-disable'
		- '--global-disable'
    condition: selection

Takeaways

The built-in macOS protection mechanisms are highly resilient and provide excellent security. That said, as with any mature operating system, attackers continue to adapt and search for ways to bypass even the most reliable protective barriers. In some cases when standard mechanisms are bypassed, it may be difficult to implement additional security measures and stop the attack. Therefore, for total protection against cyberthreats, use advanced solutions from third-party vendors. Our Kaspersky EDR Expert and Kaspersky Endpoint Security detect and block all the threats described in this post. In addition, to guard against bypassing of standard security measures, use the Sigma rules we have provided.

❌
❌