{
	"id": "60838a9b-08cf-4557-ab99-bdab6380cfe1",
	"created_at": "2026-04-06T00:10:59.381093Z",
	"updated_at": "2026-04-10T13:12:28.070529Z",
	"deleted_at": null,
	"sha1_hash": "217d82f6f483a96b10c4e6c670bdcc4a70c940fc",
	"title": "Dark River. You can't see them, but they're there",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1065210,
	"plain_text": "Dark River. You can't see them, but they're there\r\nBy Positive Technologies\r\nPublished: 2024-08-19 · Archived: 2026-04-05 20:27:10 UTC\r\nIntroduction\r\nIn October 2022, during an investigation into an incident at a Russian industrial enterprise, samples of previously unseen\r\nmalware were discovered running on compromised computers of this organization. The names of this malware’s executable\r\nfiles were similar to the names of legitimate software installed on the infected machines, and a number of samples had valid\r\ndigital signatures. Also, the identified executable files and libraries were processed by the Themida protector to make them\r\nmore difficult to detect and analyze.\r\nSubsequent analysis of these samples revealed that the identified software is a fairly complex modular backdoor, which\r\nwe called MataDoor, designed for long-term covert operation in the\r\nInitial infection vector\r\nWe presume that the initial vector for introducing the backdoor into the compromised system was a phishing email with\r\na DOCX document attached. The content of this document was relevant to the field of operations of the targeted enterprise.\r\nThis document contained an exploit for the CVE-2021-40444 vulnerability. The complete attack chain could not\r\nbe reconstructed from this document; however, the correlation between the time the backdoor activity began and the time the\r\nemail was sent strongly suggests that this malicious document was the source of the backdoor.\r\nThis email is not unique. Similar emails containing documents with exploits for the CVE-2021-40444 vulnerability were\r\nsent to Russian defense industry enterprises in August—September 2022. The content of some of them was related to the\r\nactivities of the attacked enterprises, while others were simply designed to attract the attention of the recipient. All the letters\r\nencouraged the user to enable editing in the document, which is a necessary condition for running the exploit.\r\nBelow are examples of the documents related to the enterprise’s activities:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 1 of 55\n\nThe font used in the above documents forces the user to enable editing and change the color to make it more visible. When\r\nediting is enabled, a malicious payload is loaded and executed from an attacker-controlled resource, exploiting the CVE-2021-40444 vulnerability. The actual payload could not be obtained because the servers containing it were inaccessible\r\nat the time of the investigation.\r\nThe documents that simply aimed to attract the recipient’s attention looked like this:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 2 of 55\n\nWhen analyzing the properties of these documents, it was discovered that the URL from which the exploit payload is loaded\r\nis HTML-encoded:\r\nIt should be noted that in the analysis of an array of documents exploiting the CVE-2021-40444 vulnerability, this URL\r\nencoding method was found only in these documents, which suggests that these mailings may have the same origin.\r\nThere are certain similarities between these emails and the phishing emails sent to Russian defense industry enterprises and\r\ndiscovered in September 2021. Information about them has been published by Malwarebytes\r\n(https://www.malwarebytes.com/blog/news/2021/09/mshtml-attack-targets-russian-state-rocket-centre-and-interior-ministry). In this report, there was mention of an email with a DOCX attachment, supposedly sent by the organization’s\r\nHR department with a request to fill in the table provided in the document:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 3 of 55\n\nWhen editing was enabled, the CVE-2021-40444 vulnerability was exploited, followed by the execution of malicious\r\ncontent.\r\nThe September 2021 documents also used HTML encoding for the payload URLs:\r\nThe same method of encoding the payload URL of the malicious documents suggests that the above-mentioned mailing\r\ncampaigns from 2021 and 2022 were carried out by the same actor.\r\nThere are some additional signs that support this assumption:\r\nThe payload in the September 2021 campaigns and some instances of the MataDoor backdoor discovered in 2022\r\nwere signed with Sectigo certificates.\r\nThe domain names of the payload servers for all the mentioned malicious documents are all registered by the\r\nNamecheap registrar. The C2 domain names for MataDoor are also registered by Namecheap.\r\nAll the attacks targeted Russian enterprises in the defense industry.\r\nIt is notable that the elements of the network infrastructure used in attacks on different organizations do not overlap. The\r\ndomains’ registrants’ details are hidden.\r\nSome information about the characteristics of the MataDoor backdoor was published by Kaspersky in the \"Southeast Asia\r\nand Korean Peninsula\" section of the APT Trends Report Q2 2023. In that report, this backdoor, named MATAv5, was\r\nassociated with the Lazarus group’s activity. In our investigation of the network infrastructure used, we were unable\r\nto definitively identify the author of this tool. So we assigned the name Dark River to the group that initiated the attack,\r\nbased on the name River mentioned in the Author field of some of the phishing documents mentioned above.\r\nMataDoor. Description of the backdoor\r\n1. Launching and persistence\r\nThe backdoor is a DLL that, in addition to the standard DllMain function, exports the DllRegisterServer function. This\r\nallows the payload to be run when called through the standard Windows Regsvr32 program normally used for registering\r\nOLE controls in the system registry. In practice, both functions—DllMain and DllRegisterServer—perform the same\r\nactions, initializing the backdoor and running its main execution logic in a separate thread.\r\nHowever, in all known cases, the backdoor is run using an additional component called loader service, which is a Windows\r\nservice that runs automatically when the system starts, ensuring the backdoor’s persistence in the system. The loader service\r\ncomponent does not perform any actions other than launching the DLL with the backdoor, the path of which is defined in its\r\nconfiguration. The loader’s configuration, like the configuration of the backdoor itself, is encrypted using the AES algorithm\r\nin cipher feedback (CFB) mode.\r\nIn all examined samples, the decryption key for the configuration is the sequence 29 23 BE 84 E1 6C D6 AE 52 90\r\n49 F1 BB E9 EB, which represents the sequence of the first 16 bytes generated by the linear congruential generator X[n] =\r\n(0×343fd × X[n-1] + 0×269ec3) mod 0xffffffff with the seed (X[0]) set to 1. This generator corresponds to the\r\nimplementation of the rand() function from the Microsoft C Runtime standard library.\r\nIt’s worth noting that the components of this malware have distinctive compilation names: loaders are named\r\nloader_service_raw_win_intel_64_le_RELEASE.dll, while backdoors are named\r\nMATA_DLL_DLL_PACK_**********_win_intel_64_le_RELEASE.dll, where ********** represents a specific date.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 4 of 55\n\nVariants of backdoors with the following compilation names have been investigated:\r\nMATA_DLL_DLL_PACK_20221013_011_win_intel_64_le_RELEASE.dll,\r\nMATA_DLL_DLL_PACK_20221006_011_win_intel_64_le_RELEASE.dll,\r\nMATA_DLL_DLL_PACK_20221013_011_win_intel_64_le_RELEASE.dll.\r\nGiven the presence of the string \"MATA\" in the compilation name, the backdoor was given the name MataDoor.\r\nAs previously mentioned, when installing this malware, the attackers choose the names of the loader and backdoor\r\nexecutable files separately for each compromised host, mimicking the file names of legitimate programs deployed on it. For\r\nexample, if there is a legitimate executable file named 1cv8.dll in the system, the backdoor file name might be 1cv8u.dll.\r\nIn many cases, the names of the loader and the backdoor are also similar. For example: dlpsvc.dll (loader filename) and\r\ndlpsvcHelper.dll (backdoor filename).\r\nAs mentioned earlier, some backdoor and loader instances were signed with Sectigo certificates:\r\n2. High-level architecture\r\nThe examined sample is a modular backdoor. Architecturally, it consists of a kernel (orchestrator) and modules (plugins).\r\nThe kernel includes the following functionality:\r\nData serialization mechanism using its own custom format\r\nBackdoor configuration management\r\nReceiving and processing commands from the C2\r\nMechanism for performing asynchronous operations\r\nPlugin management\r\nLet’s take a closer look at each of these components.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 5 of 55\n\n3. Plugins\r\nThe plugins (modules) used by the backdoor are divided into two categories:\r\nFunctional plugins that implement commands from the C2 that the backdoor can execute.\r\nTransport (network) plugins that implement network protocols.\r\nPlugins can either be separate PE modules or be embedded—statically linked with the backdoor. During our investigation\r\nwe found no separate plugins and examined only embedded ones.\r\nEach plugin has its own identifier, which is unique within the set of plugins in the same category. That is, the identifiers\r\nof a transport plugin and a functional one may match, but, for example, the identifiers of two different transport plugins\r\nshould not match. The identifier is used for registering the plugin in the kernel and for unloading it. Information about the\r\ncurrently registered plugins is maintained by the orchestrator in the form of two lists: one for network plugins and another\r\nfor functional plugins. Now, let’s examine each of these categories separately.\r\n3.1. Functional plugins\r\nEvery functional plugin, regardless of its form, must be registered in the orchestrator’s context. The orchestrator maintains\r\na list of all such plugins, each element of which has the following structure:\r\n typedef struct\r\n{\r\n int unknown; // This field was not used in the code.\r\n int pluginId; // Plugin ID\r\n void* pluginModulePtr; // Pointer to the loaded PE module of the plugin\r\n UNLOAD_PROC unloadProc; // Pointer to the plugin unload procedure\r\n PluginInfo *next;\r\n} PluginInfo;\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 6 of 55\n\nThe pluginModulePtr field contains a pointer to the loaded PE module of the plugin, analogous to the module handle\r\nreturned by the LoadLibrary call. For embedded plugins, this field is set to 0. In addition to the plugin itself, the orchestrator\r\nmust also register handlers for the C2 commands that this plugin implements. Each command, along with its corresponding\r\nhandler, has a unique identifier within the entire set of commands implemented by the backdoor. The plugin registers the\r\ncommand handlers it implements by calling the corresponding API method provided by the orchestrator. These actions are\r\nperformed during the plugin initialization procedure. The plugin initialization procedure also tells the orchestrator the\r\nplugin’s identifier and the address of the plugin unload procedure. An example of such an initialization procedure is shown\r\nbelow:\r\n int __fastcall Plugins::Bridge::Init(ORCHESTRATOR *ctx, int *moduleId, void *unloadProc)\r\n{\r\n if ( (ctx-\u003eid - 7) \u003e 0x3E0 )\r\n return ERR_PROC_NOT_FOUND;\r\n if ( moduleId )\r\n {\r\n Plugins::Bridge::PluginId = *moduleId;\r\n *moduleId = 5;\r\n }\r\n if ( unloadProc )\r\n *unloadProc = Plugins::Bridge::Unload;\r\n ctx-\u003eRegisterCommandHandler(ctx, 500, Plugins::Bridge::RunBridgedClients);\r\n ctx-\u003eRegisterCommandHandler(ctx, 502, Plugins::Bridge::RunAuthServer);\r\n ctx-\u003eRegisterCommandHandler(ctx, 505, Plugins::Bridge::RunRawServer);\r\n return 0;\r\n}\r\nThis procedure initializes the plugin with the identifier 5, which allows a backdoor instance to act as a relay node for\r\ntransmitting data between other instances of the backdoor. This functionality is implemented through three commands with\r\nidentifiers 500, 502, and 505. We discuss this in more detail in the corresponding section.\r\nIn terms of input, the command handler receives a pointer to the orchestrator’s context, parameters in serialized form (more\r\non serialization in the corresponding section), and the pointer to the variable for recording the command execution status\r\nor error code:\r\nSerializedData *__fastcall CommandHandler(\r\n ORCHESTRATOR *ctx,\r\n SerializedData *parameters,\r\n int *pStatus);\r\nThe handler returns the results of command execution in serialized form. The composition of serialized elements is specific\r\nto each command.\r\nThe plugin unload procedure releases various resources used by the plugin and must also remove the commands\r\nimplemented by the plugin from the list of active commands, which is also done by calling the corresponding orchestrator\r\nAPI function. An example of such a function for the aforementioned module is presented below:\r\nint __fastcall Plugins::Bridge::Unload(ORCHESTRATOR *ctx)\r\n{\r\n ctx-\u003eUnregisterCommandHandler(ctx, 500);\r\n ctx-\u003eUnregisterCommandHandler(ctx, 502);\r\n ctx-\u003eUnregisterCommandHandler(ctx, 505);\r\n return 0;\r\n}\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 7 of 55\n\n3.2. Transport (network) plugins\r\nAll network protocols used by the backdoor are implemented in transport plugins. Each of these plugins also has a unique\r\nidentifier with which it is registered with the orchestrator or unloaded. The orchestrator stores information about the network\r\nplugins in use in a separate list, the elements of which have the following structure:\r\ntypedef struct\r\n{\r\n int moduleId; // Plugin ID\r\n int unknown1; // Always equal to 0.\r\n int factoryId; // Connection factory ID\r\n int unknown2; // Not used.\r\n void* moduleBase; // Pointer to PE module of the plugin\r\n void* unloadProc; // Pointer to the plugin unload procedure\r\n NetworkPluginInfo* next;\r\n} NetworkPluginInfo;\r\nIn addition to the network module identifier itself (moduleId), registration also requires the identifier of an existing\r\nconnection factory registered in the orchestrator, within which this plugin will operate (parameter factoryId). This allows the\r\nfactory to create new connections using the protocol implemented by this transport plugin.\r\nIf the transport plugin is an embedded one, the moduleBase field is set to 0.\r\nThe transport plugin can implement multiple network protocol variants (for example, HTTP and HTTPS). Each\r\nimplemented protocol variant (or transport in the context of this report) must have its own string identifier (for example,\r\n\"tcp\", \"tcp6\"), by which it is specified in the network connection configuration string (see the \"Transport subsystem\r\noverview\" section for more details). The mapping between the string identifier and the procedure for creating the\r\ncorresponding transport is registered in the respective factory using the initialization procedure of the transport plugin\r\nprovided by the backdoor’s API. Below is an example of the initialization procedure for a network plugin that implements\r\nHTTP and HTTPS transports:\r\nint __fastcall HttpTransport::InitProc(\r\n NETWORK_TRANSPORT_FACTORY **factories,\r\n int factoryId,\r\n _DWORD *pStatus,\r\n _QWORD *unloadProc)\r\n{\r\n NETWORK_TRANSPORT_FACTORY *factory; // rbx\r\n factory = *factories;\r\n if ( !*factories )\r\n return ERR_BAD_COMMAND;\r\n while ( factory-\u003eidentifier != factoryId )\r\n {\r\n factory = factory-\u003enext;\r\n if ( !factory )\r\n return ERR_BAD_COMMAND;\r\n }\r\n if ( !HttpTransport::GetTransportDescriptorStatus )\r\n CreatePipeNoBlockingMode(\u0026HttpTransport::ControlPipeRead, \u0026HttpTransport::ControlPipeTransportEndpoint, 0i\r\n HttpTransport::GetTransportDescriptorStatus = factory-\u003eGetTransportDescriptorsStatus;\r\n factory-\u003eRegisterTransportCreationRoutines(factory, aHttp, HttpTransport::HttpTransport);\r\n factory-\u003eRegisterTransportCreationRoutines(factory, aHttps, HttpsTransport::HttpsTransport);\r\n HttpTransport::RegisterProxyTransportFactories(factory);\r\n if ( pStatus )\r\n *pStatus = 1;\r\n if ( unloadProc )\r\n *unloadProc = HttpTransport::Unload;\r\n return 0;\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 8 of 55\n\n}\r\nThe plugin unload procedure removes the transport creation functions from the factory and releases the resources in use.\r\nAn example of such a procedure for the \"http\" network plugin:\r\nint __fastcall HttpTransport::UnregisterFactory(NETWORK_TRANSPORT_FACTORY **factoriesList, int factoryId)\r\n{\r\n NETWORK_TRANSPORT_FACTORY *factory; // rbx\r\n factory = *factoriesList;\r\n if ( !*factoriesList )\r\n return ERR_BAD_COMMAND;\r\n while ( factory-\u003eidentifier != factoryId )\r\n {\r\n factory = factory-\u003enext;\r\n if ( !factory )\r\n return ERR_BAD_COMMAND;\r\n }\r\n factory-\u003eDeleteTransportCreationInfo(factory, \"http\");\r\n factory-\u003eDeleteTransportCreationInfo(factory, \"https\");\r\n factory-\u003eDeleteTransportCreationInfo(factory, \"proxy_http\");\r\n factory-\u003eDeleteTransportCreationInfo(factory, \"proxy_https\");\r\n return HttpTransport::FinalizeProcessingConnections();\r\n}\r\n4. Asynchronous operations\r\nCommands that take a long time to execute are executed asynchronously, in a separate thread. The backdoor stores the\r\nInformation about all current asynchronous operations in a separate global (within the backdoor instance) list. Information\r\nabout an asynchronous operation is added to the list before the operation starts and removed after its completion. This\r\ninformation has the following structure:\r\ntypedef struct\r\n{\r\n int state; // Operation state. 1: in progress, 2: completed\r\n int pluginID; // ID of the functional plugin performing the operation\r\n int unknown1;\r\n int workerThreadId; // ID of the thread performing the operation\r\n HANDLE hWorkerThread; // Handle of the thread performing the operation\r\n long startTime; // Start time of the operation in Unix timestamp format\r\n int unknown2;\r\n int operationId; // Operation identifier\r\n int unknown3;\r\n char* operationDescription; // Not used during execution, presumably description of the operation.\r\n void* asyncOperationRun; // Function that directly performs the operation\r\n void* asyncOperationFinalize; // Function for releasing resources\r\n AsyncOperationBasicContext *next;\r\n} AsyncOperationBasicContext;\r\nEach asynchronous operation has its own identifier operationId, which is not related to the identifier of the command that\r\nperforms the operation. Multiple operations with the same operationId can be executed simultaneously. In the context\r\nof an asynchronous operation, the identifier of the plugin that performs it (pluginID) must be specified to avoid unloading\r\nthe plugin while it is performing the asynchronous operation. In this case, the backdoor waits for the operation to complete\r\nbefore unloading the plugin.\r\n5. Serialization mechanism\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 9 of 55\n\nThe backdoor extensively utilizes its own data serialization system, which is used, for example, to store configurations for\r\nboth the backdoor itself and its network connections, as well as to transmit commands and the results of their execution.\r\nData serialization is carried out through an API provided by the backdoor’s kernel.\r\nA serialized storage unit has the following structure:\r\n[data size: 4 bytes, big-endian] [key: 4 bytes, big-endian] [serialized data]\r\nSerialized storage units can be grouped together into arrays, in which case they are treated as a single entity from the\r\nbackdoor’s perspective.\r\nThe serialization key is a DWORD. The high-order word indicates the type of the serialized data, and the low-order word\r\nserves as the identifier for the storage unit. The same array can contain multiple units with identical keys. In this case, they\r\nare accessed using an additional index.\r\nA total of 14 possible data types are used, ranging from 0×01 to 0×0E. Based on the size of the stored data, these types can\r\nbe categorized as follows:\r\n0×01, 0×06: BYTE\r\n0×02, 0×07: WORD\r\n0×03,0×08, 0×0B: DWORD\r\n0×04, 0×09, 0×0A: QWORD\r\n0×05, 0xC, 0xD, 0xE: byte array\r\nAll fixed-size values (WORD, DWORD, QWORD) are serialized in big-endian format.\r\nThe purpose of some types can be inferred from their usage context:\r\n0×1: not encountered in the analyzed code.\r\n0×2: network connection port number.\r\n0×3: 4-byte storage unit without specific properties.\r\n0×4: offset relative to some value, such as the beginning of a file.\r\n0×5: cryptographic data (public keys, nonce, hash value, and so on).\r\n0×6: not encountered in the analyzed code.\r\n0×7: not encountered in the analyzed code.\r\n0×8: operation/command execution status.\r\n0×9: not encountered in the analyzed code.\r\n0×A: temporary attributes of operating system objects (processes, files) in Unix timestamp format.\r\n0×B: bitmask.\r\n0×C: string.\r\n0×D: array of containers.\r\n0×E: container, with its data area being an array of other serialized units. Containers allow serialized data\r\nto be organized into a hierarchical structure of arbitrary depth.\r\nThe API for working with this format includes the following functions:\r\n// Concatenate one serialized storage unit with another so that they are in a single buffer and follow one ano\r\nSerializedData *__fastcall ConcatItems(SerializedData *dstItem,\r\n SerializedData *srcItem);\r\n \r\n// Add a storage unit to the container.\r\nSerializedData *__fastcall AddNewItemIntoContainer(SerializedData *container,\r\n u_long newItemId,\r\n char *newItemData,\r\n signed int newItemBufLen,\r\n DWORD *status);\r\n// Copy a storage unit.\r\nSerializedData *__fastcall CopyItem(SerializedData *srcItem);\r\n// Get the size of a storage unit (including headers).\r\n__int64 __fastcall GetItemSize(SerializedData *item);\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 10 of 55\n\n// Get the number of storage units with the specified key in a container.\r\n__int64 __fastcall CountItemsInContainer(SerializedData *container,\r\n int itemsKey);\r\n// Get the pointer to the data buffer for the storage unit with the specified key and index.\r\nSerializedData *__fastcall GetItemDataWithDesiredId(SerializedData *items,\r\n int key,\r\n int itemIndex)\r\n// Get a copy of the storage unit with the specified key and index from a container.\r\nint __fastcall GetItemDataCopyFromContainer(SerializedData *items,\r\n unsigned int itemKey,\r\n __int64 index,\r\n _BYTE *dst,\r\n unsigned int dstCapacity);\r\n// Get a pointer to the buffer of the storage unit with the specified key and index from a container.\r\nchar *__fastcall FindItemPtrWithDesiredId(SerializedData *items,\r\n int key,\r\n int itemIndex);\r\n// Create a new container with the specified key.\r\nSerializedData *__fastcall MakeNewContainer(u_long key);\r\n// Delete the specified storage unit from a container.\r\n__int64 __fastcall DeleteItem(SerializedData *items,\r\n int id,\r\n int itemsCount);\r\n// Replace the specified storage unit in a container.\r\nSerializedData *__fastcall ReplaceItem(SerializedData *container,\r\n unsigned int key,\r\n int replacedItemIndex,\r\n char *replacementData,\r\n u_long size,\r\n DWORD *status);\r\n6. General workflow of the backdoor\r\nThe general workflow of the backdoor is as follows:\r\n6.1. Initialization\r\nDuring this stage, the backdoor initializes the orchestrator, registers the built-in network and functional plugins, and\r\nretrieves its parameters from its configuration. The backdoor’s configuration is a separate block in the initialized data\r\nsection, encrypted using the AES algorithm in CFB mode with the key mentioned earlier in the \"Launching and persistence\"\r\nsection. A pointer to the decrypted configuration is then stored in the orchestrator’s context.\r\nAfter decrypting the configuration, the backdoor attempts to load the saved configuration. To do this, it extracts from its\r\ncurrent configuration the serialized (under the 0xC0037 key) name of a registry key in the HKEY_LOCAL_MACHINE\r\nsection where the saved configuration is located. When saving, the current configuration is compressed using the LZ4\r\nalgorithm and then encrypted with the AES-CFB algorithm, using the same key. If there is no saved configuration—for\r\nexample, during the first launch—the backdoor uses the configuration extracted from the data section.\r\nNext, a command (key 0xC002F) is extracted from the configuration and executed in the cmd.exe interpreter. In all\r\nexamined instances of the backdoor, the value of this command in the initial configuration was empty.\r\n6.2. Establishing connection\r\nNext, the backdoor establishes a connection to the C2 based on the network configuration stored in serialized form under the\r\n0xD0033 key. The network configuration may contain several different options for connecting to the C2. In this case, the\r\noption used is selected randomly. In the selected connection configuration, its usage counter (serialization key = 0×30035)\r\nis increased by 1 and saved. Next, a string that describes the connection to the C2 (serialization key = 0xC0034) is extracted\r\nfrom the selected configuration; based on this string, the backdoor establishes a connection to the C2 and then sends\r\nserialized lists of identifiers of all network and functional plugins in use to the C2. The serialization key for the list\r\nof functional plugins is 0xD0006; for the network plugin list it’s 0xD0009. Next, the backdoor starts receiving and\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 11 of 55\n\nprocessing commands. The connection to the C2 uses the agent transport, with authentication of connected clients (see the\r\ndescription of the transport subsystem).\r\n6.3. Command receiving and processing cycle\r\nCommands are transmitted from the C2 to the backdoor in serialized form. The total size of the command is transmitted first\r\n(4 bytes, byte order: big-endian), followed by the command itself. The command code has a size of 4 bytes and is serialized\r\nwith key 0×30005. The data which is obtained during command execution and has to be sent to the C2 is also serialized into\r\nthe container with key 0xE0002. The code of the operation execution status (key = 0×80003) is also added to this container,\r\nand then the result is sent to the C2 and the cycle repeats.\r\nIn case of errors, the connection to the C2 is reset and re-established according to the procedure described above\r\nin Section 6.2.\r\nBelow is an example of an empty command handler from the Processes plugin (see the corresponding section).\r\nSerializedData *__fastcall Processes::EmptyCommand(\r\n ORCHESTRATOR *ctx,\r\n SerializedData *parameters,\r\n int *pStatus)\r\n{\r\n int status;\r\n SerializedData *operationResultContainer;\r\n status = 0;\r\n operationResultContainer = ctx-\u003eMakeNewContainer(OPERATION_RESULT_CONTAINER);\r\n if ( !operationResultContainer )\r\n status = ERR_OUT_OF_MEMORY;\r\n if ( pStatus )\r\n *pStatus = status;\r\n return operationResultContainer;\r\n}\r\nHere is the minimum necessary set of actions that the command handler must perform:\r\n1. Create a container in which the results of command execution will be serialized. In this case, it does not contain any\r\nstorage units.\r\n2. Write the operation execution status or error code to the variable pStatus.\r\n3. Return the container with the command execution results.\r\n7. Deferred commands\r\nThe backdoor has an additional ability to periodically execute a set of additional commands, outside the main cycle of their\r\nreception and processing. Such commands are hardcoded in the backdoor configuration, and their composition can\r\nbe changed only by changing the whole configuration.\r\nA certain time interval is set for the execution of deferred commands, which is specified by the backdoor operator\r\n(commands with ID = 109 and 110 of the Management plugin, see the description below). If this interval is not set (equal\r\nto 0), deferred commands are not executed. Checking for this interval and calling of the deferred command handler occurs\r\nafter execution of a successive command from C2 in the main cycle of command reception and processing.\r\nIf the time interval for deferred commands is not 0, an attempt is made to execute commands. In case of failure, the handler\r\nwaits for some time (three seconds by default), and then attempts to execute the commands again until the time interval\r\nis exhausted or the commands are successfully executed.\r\nIn addition, the operator can set a specific start time for deferred commands (command with ID = 110 of the Management\r\nplugin), which can also be referred to as the \"wake-up time.\" If this time is specified, the deferred command handler waits\r\nuntil the specified wake-up time comes and only then proceeds to execution. Accordingly, the whole cycle of receiving and\r\nprocessing commands from the C2 is suspended for the time of waiting.\r\nA simplified listing of the deferred command handler is shown below:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 12 of 55\n\n__int64 __fastcall ExecuteScheduledTasks(ORCHESTRATOR *this, unsigned int scheduledCmdExecutionTimeInterval)\r\n{\r\n ...\r\n config = this-\u003econfiguration;\r\n commandsExecutionTimeFrame = scheduledCmdExecutionTimeInterval;\r\n sleepIntervalInSeconds = 0;\r\n wakeUpTime = 0i64;\r\n timeWhenExecutionBegun = time64(0i64);\r\n \r\n if ( GetItemDataCopyFromContainer(config, SLEEP_TIME_IN_SECONDS, 0i64, \u0026sleepIntervalInSeconds, 4u) \u003c 0 ||\r\n {\r\n sleepIntervalInSeconds = 3;\r\n }\r\n \r\n if ( GetItemDataCopyFromContainer(config, WAKE_UP_TIME, 0i64, \u0026wakeUpTime, 8u) \u003e 0 \u0026\u0026 wakeUpTime )\r\n {\r\n while ( time64(0i64) \u003c wakeUpTime )\r\n {\r\n Sleep(1000 * sleepIntervalInSeconds);\r\n }\r\n wakeUpTime = 0i64;\r\n ReplaceItem(config, WAKE_UP_TIME, 0, \u0026wakeUpTime, 8u, 0i64);\r\n }\r\n \r\n while ( timeWhenExecutionBegun + commandsExecutionTimeFrame \u003e time64(0i64) )\r\n {\r\n ...\r\n if ( ExecuteScheduledCommands(this) \u003e= 0 )\r\n return 4i64;\r\n Sleep(1000 * sleepIntervalInSeconds);\r\n }\r\n \r\n return 1i64;\r\n}\r\nThe array of deferred commands is serialized in the backdoor configuration with the key 0xD0024. Each element of this\r\narray includes the following values:\r\nSerialization key Data unit description\r\n0x30025 Command identifier\r\n0xD0028 Command parameter array\r\n0x30026 If this parameter is 0, this command is not executed\r\n0x30027 If this parameter equals 1, execution of deferred commands is terminated\r\nThe results of execution of deferred commands are serialized in the array with the 0xD002A key. Each element of the array\r\nincludes the following elements:\r\nSerialization key Data unit description\r\n0x30025 Executed command identifier\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 13 of 55\n\n0x4003B Command execution termination time\r\n0xE0002 Container with the command execution results\r\nThe results of the execution of deferred commands are sent to the C2 by a separate command with ID = 105. Once sent, the\r\ncontainer with the results is released. If the number of entries in this container exceeds 1,000, the old entries are deleted.\r\n8. Descriptions of plugins\r\nIn this section, we will examine the functionality of all the plugins used in the analyzed instances of the backdoor. The\r\nplugin names are based on their functionality and are not found in the code itself.\r\n8.1. Management\r\nLet’s take a look at the Management plugin, which has the identifier pluginId = 1. Its primary functionality is to manage the\r\nbackdoor orchestrator. There are a total of 18 commands here, with the IDs from 100 to 117. The unique feature of these\r\ncommands is that all the data handlers for them are obfuscated using the Themida virtual machine to complicate analysis\r\nof the orchestrator control logic.\r\nCommand with ID = 100 loads and registers the specified plugin. It takes the following parameters:\r\nParameter serialization key Parameter value\r\n0x30007 Plugin identifier\r\n0x50065 Buffer with a PE module that implements the plugin’s functionality\r\n0xC0066 Name of the plugin initialization function (exported by the PE module of the plugin)\r\nThe specified PE module is reflectively loaded into the address space of the backdoor process, after which the specified\r\nplugin initialization function is called. The newly loaded plugin is then registered in the orchestrator under its identifier.\r\nCommand with ID = 101 unloads the specified plugin. It takes the plugin identifier as a parameter (serialization key\r\n= 0×30007). When unloading the plugin, it waits for all asynchronous operations performed by the plugin\r\nto complete.\r\nCommand with ID = 102 loads the specified transport plugin. It takes the following parameters:\r\nParameter serialization key Parameter value\r\n0x30007 Transport plugin identifier\r\n0x30014 Connection factory identifier (see the \"Transport subsystem architecture\" section)\r\n0x50065 Buffer with a PE module that implements the plugin’s functionality\r\n0xC0066 Name of the plugin initialization function (exported by the PE module of the plugin)\r\nSimilarly to the command with ID=100, the specified PE module is reflectively loaded into the address space of the\r\nbackdoor process, and the plugin initialization function is called. The plugin is then registered in the specified connection\r\nfactory under its identifier.\r\nCommand with ID = 103 unloads the specified network plugin, removing it from the specified factory. It takes the\r\nfollowing parameters: network transport factory identifier (0×30014) and plugin identifier (0×30007).\r\nCommand with ID = 104 takes as input a connection factory identifier (serialization key 0×30014) and creates\r\na new (empty) connection factory with the specified identifier.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 14 of 55\n\nCommand with ID = 105 sends the results of scheduled command execution to the C2, along with detailed\r\ninformation about the current machine. The container with the result of the command contains the following entries:\r\nSerialization\r\nkey\r\nStorage unit description\r\n0x4007B\r\nConstant that varies in different samples (for example, 0×846A9F5EA9D10C3C), possibly part\r\nof the build identifier\r\n0x3007A\r\nConstant that varies in different samples (for example, 0×780C2714), possibly part of the build\r\nidentifier\r\n0x30079 Constant that varies in different samples (for example, 0×03), possibly part of the build identifier\r\n0xC0081 Computer name\r\n0xC0082 User name\r\n0xC0083 IP address of the local network adapter (as a string)\r\n0x50085 MAC address of the local network adapter\r\n0x50088 Information about the OS version in the form of the OSVERSIONINFOEXW structure\r\n0x30086 Major OS version number\r\n0x30087 Minor OS version number\r\n0x8003A Difference in seconds between local time and UTC time\r\nCommand with ID = 106 sends the current configuration of the backdoor to the C2.\r\nCommand with ID = 107 updates the backdoor configuration. The new configuration is specified in the command\r\nparameters (serialization key = 0xE001E). During command execution, it tests newly configured network\r\nconnections to the C2. The test results for each network connection variant are sent to the C2, serialized as follows:\r\nSerialization key Storage unit description\r\n0xC0034 Configuration string of the tested connection\r\n0x80003 Result code of the connection attempt using the specified transport\r\nCommand with ID = 108 has an empty handler.\r\nCommand with ID = 109 sets the time interval during which a dedicated function will attempt to execute\r\ndeferred/scheduled commands (parameter key = 0×30064). The function also stops these attempts upon successful\r\nexecution of these commands. The time interval is specified in minutes. The command serializes the value of the set\r\nparameter (key = 0×30064) into the command result container. The specified time interval cannot exceed 180 days.\r\nCommand with ID = 110 sets the time interval for executing deferred commands (similarly to the command with ID\r\n= 109, the parameter key is 0×30064), and also sets the wake-up time in Unix timestamp format (parameter key =\r\n0×40022). The wake-up time is calculated as the sum of start time of the specified command execution and the\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 15 of 55\n\nspecified time interval for executing deferred commands. After the execution of the specified command is completed,\r\nthe cycle of receiving and processing commands from the C2 is suspended until the wake-up time.\r\nCommand with ID = 111 sets the parameter with the key 0×30021 in the backdoor configuration, the purpose\r\nof which is unknown.\r\nCommand with ID = 112 sets the parameter with the key 0×30020 in the backdoor configuration, the purpose\r\nof which is unknown.\r\nCommand with ID = 113 adds new configurations of network transports for communication with the C2, specified\r\nin the command parameters (key = 0xC0034). During this, each of the specified configurations is tested by creating\r\na test transport (transport type AUTH), which attempts to connect to the C2. Test results—a string with the network\r\ntransport configuration (key = 0xC0034) and the result code of the connection attempt using the specified transport\r\n(key = 0×80003)—are sent to the C2 via the connection currently in use.\r\nCommand with ID = 114 obtains the identifiers of the current and parent processes, which it serializes into the\r\ncommand result container with keys 0×3008C and 0×3008D, respectively.\r\nCommand with ID = 115 stops the receiving and processing of commands from the C2 until the next backdoor\r\nlaunch. The constant \"0×1\" is serialized with key 0×3006B into the command result container.\r\nCommand with ID = 116 collects data on all current asynchronous operations, serialized in the following format:\r\nSerialization key Storage unit description\r\n0x30090 Identifier of the thread performing the asynchronous operation\r\n0x3003E Identifier of the asynchronous operation in progress\r\n0xC0091 Description of the asynchronous operation in progress\r\n0x3003D Operation status: started / in progress / completed\r\n0x3003F Identifier of the plugin that performs the operation\r\nThe array of received data for each asynchronous operation is serialized with key 0xD003C into the command result\r\ncontainer.\r\nCommand with ID = 117 has an empty command handler.\r\n8.2. Remote Shell\r\nThis plugin has pluginId = 5 and provides only two functions that implement the remote command line mechanism.\r\nCommand with ID = 200 starts a process with the command line specified in the parameters (serialization key =\r\n0×C00C8). The result of this command’s execution is everything that the running process outputs to stdout and stderr,\r\nserialized with key 0×C0004, as well as the execution status of the operation or the error code that occurred when\r\nstarting the process (key = 0×80003).\r\nCommand with ID = 201 launches a remote command line by executing cmd /c start /b \u003cspecified command\r\nline\u003e and redirecting stdin and stdout to the remote host. If the remote command line process is already running, the\r\ncommand redirects stdin and stdout of that process to the specified remote host. Command parameters:\r\nParameter\r\nserialization key\r\nParameter value\r\n0x300CE ID of the process whose stdin/stdout need to be redirected to the remote host\r\n0xС00СD Command line to start the new process whose stdin/stdout will be redirected to the remote host\r\n0xС00СС\r\nString of the connection configuration to the remote host (more details in the \"Transport\r\nsubsystem architecture\" section)\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 16 of 55\n\n0x300CA\r\nIf this parameter is non-zero, client authentication is required when establishing a connection\r\nto the remote host (see the description of the client and agent transports)\r\nIf the process ID specified in the parameters is 0, a new process with the specified command line is started. Otherwise, the\r\ncommand line is ignored.\r\n8.3. Processes\r\nThis plugin has the identifier pluginId = 7 and is designed to work with processes. It implements the following commands:\r\nCommand with ID = 400 performs the same operations as the command with ID = 401 (see below) but using the\r\nIWbemServices interface. Also, there is a slight difference in the information collected about the running system\r\nprocesses. The command line with which the process was launched (serialization key = 0xC0064) is added to the\r\ninformation specified in the description of the command with ID = 401.\r\nCommand with ID = 401 retrieves information about each running process in the system. The collected array of data\r\nis serialized with key 0xD003C into the container. The number of processes whose information is collected in this\r\ncontainer does not exceed 256. When the specified threshold is reached, the intermediate result is sent to the C2, after\r\nwhich the collection of information continues. If the information collection is completed, and the number\r\nof processes whose information is recorded in the container is less than 256, the container is sent to the C2 in the\r\nusual manner, along with the command execution result (see the \"Command receiving and processing cycle\" section).\r\nThe information collected about each process is as follows:\r\nSerialization key Storage unit description\r\n0x30066 Process ID\r\n0x3006A Parent process ID\r\n0xC006B Name of the executable file of the process in UTF-8 encoding\r\n0x80065 Process session ID\r\n0xA006C Process creation time in Unix timestamp format\r\n0xC006D Domain of the user on whose behalf the process was created\r\n0xC006E User on whose behalf the process was created\r\n0x3006F Processor architecture ID of the current system\r\n0x30070 Bit depth of the current system processor\r\nThe backdoor uses the following processor architecture identifiers:\r\nArchitecture name Processor ID in the backdoor\r\nX86_64 1\r\nARM, ALPHA 2\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 17 of 55\n\nPowerPC 3\r\nMIPS 4\r\nSHX 6\r\nIA32, IA64 7\r\nMicrosoft Intermediate Language 8\r\nNeutral 9\r\nCommand with ID = 402 takes a process ID as a parameter (serialization key = 0×30066) and terminates the\r\nexecution of the process with the specified ID.\r\nCommand with ID = 403 creates a process with the specified command line on behalf of the user who created the\r\nspecified session (or the process with the specified ID). The parameter with key 0×3006A holds the ID of the parent\r\nprocess of the newly created process. The command takes the following parameters:\r\nParameter\r\nserialization key\r\nParameter value\r\n0xC0064 Command line of the process to be run\r\n0x80065 ID of the session in which to run this process\r\n0x30066\r\nProcess ID. The new process will be created on behalf of the user who created the process\r\nwith the specified ID if the session ID is not specified (less than 0)\r\n0x3006A ID of the process that will be specified as the parent of the newly created process\r\nAs the result, the command returns serialized ID of the newly created process (serialization key = 0×30066).\r\nCommand with ID = 404 is empty and does not perform any substantive actions.\r\nCommand with ID = 405 performs an asynchronous operation specified in an external library (DLL). Command\r\nparameters:\r\nParameter serialization\r\nkey\r\nParameter value\r\n0x50069 Buffer with the DLL performing the asynchronous operation\r\n0xC0067\r\nName of the function performing the asynchronous operation. Exported by the specified\r\nDLL.\r\n0xC0064\r\nString parameter passed to the function performing the asynchronous operation (presumably\r\nthe description of the operation)\r\nThe command handler reflectively loads the specified DLL into the memory of the current backdoor process, calling the\r\nentry point (presumably DllMain) with fdwReason = DLL_PROCESS_ATTACH as a parameter. After loading, it performs\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 18 of 55\n\nan asynchronous operation by calling, in a separate thread, the exported DLL function whose name is specified in the\r\ncommand parameters. The code for performing this operation is provided below:\r\nint __fastcall Processes::InvokeExternalAsyncOperation(EXTERNAL_MODULE_ASYNC_OP_CONTEXT *ctx)\r\n{\r\n __int64 opDescription;\r\n void (*externalAsyncOperation)(void);\r\n operationDescription = ctx-\u003eoperationDescription;\r\n externalAsyncOperation = ctx-\u003eexternalAsyncOperation;\r\n if ( opDescription )\r\n (externalAsyncOperation)(0, 0, operationDescription, 0);\r\n else\r\n externalAsyncOperation();\r\n return 0;\r\n}\r\nUpon completion of the asynchronous operation, the DLL is unloaded from memory.\r\n8.4. Filesystem\r\nThis plugin has the identifier pluginId = 9 and implements functions for working with the file system via the following\r\ncommands:\r\nCommand with ID = 300 collects information about the specified file or directory. It takes the following parameters:\r\nParameter\r\nserialization key\r\nParameter value\r\n0xC0064 Path of the specified file or directory\r\n0x30070\r\nMaximum number of files or directories whose information will be included in the final\r\nresult. By default, it is set to 1024\r\n0x3007B Flag regulating the output data format, hereinafter referred to as the format flag\r\n0x3007E Unknown parameter, not used when executing the command\r\nIf the path is not specified, it returns a list of logical disk names with information about their types, obtained through the API\r\ncall GetDriveTypeA(). If the object with the specified path is a file, the command collects and serializes the following data:\r\nSerialization key Storage unit description\r\n0x30067, or 0xB007C if the format\r\nflag is set\r\nFile attributes: whether it is a directory, read/write/execute permissions (the mode\r\nfield of the _stat64 structure in the SYS\\STAT.H header)\r\n0xС006E File owner account name\r\n0xC007D File owner account domain name\r\n0x4006C File size\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 19 of 55\n\n0xA0069 File modification time in Unix timestamp format\r\n0xA0068, included in the result\r\nif the format flag is set\r\nLast file access time in Unix timestamp format\r\n0xA006A, included in the result\r\nif the format flag is set\r\nFile creation time in Unix timestamp format\r\n0xС0066 File name\r\nThe collected information is serialized into the container with key 0xD003C if the format flag is set, or with key\r\n0xD006D otherwise.\r\nIf the specified file system object is a directory, the function obtains the above information for each file or subdirectory\r\nlocated in it (non-recursively). It should be noted that the procedure used to obtain the owner’s account name and file\r\nowner’s domain requires the SeBackupPrivilege privilege.\r\nCommand with ID = 301 can be used to send large files from an infected machine to the C2, as well as to track\r\nchanges in files of interest to the backdoor operator. The command takes the following parameters:\r\nParameter serialization key Parameter value\r\n0xC0064 File path\r\n0x4006B Offset inside the specified file\r\n0x3007A Unknown parameter, not used when executing the command\r\n0x50071 MD5 hash of the part of the file which starts at byte zero and ends at specified offset\r\nThe command is executed as follows:\r\nCalculate the MD5 hash of the file contents from byte zero to the specified offset.\r\nIf the calculated hash matches the specified one, send to the C2 information about the file along with its contents\r\nstarting from the offset specified in the parameters. If the calculated hash differs from the specified one, send to the\r\nC2 the entire file contents along with its metadata.\r\nInformation about the file includes the following serialized data:\r\nSerialization key Storage unit description\r\n0x4006B Current offset inside the file, starting from which the data was read\r\n0x4006С File size\r\n0xA006A File creation time in Unix timestamp format\r\n0xA0069 File modification time in Unix timestamp format\r\n0xA0068 Last file access time in Unix timestamp format\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 20 of 55\n\n0x80003 Status of the current operation (error code or 0 if successful)\r\nCommand with ID = 302 can be used to download large files from the C2 to an infected machine, or to synchronize\r\nchanges between versions of the same file on the C2 and on the infected machine. The command takes the following\r\nparameters:\r\nParameter serialization key Parameter value\r\n0xC0064 File path\r\n0x30076 Size of the buffer that will be used to transfer the file contents\r\n0x30067 Unknown parameter, not used when executing the command\r\nThe command is executed as follows:\r\nSend to the C2 the file size (key = 0×4006B), the MD5 of the file contents (key = 0×50071), and the status of the file\r\nopen operation (key = 0×80003).\r\nReceive from the C2 the offset within the specific file (key = 0×4006B) where the data received from the C2 will\r\nstart overwriting the file contents, and the size of this data (key = 0×40006C).\r\nCommand with ID = 303 can be used to send to the C2 the contents of a specific file or of all files residing directly\r\nin a specific directory (files in subdirectories are not processed). The command can be used when the operator needs\r\nto track changes made to the contents of a specific directory and send only modified or newly created files to the C2.\r\nIt takes the following parameters:\r\nParameter serialization\r\nkey\r\nParameter value\r\n0xC0064 Path of the specific file system object (file or directory)\r\n0x50072 Hash table containing data on the state of files (detailed below)\r\n0xC0077 Mask of the files to be ignored during the command execution (in the format of the WinAPI\r\nfunction PathMatchSpec)\r\nThe structure containing file state data looks as follows:\r\ntypedef struct\r\n{\r\n __int64 key; // Key calculated as the djb2 hash of the file path\r\n __int64 offsetInFile; // Offset within the file\r\n __time64_t modificationTime; // File modification time\r\n int fileContentsMd5[4]; // MD5 of the file contents\r\n} FILE_STATE;\r\nThe hash table of the specified structures describes the files that were previously sent to the C2. The structure fields reflect\r\nthe file state from the last time the file was sent.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 21 of 55\n\nPlease note that when calculating the specified key using the djb2 algorithm, the special characters \" * ? \u003e \u003c : are replaced\r\nby the character _ and the character \\ is replaced by /.\r\nDuring command execution, the following actions are performed for each file from the specified directory:\r\n1. If the file path matches the mask specified in the parameters (key = 0xC0077), the file is ignored.\r\n2. The algorithm specified above is used to calculate the key based on the file path.\r\n3. Based on the calculated key, the hash table is searched for an entry that matches the specific file. If an entry cannot\r\nbe found, the file is subsequently sent in its entirety.\r\n4. If the MD5 hash of the file contents and the modification date match the corresponding fields in the found hash table\r\nentry, the file contents starting with the specified offset are sent. Otherwise, the file is sent to the C2 in its entirety.\r\n5. The following serialized file information is sent to the C2:\r\nSerialization key Storage unit description\r\n0xС0064 Full file path\r\n0x4006C File size\r\n0x4006B Offset to start reading data from the current file\r\n0xA0069 File modification time\r\n0x80003 Error code returned when trying to process the file, or 0 if successful\r\n6. Either the entire contents of the file or the contents starting at the specified offset (based on the conditions of steps 4–\r\n5) are sent to the C2.\r\nPrior to sending the files to the C2, serialized metadata on execution of the current operation is also sent:\r\nSerialization\r\nkey\r\nStorage unit description\r\n0x4006C Total size of the transmitted files\r\n0x80003 Status of the operation (always equal to 0)\r\n0x30074 Number of files whose contents will be transmitted\r\n0x4006B\r\nTotal volume of data that will be transmitted by current command (this volume may be different than\r\nthe cumulative size of files because some files may be transmitted only partially)\r\nThe workflow presented above and the scope of transmitted data are also retained if the command parameter is a file path.\r\nCommand with ID = 304 takes file path (key = 0xС0064) as input. It fills the first 0×10000000 bytes of the specific\r\nfile with zeros if the file size is larger than 0×10000000 bytes. Otherwise, it completely fills the specific file with\r\nzeros.\r\nCommand with ID = 305 takes path of the original file and path of the modified file (key = 0xC0064) as input.\r\nIt copies the creation time, last access time and last write time of the original file and sets them as the corresponding\r\nattributes of the modified file.\r\nCommand with ID = 306 takes directory path (key = 0xC0064) as input. It creates a directory with the specified\r\npath.\r\nCommand with ID = 307 deletes the specified files or directory. It takes the following parameters:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 22 of 55\n\nParameter serialization\r\nkey\r\nParameter value\r\n0xC0064 File path, directory path, or mask in the format of the WinAPI function PathMatchSpec\r\n0x30067\r\nFlag; if 0, the preceding parameter is interpreted as a directory path, otherwise\r\nit is interpreted as a file name mask\r\nIf the object at the specified path is a file, it is simply deleted. If a directory resides at the specified path, the following\r\noccurs:\r\nIf no mask is specified, the function deletes the specified directory together with all its contents.\r\nOtherwise, the files whose paths match the specified mask are recursively deleted from the directory. If the name\r\nof a subdirectory matches the mask, the subdirectory is completely deleted with all its contents.\r\nThe command execution result includes data on the number of files to be deleted and the number of files actually deleted.\r\nThis data is serialized with key 0×30074.\r\nCommand with ID = 308 collects information about the specified directory. It takes a directory name (key =\r\n0xC0064) as a parameter. It returns the total size of files residing in the specified directory, the total number\r\nof subdirectories, and the total number of files in the directory. The command execution result is a string serialized\r\nwith key 0xC0004:\r\n ...\r\n GetDirectoryInfo(dirName, \u0026totalFilesSize, \u0026dirsNum, \u0026filesNum);\r\n sprintf_s(Buffer, 0x1000ui64, \"%lld bytes, %d dirs, %d files\\n\", totalFilesSize, dirsNum, filesNum);\r\n operationResult = ctx-\u003eAddOrReplaceItem(operationResult, OPERATION_RESULT_STRING, 0i64, Buffer, -1, \u0026err)\r\n ...\r\nCommand with ID = 309 copies the specified directory. Command parameters:\r\nParameter serialization\r\nkey\r\nParameter value\r\n0xC0064 Source directory path or mask in the format of the WinAPI function PathMatchSpec\r\n0xC0064 Target directory path\r\n0x30067\r\nFlag; if 0, the first parameter is interpreted as a directory path, otherwise it is interpreted\r\nas a file name mask\r\nIf the source directory path is specified by a mask, the files matching the specified mask are copied. The command\r\nexecution result includes the total number of files to be copied and the total number of files actually copied. This data\r\nis serialized with key 0xC0004.\r\nCommand with ID = 310 moves the specified directory. The parameters, returned results, and workflow are\r\nanalogous to the command with ID = 309.\r\nCommand with ID = 311 takes file path (0xC0064) as input. The command reads the file and sends its contents\r\nto the C2. If the file size does not exceed 4096 bytes, the command serializes the file contents (key = 0xC0004) into\r\nthe command result container. Otherwise, it sends the file contents in parts after serializing them as follows:\r\nSerialization key Storage unit description\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 23 of 55\n\n0xC0004 Next part of the file, with a size of no more than 4096 bytes\r\n0x3000B Data end flag. If set to 0, the current part of the file is the last part\r\nCommand with ID = 312 reads text files and sends them to the C2. The group of commands working with text files\r\nwas likely implemented to send the results of other commands saved in these files to the C2. Command parameters:\r\nParameter serialization key Parameter value\r\n0xC0064 Text file path\r\n0x30081 Number of strings to be read\r\n0x30082 String length\r\nIf the number of strings to be read from the file is not zero, the function reads the specified number of strings from the file\r\nand sends them to С2 after dividing them into buffers of 4096 or less bytes and serializing them similarly to the command\r\nwith ID=311. Otherwise, a string with the length specified in the command parameters but not more than 4096 bytes is read\r\nfrom the specified file and then serialized (key = 0xC0004) into the command result container.\r\nCommand with ID = 313 reads data starting from a specific string in a text file. Command parameters:\r\nParameter\r\nserialization key\r\nParameter value\r\n0xC0064 Text file path\r\n0x30081 Number of strings that must be skipped\r\n0x30082 Initial offset within the file\r\n0x30083\r\nFlag indicating the count direction (0 means that the count starts from the end of the file,\r\notherwise the count starts from the beginning of the file)\r\nThe command reads the contents of the text file beginning with the specified offset in the file and skipping the specified\r\nnumber of strings, and then sends the contents to the C2.\r\nIf the specified count direction flag is not zero, the initial offset is counted from the beginning of the file. In this case, the\r\ncommand then skips the specified number of strings (treating CRLF as line end) and sends the remaining part of the file\r\nto the C2.\r\nIf the specified count direction flag is zero, the initial offset is counted from the end of the file. In this case, the command\r\nskips the specified number of strings, read in the reverse direction from the current position toward the start of the file. Then\r\nthe part of the file spanning from the resultant position to the end of the file is sent to the C2.\r\nThe file contents are serialized and sent to С2 similarly to the commands with IDs 311 and 312.\r\nCommand with ID = 314 implements an asynchronous operation to get a listing of the specified directory. The result\r\nis saved to a text file. Command parameters:\r\nParameter\r\nserialization key\r\nParameter value\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 24 of 55\n\n0xC0064 Directory path\r\n0xC0064 Name of the text file used to save the results\r\n0x30074 Maximum object nesting depth\r\n0x30083\r\nFlag indicating the count direction (0 means that the count starts from the end of the file,\r\notherwise the count starts from the beginning of the file)\r\nFor the specified directory, the command recursively collects the following information and saves it to the specified file:\r\nLast write date (year, month, day, hour, and minute specified as a string in the format YYYYMMDDhhmm)\r\nFile size\r\nFile name\r\nObject nesting depth relative to the original directory\r\nNote that the function used to collect the specified information has the capability to exclude the names of files and\r\ndirectories from being processed. However, this option is not used in this command.\r\nCommand with ID = 315 archives the specified files. It is executed asynchronously. Command parameters:\r\nParameter serialization key Parameter value\r\n0xD006D Array of paths of the files that must be archived\r\n0x30074 Size of the array containing the paths of files, in bytes\r\nThe file paths are specified as an array of consecutive structures each describing a string. Structure format: \u003cpath length, 4\r\nbytes\u003e\u003cfile path\u003e. Files from the specified array are put into a consolidated ZIP archive whose path is defined by the last\r\nelement of this array. The progress of the operation is written to a log file whose name has the following format: \u003cZIP\r\narchive name\u003e.\u003cZIP archive name length in hexadecimal format\u003e When the operation starts, the string \"begin \u003cZIP file\r\nname\u003e\" is written to the log file. When the next file is processed, its name is written to the log file. If the next object from\r\nthe specified array is a directory, it is put into the archive recursively. If a file cannot be archived for some reason, the string\r\n\"skipped \u003cfile name\u003e\" is added to the log file. The command execution result is a string serialized with key 0xC0004: \"Zip\r\n\u003cnumber of directories\u003e Dir(s), \u003cnumber of files\u003e File(s) to \u003cZIP archive name\u003e\".\r\n8.5. Netscan\r\nThis plugin has the identifier pluginId = 4 and provides the capability to get various network environment information.\r\nIt implements a total of 8 commands, which are examined in detail below.\r\nCommand with ID = 2000 checks the availability of the remote host with the specified IP address and port\r\nby attempting to connect to it over the TCP protocol. Parameters:\r\nParameter serialization key Parameter value\r\n0xD006D Remote host IPv4 address\r\n0x30074 Remote host port\r\n0x30067 Local port\r\n0x30069 Connection timeout, in seconds\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 25 of 55\n\nDepending on whether or not a connection was established, the command execution result is either the \"SUCCESS\"\r\nor \"FAILED\" string serialized with key 0xC0004.\r\nCommand with ID = 2001 performs an asynchronous operation that gets the MAC addresses for the specified list\r\nof IPv4 addresses. Command parameters:\r\nParameter serialization key Parameter value\r\n0x30074 Size of the structure containing command parameters\r\n0xD006D Structure containing command parameters\r\nCommand parameters are defined as a separate structure as shown below:\r\ntypedef struct\r\n{\r\n int flags;\r\n unsigned int initialHostIpv4Addr;\r\n int hostsInfoSize;\r\n char outFilePath[1024];\r\n int waitTimeout;\r\n int localPort;\r\n int remotePort;\r\n char hostsInfo[];\r\n} NETSCAN_OPERATION_PARAMETERS;\r\nFor each IPv4 address from the array specified in this structure, the command handler retrieves its corresponding MAC\r\naddress. IP addresses are specified in the hostsInfo field as strings separated by the CR character. In this case, the\r\nDESIRED_HOSTS_AS_STRING_LIST (0x20) flag is set in the flags field.\r\nIf this flag is not set, IP addresses are specified by a range with an initial value (initialHostIpv4Addr) and the number of\r\naddresses (hostsInfoSize) whose information is required. Each successive address is obtained by adding 1 to the preceding\r\naddress. The result is a buffer containing strings that look as follows: IP\\tMAC\\r\\n\u003cIP address\u003e\\t\u003cMAC address\u003e\\r\\n. If the\r\nWRITE_RESULT_TO_FILE (0x10) flag is set in the flags field, this buffer is written to a text file whose path is specified in\r\nthe outFilePath field of the NETSCAN_OPERATION_PARAMETERS structure. If the flag is not set, the buffer is\r\nserialized with key 0xC0004 into the command result container.\r\nLikewise, if the GET_DESIRED_INFO_LOCALLY (0x40) flag is set in the flags field, the operation receives the local IP-to-MAC mapping table available on the host (via the API call GetIpNetTable). Otherwise, the necessary information is\r\nobtained by sending ARP requests.\r\nCommand with ID = 2002 takes the same parameters as the command with ID=2001. It implements\r\nan asynchronous operation that checks the availability of the specified network port (the remotePort field of the\r\nNETSCAN_OPERATION_PARAMETERS structure) on a set of network hosts specified by the list\r\nof IPv4 addresses. The list of IPv4 addresses is specified the same way as in the previous command. This results\r\nin a buffer containing strings that look as follows: IP:PORT\\tSTATUS\\r\\n\u003cIPv4 address\u003e:\r\n\u003cport\u003e\\t\u003cSUCCESS|FAILED\u003e\\r\\n. The result is saved the same way as in the command with ID=2001. The\r\noperating logic is nearly identical for both commands. The only difference is that the\r\nGET_DESIRED_INFO_LOCALLY (0×40) flag is not relevant for this command because the performed operation\r\nalways involves network interaction with specified hosts.\r\nCommand with ID = 2003 takes the same parameters as the command with ID=2001. It implements\r\nan asynchronous operation that collects the following data for each IPv4 address from the list specified in the\r\nparameters:\r\n— Host domain name (if available)\r\n— delay: total time of packet transmission from the local machine to the specified network host and back\r\n(in milliseconds)\r\n— ttl: time to live of a packet sent from the specified network host to the local machine\r\nThe delay and ttl values are obtained by sending an ICMP echo request to the specified network host. The\r\ndelay is calculated as the difference between the time when the ICMP echo request is sent and the time when\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 26 of 55\n\na response is received from the specified network host. The ttl value is taken from the corresponding IP packet\r\nheader of the response to the ICMP echo request.\r\nNote that the ICMP request is built using raw sockets. The contents of this ICMP echo request packet are not\r\ntypical for the standard Windows ping utility because the ICMP packet data generated by this command\r\nis preceded by a timestamp, which is more typical for Linux systems. Aside from the timestamp, the ICMP\r\necho request packet contains only the string !\"#$%\u0026’()*+’-./0123456789:;\u003c=\u003e?. This payload also differs\r\nfrom packets that are generated by the Windows ping utility. The command execution result is a buffer that for\r\neach specified IPv4 address has a string in the following format: \"HOSTNAME\\tIP\\tTTL\\tDELAY\\r\\n\u003chost\r\nname\u003e\\t\u003chost IP address\u003e\\t\u003cdelay\u003e\\t\u003cttl\u003ems\\r\\n\". The operating logic of flags specified in the\r\nNETSCAN_OPERATION_PARAMETERS structure matches the handler of the command with ID=2002.\r\nCommand with ID = 2004 performs an asynchronous operation to collect information about all active TCP\r\nconnections on the local machine. The command takes the serialized structure\r\nNETSCAN_OPERATION_PARAMETRS_SHORT (key = 0xD006D) and the size of the specified structure (key =\r\n0×30074) as a parameter. The NETSCAN_OPERATION_PARAMETRS_SHORT structure is defined as follows:\r\ntypedef struct\r\n{\r\n int flags;\r\n char outFilePath[256];\r\n} NETSCAN_OPERATION_PARAMETERS;\r\nThe collected information includes the following:\r\nLocal host and port\r\nRemote host and port\r\nState of the TCP connection represented by a string from the following list: \"CLOSED\", \"LISTEN\", \"SYN-SENT\", \"SYN-RECEIVED\", \"ESTABLISHED\", \"FIN-WAIT-1\", \"FIN-WAIT-2\", \"CLOSE-WAIT\", \"CLOSING\", \"LAST-ACK\", \"TIME-WAIT\", \"DELETE-TCB\".\r\nPID and name of the executable file of the process that owns the TCP connection.\r\nThe result is a buffer that for each found TCP connection has a string in the following format: TCP\\t\u003clocal host\u003e:\u003clocal\r\nport\u003e\\t\u003cremote host\u003e:\u003cremote port\u003e\\t\u003cconnection state\u003e\\t\u003cPID\u003e \u003cprocess name\u003e\". Similarly to the command with\r\nID=2001, the WRITE_RESULT_TO_FILE (0×10) flag is used to determine whether to save the result to an external file\r\nor to the command result container.\r\nCommand with ID = 2005 — Is not available.\r\nCommand with ID = 2006 implements an asynchronous operation to collect information about specified network\r\nshares. The command takes the structure NETWORK_INFO_ENUM_SMB_SHARES_PARAM (key = 0xD006D)\r\nand the size of this structure (key = 0×30074) as a parameter. The\r\nNETWORK_INFO_ENUM_SMB_SHARES_PARAM structure is defined as follows:\r\ntypedef struct\r\n{\r\n int flags;\r\n int initialHostIpv4Addr;\r\n int hostInfoTotalSize;\r\n char outFileName[1024];\r\n char username[256];\r\n char password[256];\r\n char hostsInfo[];\r\n} NETWORK_INFO_ENUM_SMB_SHARES_PARAM;\r\nNetwork shares are defined as a list of IPv4 addresses in the format used for similar lists in the commands with IDs 2001–\r\n2003.\r\nThe following actions are performed for each IPv4 address from the specified list:\r\n1. Check that port 139 or 445 is open on the target host. If not, the current address is skipped.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 27 of 55\n\n2. If one of the specified ports is open, attempt to connect to the specified network resource over the SMB protocol\r\nby using the username and password from the corresponding fields of the\r\nNETWORK_INFO_ENUM_SMB_SHARES_PARAM structure.\r\n3. If the connection is successful, get a listing of all resources that can be accessed through the specified share. This\r\nlisting is compiled as a list of strings.\r\nThe following text buffer is generated for each share:\r\n-------------------------------------------------------\\r\\n\r\n[+]\\t\u003cremote host name\u003e\\t\u003cremote host IP address\u003e\\t\u003cconnection port\u003e\\tAuth\\t\u003cSUCCESS|FAILED\u003e\\r\\n\r\n\\\\\\\\\u003chost IP address\u003e \"\u003cpassword\u003e\" /u:\"\u003cusername | 'CURRENT_SESSION'\u003e\"\\r\\n\r\n\u003cinformation about the available network resource\u003e\\r\\n\r\n...\r\n\u003cinformation about the available network resource\u003e\\r\\n\r\nThe string containing information about the available network resource has the following format:\r\n\u003c'-' if there are files on the resource, otherwise 'A'\u003e\\t\u003cshare type: 'Print'|'Device'|'Disk'|'IPC'\u003e\\t\u003cnetwork\r\nThe specified information is gathered by calling the WinAPI function NetShareEnum.\r\nThe buffer containing the command execution result is saved similarly to the commands described above, depending\r\non whether the WRITE_RESULT_TO_FILE (0×10) flag is set in the flags field of the\r\nNETWORK_INFO_ENUM_SMB_SHARES_PARAM structure.\r\nCommand with ID = 2007 (0×7D7) receives the serialized structure\r\nNETSCAN_SHARES_MANIPULATION_PARAMS (0xD006D) and its size (0×30074) as input. The structure\r\nis defined as follows:\r\ntypedef struct\r\n{\r\n char localDeviceName[128];\r\n char resourceShareName[128];\r\n char domainName[64];\r\n char userName[64];\r\n char password[64];\r\n int needToDeleteConnection;\r\n int needToEnumConnections;\r\n}\r\nNETSCAN_SHARES_MANIPULATION_PARAMS;\r\nThis command performs the following operations:\r\nIf the needToEnumConnections field is not zero, the command receives a list of all active connections to network\r\nshares on the local machine and then serializes this list in textual form (key = 0xC0004) into the command result\r\ncontainer. The data collection result looks as follows:\r\nSTATUS\\tLOCAL\\tREMOTE\\r\\n\r\n\u003cconnection description string\u003e\r\n...\r\n\u003cconnection description string\u003e\r\nThe connection description string has the following format: \u003cconnection state information:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 28 of 55\n\n'OK'|'Paused'|'Lost'|'Disconnected'|'NetErr'|'Connecting'|'Reconnecting'\u003e\\t\u003cname of the local device (logical\r\nOr if the needToDeleteConnection field is not zero, the command deletes the existing connection to the network share\r\nspecified by the localDeviceName field of the NETSCAN_SHARES_MANIPULATION_PARAMS structure.\r\nOr if both of the above-mentioned parameters are zero, the command creates a connection using resourceShareName\r\nas the name of the shared resource and using the userName and password fields as the access credentials.\r\nCommand with ID = 2008 (0×7D8) performs the same actions as the command with ID = 2003.\r\n8.6. Bridge\r\nThis plugin has the identifier pluginId = 5, which matches the ID of the Remote Shell plugin possibly due to inconsistent\r\nassignment of IDs to plugins. Bridge facilitates various types of proxy connections, serving as a relay node between other\r\nhosts, which normally are backdoor instances. This plugin implements the 3 commands described below.\r\nCommand with ID = 501\r\nThis function creates in a separate thread a client which serves as a relay node redirecting network traffic between two\r\nspecified hosts. This activity can be carried out in two different modes, which we will provisionally designate as Mode 1 and\r\nMode 2 and further examine below. This command accepts the following arguments:\r\nClient authenticity validation flag (the NEED_TO_ENSURE_CLIENT_AUTHENCITY parameter for the\r\nconfiguration of the created connections), serialization key = 0×301F4.\r\nSize of the intermediate buffer used to store transmitted data, serialization key = 0×301F5.\r\nConnection mode: Mode 1 if the parameter value is zero, otherwise Mode 2, serialization key = 0×301FD.\r\nTimeout value for closing the connection if there are no network events, serialization key = 0×30200.\r\nConfiguration string for the primary connection, serialization key = 0xC01F6.\r\nThe general workflow of the relay node (called \"client\" in this context) is as follows:\r\n1. The client connects to the first (primary) host using the configuration string specified in the parameters.\r\n2. Then the client waits for the command to establish a second connection with another (secondary) host. The\r\nconfiguration of the connection with the secondary host is defined directly in the command parameters.\r\n3. After establishing a connection, the primary host and the secondary host exchange data that is relayed by the client.\r\nThis process can be illustrated as follows:\r\nWith the proper server implementation, this functionality lets you set up data exchange between the backdoor and\r\nan operator—for example, following the scheme below by connecting to the auth server through a relay node:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 29 of 55\n\nAlso, with the proper implementation of primary server functionality, this scenario can also be used to build flexible routes\r\nfor transmitting data between the backdoor and an operator, as any backdoor instance with the Bridge module can function\r\nas a relay node and an auth server (this module was enabled by default in all analyzed samples). Please note that we do not\r\nhave access to an actual instance of the primary server, so the example above illustrates only one of the potential use\r\nscenarios for the described functionality.\r\nIn Mode 1, the command connectCommand for establishing a connection with the secondary host has a size of 16 bytes and\r\nmust meet the following conditions:\r\n1. connectCommand[0] == 501\r\n2. connectCommand[1] ^ connectCommand[1] ^ connectCommand[2] == 0\r\nThe command connectCommand is examined here as an array of 4 unsigned DWORDs. After receiving this\r\ncommand, the client creates a new, separate connection with the primary host, from which it receives the connection\r\nconfiguration string for connecting to the secondary host. After connecting, the client sends to the primary host a 4-\r\nbyte status code indicating the progress of this operation. A status code of 0 is sent if the connection was successfully\r\ncreated, otherwise the appropriate error code is sent. Then the primary and secondary hosts proceed to exchange data.\r\nIn contrast to Mode 1, Mode 2 does not create a separate connection for data exchange between the primary and secondary\r\nhosts. Instead, the commands to connect to new hosts and the data sent to already connected secondary hosts are transmitted\r\nover the same control connection. This requires additional headers to be added to data transmitted over the control\r\nconnection to distinguish their recipients.\r\nThe differences between Mode 1 and Mode 2 are illustrated below:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 30 of 55\n\nMode 1\r\nMode 2\r\nThe additional header of the buffer transmitted from the primary server to the client has the following format:\r\ntypedef struct\r\n{\r\n int dataSize; // Size of transmitted data\r\n int connectionID; // Connection identifier\r\n int state; // Connection state\r\n char data[] // Data\r\n} BRIDGED_CONTROL_CONNECTION_DATA_HEADER;\r\nData from the client to the secondary host is then transmitted as is, without this header. When transmitting data within\r\nan already established connection, the value of the header state field is set\r\nto BRIDGED_PACKET_DATA_TRANSMISSION = 0×0. If a data transfer error occurs, the control connection is sent\r\nan empty buffer with the header state field set to BRIDGE_CONNECTION_ERROR = 0×4 and connectionID containing\r\nthe identifier of the corresponding connection.\r\nTo establish a new connection, the primary server sends the client a buffer containing the configuration string of the new\r\nsecondary connection with state = BRIDGED_PACKET_ESTABLISH_NEW_CONNECTION=0×1 and the identifier of the\r\nnew connection (connectionID). The identifier connectionID is generated by the primary server. After successfully\r\nestablishing a secondary connection, the client sends the primary server an empty buffer with state =\r\nBRIDGED_PACKET_CONNECTION_ESTABLISHED (0×2) and the corresponding connectionID. If the connection\r\nis unsuccessful, an empty buffer with state = BRIDGED_PACKET_ERROR is sent.\r\nCommand with ID = 502\r\nThis command implements an auth server (see the description of the client, agent, auth, and control transports) created\r\nin a separate thread. The command takes a serialized string containing the configuration of the server connection\r\n(serialization key = 0xC01FA) as a parameter.\r\nCommand with ID = 505\r\nThis function creates a separate thread in which a proxy server is started. The proxy server implements its own custom\r\nprotocol for establishing a connection with the target host. This proxy server may be called a raw proxy because it can\r\noperate only on the transport layer, specifically with the tcp transport and the udp transport without establishing a connection\r\n(raw mode). Please note that a connection from the client to the proxy can be established using any available transport that\r\nis supported by the backdoor. However, the connection from the proxy to the target host can be established only by using the\r\ntcp and raw udp transports. Therefore, the raw proxy can be viewed as a kind of gateway for exchanging data with other,\r\nexternal, network hosts.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 31 of 55\n\nThis command takes the serialized number of the port that must receive the incoming connection (serialization key =\r\n0×201FE) as a parameter. This raw proxy port listens on all available network interfaces.\r\nThe procedure for establishing a connection is as follows:\r\n1. The client establishes a connection with the raw proxy.\r\n2. The client sends a command to connect to the target host. The command has the following format:\r\ntypedef struct\r\n{\r\n unsigned short unused;\r\n unsigned short dstPort; // Target host port\r\n in_addr dstAddr; // Target host IP address\r\n char configStr[]; // String containing connection configuration elements\r\n} RAW_SERVER_CONNECT_COMMAND;\r\nIf dstAddr is not specified (zero), the name of the target host is taken from the configuration string whose length does not\r\nexceed 512 bytes. The configuration string has the standard format used by the backdoor. However, only the !proto\r\nparameter defining the transport-layer protocol is significant when the string is parsed. If udp is not indicated for this\r\nprotocol, the connection is established over the TCP protocol. If the configuration string begins with the [ character, the IPv6\r\nversion of the corresponding protocol is used. Upon successful connection to the target host, the server sends the code\r\n0×0000000000005A00 to the client and the sides proceed to exchange data. If an error occurs, the code\r\n0×0000000000005B00 is sent.\r\n9. Network subsystem architecture\r\n9.1. Overview of the transport subsystem components\r\nThe transport subsystem implemented by the analyzed backdoor is very advanced. The code that implements various\r\ncomponents of this subsystem makes up around 70% of the total volume of the backdoor code.\r\nThe transport subsystem of the backdoor includes entities known as factories, transport interfaces, transports, and\r\nconnections. Factories are responsible for instantiating transport interfaces and connections. When the factories are created,\r\nthey are registered in the orchestrator context, and each of them has its own unique integer ID. In the analyzed backdoor\r\ninstances, only one factory with the ID 0×01 is used by default.\r\nA transport interface provides all other backdoor components with an interface to do the following:\r\nEstablish outgoing connections (as a client) and receive incoming connections (as a server).\r\nReceive data from a remote host and send data to it.\r\nReceive information about the connection state.\r\nTerminate a connection.\r\nThe main purpose of transport interfaces is to conceal the internal hierarchy of transports and the interaction between\r\ndifferent levels of this hierarchy.\r\nEach transport is a component (class) that implement a specific network protocol. Transports form a hierarchy amongst\r\nthemselves. Each transport may be higher, lower, or on the same level as another transport. A higher-level transport always\r\noperates over a lower-level transport.\r\nThe names of transports are presented below in the same format in which they are encountered in the backdoor code. Please\r\nnote that the network protocol that is actually implemented by a transport with a specific name may differ from the standard\r\nimplementation of the protocol with the same name and may have a completely different functionality and properties as will\r\nbe shown in the section describing the specific transports.\r\nTransports can be divided according to their respective layers of the OSI model:\r\nTransport layer, providing reliable data transfer between network hosts: tcp, udp, pipe.\r\nSession layer, providing continuous exchange of information through a set of data transfers between hosts: ssl, http,\r\nhttps, proxy-http.\r\nApplication layer, implementing high-level logic for establishing connections and transmitting data between\r\nbackdoor instances or between a backdoor and a C2: raw, auth, control, client, agent.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 32 of 55\n\nThis list of transports can be expanded by installing additional network plugins. The transports listed here are embedded into\r\nthe backdoor by default.\r\nThe reference hierarchy presented here is not actually reflected in the backdoor code but nevertheless provides a convenient\r\noverview of the properties of backdoor transports. Despite the fact that named pipes pertain to the session layer of the OSI\r\nmodel and the HTTP and HTTPS protocols pertain to the application layer, the corresponding backdoor transports named\r\npipe, http, https, and proxy-http in this analysis are attributed to other OSI model layers based on their usage context and the\r\nproperties of the protocol that is actually implemented by the specific network transport.\r\nFor example, the pipe transport implemented by the backdoor is used in the code exclusively to ensure reliable data transfer\r\nbetween hosts. Therefore, we attribute this transport to the transport layer of the OSI model within the context of this\r\nanalysis. The http/https transport implements a protocol that is different from standard HTTP/HTTPS. It operates over the\r\nlower-level transports and creates a logical data transfer session that can be resumed if the connection on the transport layer\r\nis disrupted. For this reason, and based on its usage context in the backdoor, this transport is attributed to the session layer\r\nof the OSI model in our analysis.\r\nA transport does not encapsulate its packets into another transport on the same layer, even though this may be technically\r\npossible. Therefore, the tcp transport will not operate over the udp and pipe transports by encapsulating its own packets into\r\nthese protocols. For example, technically the http transport can operate over the ssl transport. However, a separate session-layer https transport is logically created for this configuration in the backdoor.\r\nPlease note that application-layer transports can operate over each other, for example, when a higher-level transport\r\nimplements or supplements the procedure for establishing a connection to another host. In this case, the packet\r\nencapsulation rule mentioned above remains in force.\r\nEach transport can operate as a client or as a server. A transport operates as a server when the method named\r\nListenToConnection is called. This method accepts incoming connection requests from a client.\r\nUnless otherwise specified, for our purposes a connection shall refer to a hierarchy of specific instances of transports that are\r\nused to transfer data between backdoor instances and between a backdoor and a C2.\r\n9.2. Creating transport interfaces and connections. Configuring connections\r\nAs mentioned earlier, transport interfaces and connections are instantiated by a factory. A transport interface can own only\r\none connection at a time. However, a transport interface can close the current connection and open another connection\r\n(instantiated using the factory) based on a previously used or newly created connection configuration.\r\nA connection configuration includes the identifiers of transports that form this connection, and the parameters necessary for\r\ntheir operation (such as the names of hosts, IP addresses, and ports).\r\nThe procedure for creating transports must be registered in the factory. In the context of a factory, each transport is identified\r\nby its name. However, this does not apply to application-layer transports, which are not registered in the factory but instead\r\nare instantiated directly by a transport interface.\r\nThe primary way to define the configuration of connections in a backdoor is to use a specifically formatted string that can\r\nbe described as follows (in extended Backus—Naur form syntax):\r\n \r\nConnectionConfig ::= ConnectionSubConfig {\";\" , ConnectionSubConfig}\r\nConnectionSubConfig ::= [SessionProtocolName , \"://\"], [\"[\"] , HostParameters , ({\"|\", SessionProtocolParamete\r\nHostParameters ::= Path | (HostName, \":\", PortNumber)\r\nSessionProtocolName ::= Identifier\r\nSessionProtocolParameter ::= (\"!\" , ParameterName, \"=\", ParameterValue)\r\nTransportProtocolSpecification ::= (\"!\", \"proto\", \"=\", TransportProtocolName)\r\nTransportProtocolName ::= Idenitifier\r\nParameterName ::= Idenitifier\r\nParameterValue ::= (Idenitifier) | (Number)\r\nHostName ::= Identifier\r\nPortNumber ::= Number\r\nIdentifier ::= (can use all printable ASCII characters except \";\" , \"|\", \"://\", \"!\")\r\nPath ::= (standard path in the Windows file system)\r\n \r\nMore intuitively, the configuration string for a connection with a specified host can be presented as follows:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 33 of 55\n\n\u003cname of the session-layer protocol\u003e\"://\"\u003chost description\u003e\"|!proto=\"\u003cname of the transport-layer protocol\u003e\"|\"\r\n \r\nAt the beginning, before the \"://\" string, the name of the session-layer transport is specified, whose creation procedure must\r\nbe registered in the factory under the corresponding name. For example, this name can be http, https, orssl— see the list\r\nabove.\r\nAfter the \"://\" string, a description of the network host to connect to (or listen to if acting as a server) follows. For example,\r\na URL will be entered for the http transport. If a transport-layer protocol is used for the pipe transport, the name of the pipe\r\nwill be here; for the udp/tcp transports, the host:port pair will be used.\r\nThe !proto parameter defines the name of the transport-layer transport (tcp, udp, or pipe) that the newly created connection\r\nwill operate over. If this parameter is not specified, the tcp transport is selected by default.\r\nThe !type parameter defines the name of the application-layer transport (auth, control, connect, or agent). If it is not\r\nspecified, the transport type is selected based on the operating context of the interface. For example, the agent transport\r\nis used by default in the loop for receiving and processing commands from the C2.\r\nA connection to a specified host may traverse multiple relay nodes. The connection between neighboring hosts in this chain\r\nare also defined by a configuration string. The configuration strings for connections of relay nodes are separated from each\r\nother by the ; character. Therefore, the configuration of a connection to a destination host via multiple relay nodes generally\r\nlooks as follows:\r\n\u003cconnection to relay node 1\u003e;\u003cconnection to relay node 2\u003e; ...\r\n\u003cconnection to relay node N\u003e;\u003cconnection to the destination host\u003e.\r\nSeveral examples of connection configurations are presented below:\r\n1. \"ssl://192.168.1.166:8530|!proto=udp;ssl://igloogawk.com\". This string describes a connection to the C2 server\r\nigloogawk.com via the relay node 192.168.1.166. The relay node configuration\r\n\"ssl://192.168.1.166:8530|!proto=udp\" describes a connection using an ssl session-layer transport operating over\r\na udp transport using network host 192.168.1.166:8530 for a remote connection (this string was extracted from\r\na sample configuration and describes the connection to a C2). The destination host configuration\r\n\"ssl://igloogawk.com\" describes a connection using an ssl transport operating over a tcp transport (as mentioned\r\nearlier, this transport is selected by default when no transport is explicitly specified).\r\n2. \"raw://192.168.1.166:2212|!proto=udp|!udp_type=raw\". This string describes a connection to the server\r\n192.168.1.166:2212 (also used as a C2, or more specifically as a relay node to it). The selected session-layer\r\ntransport is the raw pseudoprotocol, which is not registered in the factory and does not have a separate component\r\n(class) for its implementation. Use of the reserved word raw as the name of the session-layer transport means that this\r\nconnection uses only the transport layer for data transfer. In other words, raw is an empty session-layer protocol. The\r\nlower-level transport here is udp. The configuration includes an additional parameter named udp_type whose raw\r\nvalue indicates that the configuration will use a udp transport version that does not provide support for sessions,\r\nreliable delivery or correctly ordered data assembly. For more details, see the description of the udp transport.\r\n9.3. Specific transports\r\nFunctions of the transport interface have the prototypes presented below:\r\n \r\nvoid CloseConnection(TRANSPORT *this);\r\nStatusCode ConnectToServer(TRANSPORT *this);\r\nStatusCode SendBuf(TRANSPORT *this, const char *buffer, int bufLen);\r\nStatusCode RecvBuf(TRANSPORT *this, const char *buffer, int bufCapacity);\r\nBOOL IsConnectionEstablished(TRANSPORT *this);\r\nStatusCode ListenToConnection(TRANSPORT *this);\r\nStatusCode AcceptConnection(TRANSPORT *this, TRANSPORT **acceptedConnection);\r\nchar * GetPeerNameString(TRANSPORT *this);\r\nu_short GetPeerAddressData(TRANSPORT *this);\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 34 of 55\n\n__int64 GetTransportDescriptor(TRANSPORT *this);\r\nStatusCode SendSessionControlCmd(TRANSPORT *this, int cmd, const char *cmdArgs, unsigned int cmdArgsSize);\r\nStatusCode RecvSessionControlCmd(TRANSPORT *this, int *cmd, const char *cmdArgsBuf, unsigned int cmdArgsBufCap\r\n \r\nThe transport interface includes the standard functionality:\r\nEstablish a connection with a server (ConnectToServer).\r\nReceive incoming connections from clients (ListenToConnection, AcceptConnection).\r\nSend and receive data (SendBuf, RecvBuf).\r\nReceive connection status information, and information about the remote host of the connection\r\n(IsConnectionEstablished, GetPeerNameString, GetPeerAddressData).\r\nThere are uncommon functions as well, such as the following:\r\nGet the transport descriptor (GetTransportDescriptor).\r\nSend and receive session control commands (SendSessionControlCmd, RecvSessionControlCmd).\r\nLet's take a closer look at the uncommon elements of the transport interface. The transport descriptor is a 64-bit integer.\r\nIn particular, for a connection over the pipe transport, this will be the HANDLE of the named channel, and for connections\r\nover the udp and tcp transports, the descriptor of the corresponding socket.\r\nThis abstraction is introduced to provide uniform polling of the connection array for incoming data or readiness to send data.\r\nThis uses an extended version of the int WSAAPI WSAPoll(LPWSAPOLLFD fdArray, ULONG fds, INT timeout) function,\r\nwhich differs from the original through its ability to uniformly poll both sockets and pipes—both anonymous and named—\r\nin the current implementation of the backdoor. The transport system of the backdoor uses only non-blocking sockets and\r\npipes.\r\nThe polling function is registered in a factory. The set of descriptor types to process can thus be extended by creating a new\r\nfactory and registering the appropriate polling function in it.\r\nThe aforementioned functions for sending and receiving session commands (SendSessionControlCmd,\r\nRecvSessionControlCmd) are only used by application-layer transports to exchange service commands when initiating\r\na connection.\r\nTCP\r\nThis transport belongs to the transport layer. It is used by default if the connection configuration does not explicitly specify\r\nthe transport level.\r\nThe transport is implemented on top of IPv4 (configuration name tcp) and IPv6 (configuration name tcp6). The IPv6\r\nprotocol is also used if the configuration string separates the name of the session-layer protocol from the host parameters\r\nwith the character sequence \"://[\", as, for example, in ssl://[igloogawk.com\r\nThe core transport functions essentially constitute a wrapper for the Winsock API, as the entire scope of network operations\r\nis implemented inside that library.\r\nThe configuration may additionally contain bind_ip parameter: the local address with which to associate the socket used\r\nin the transport.\r\nUDP\r\nAccording to the hierarchy provided in the \"Overview of the transport subsystem components\" section, udp belongs to the\r\ntransport layer, which implies reliable transfer of data between hosts.\r\nThis is a case where the transport name, udp, does not reflect the properties of the actual protocol implementation,\r\nsomething we will examine below. Here’s an example of a connection configuration that uses this transport:\r\n \r\nraw://192.168.1.166:2212|!proto=udp|!udp_type=raw\r\n \r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 35 of 55\n\nThe transport in question can function in one of two modes:\r\nRaw UDP, where data is sent inside basic UDP datagrams with no extra logic overlaid. The udp_type=raw parameter\r\nin the connection configuration corresponds to that mode.\r\nConnection-oriented protocol over the UDP that ensures a connection between hosts, reliable delivery of packets and\r\nintegrity of transferred data, which makes it somewhat similar to the TCP. A cursory comparison with analogous\r\nprotocols that work on top of the UDP shows a certain vague similarity with SCTP and RUDP protocols. All in all,\r\nthis is a full-featured custom protocol that provides reliable data transmission between hosts.\r\nFurther description of the UDP transport protocol will assume the connection-oriented mode by default.\r\nThe UDP is logically divided into a connection handler and interfaces, with a one-to-many relationship between these. The\r\nconnection handler is executed in a separate thread, which is created, always in a single copy for each backdoor instance,\r\nas long as there is at least one active connection on top of the UDP transport.\r\nThe connection handler implements all of the protocol logic. Its functions include the following:\r\nPolling transport descriptors for incoming data or readiness to send data.\r\nGenerating and supporting a list of active connections—this is a global list for a backdoor instance.\r\nGenerating outgoing packets and parsing incoming ones.\r\nInitiating and terminating connections.\r\nEnsuring that incoming data is correctly assembled.\r\nData sharing between the transport interface and connection handler relies on anonymous pipes initialized when\r\na connection is set up. Thus, for example, from an interface standpoint, sending data means simply writing it to the\r\nappropriate pipe:\r\n \r\nint __fastcall UdpTransport::SendBuf(UDP_TRANSPORT *this, const char *dataToSend, DWORD dataLen)\r\n{\r\n if ( this-\u003econnectionState == UDP_CONNECTION_ESTABLISHED )\r\n return WritePipe(this-\u003esendPipeInterfaceEndpoint, dataToSend, dataLen);\r\n else\r\n return ERR_NOT_CONNECTED;\r\n}\r\n \r\nThe connection handler, which implements all of the data-sending logic, receives the above data. Data reception is similar\r\nto that: the interface simply reads the data from the appropriate pipe, where it was written by the connection handler after\r\naccepting and processing:\r\n \r\nint __fastcall UdpTransport::RecvBuf(UDP_TRANSPORT *this, char *buf, DWORD bufCapacity)\r\n{\r\n if ( this-\u003econnectionState \u003e= UDP_CONNECTION_ESTABLISHED )\r\n return ReadDataFromPipe(this-\u003erecvPipeTransportEndpoint, buf, bufCapacity);\r\n else\r\n return ERR_NOT_CONNECTED;\r\n}\r\n \r\nThe method above is used both for data being sent or received and service messages about connection status. If the transport\r\nis running in server mode, pointers to newly accepted connections are transmitted in a similar way:\r\n \r\nint __fastcall UdpTransport::AcceptConnection(UDP_TRANSPORT *this, UDP_TRANSPORT **acceptedConnectionPtr)\r\n{\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 36 of 55\n\nint readPipe;\r\n int status;\r\n UDP_TRANSPORT *acceptedConnection;\r\n readPipe = this-\u003erecvPipeInterfaceEndpoint;\r\n acceptedConnectionPtr = 0i64;\r\n status = ReadDataFromPipe(readPipe, \u0026acceptedConnectionPtr, 8u);\r\n if ( !status )\r\n status = ERR_NETWORK_DATA_TRUNCATED;\r\n if ( acceptedConnection )\r\n *acceptedConnectionPtr = acceptedConnection;\r\n return status;\r\n}\r\n \r\nPacket format\r\nA packet consists of a header and data. The header has the following format:\r\n \r\n[data size (2 bytes)][connection identifier (stream id, 4 bytes)][number within the sequence (sequence number\r\n \r\nThe purpose of a packet is defined by flags, of which there are four:\r\n1. UDP_SYN = 1: establishing connection\r\n2. UDP_ACK = 2: confirmation\r\n3. UDP_FIN = 4: connection termination\r\n4. UDP_DEMAND_CHUNK = 8: packet request\r\nWe explain more about how these are used below.\r\nThe connection identifier (stream id) allows the host to tell which connection a packet it received belongs to.\r\nThe sequence number (sequence number) defines the order of assembling packets into one buffer upon receiving.\r\nInitiating and terminating a connection\r\nInitiating a connection is a two-step process:\r\n1. To initiate a connection, the client sends the server a message with a UDP_SYN flag and a client_stream_id in the\r\nmessage packet body.\r\n2. The server responds with a message with UDP_SYN|UDP_ACK flags and a server_stream_id.\r\nThe client_stream_id and server_stream_id each have a size of 4 bytes, and they are generated by the host randomly.\r\nGenerated identifiers are guaranteed not to match the identifiers of other UDP connections within the same backdoor\r\ninstance. The purpose of these identifiers is to define the UDP transport connection that the packet belongs to.\r\nTo terminate the connection, the host sends the recipient a UDP_FIN packet and closes the connection.\r\nData transmission\r\nData is transmitted in packets of up to 1460 bytes including the packet header. This data size was probably selected to ensure\r\nthat a total packet size never exceeded the Ethernet MTU.\r\nA data packet has no flags. Upon receiving a data packet, the host sends the recipient a UDP_ACK packet with the sequence\r\nnumber of the received packet. Received packets are collected into a single buffer according to their sequence numbers and\r\nforwarded to the interface.\r\nLost packets are requested again as follows:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 37 of 55\n\n1. For each connection, the system stores the sequence number of the next packet that must be sent to the interface.\r\nLet's assume that number is N.\r\n2. Suppose the host receives a packet with the sequence number N+m, but the previous packets with numbers N+1, ...\r\nN+m-1 have not been received yet. In that case, the host requests that the missing packets be resent by sending\r\nUDP_DEMAND_CHUNK packets with sequence numbers N+1, ..., N+m-1. This is done for each packet that arrives\r\nout of sequence. Therefore, UDP_DEMAND_CHUNK packets for the same sequence number may be sent multiple\r\ntimes.\r\n3. If the packet thus requested is received, the host sends a UDP_DEMAND_CHUNK|UDP_ACK confirmation with\r\nthe sequence number of the received packet.\r\nConsidering that UDP_DEMAND_CHUNK packets may be sent multiple times, the host waits before resending the\r\nrequested packet. It starts counting the requests and only resends when the number reaches 32, whereupon the counter\r\nis reset.\r\nPIPE\r\nIt belongs to the transport layer according to the hierarchy provided in the \"Overview of the transport subsystem\r\ncomponents\" section. It is registered in a factory under the name pipe. This transport is implemented on top of Windows\r\nnamed channels. Here's an example configuration of a connection that uses this transport:\r\nraw://name_of_the_pipe|!proto=pipe\r\nThis transport wraps around the functions for working with WinAPI named channels.\r\nIn particular, establishing a connection (ConnectToServer) is done with the help of the CreateFileA API; receiving and\r\nprocessing incoming connections (ListenToConnection, AcceptConnection)—with the help of the CreateNamedPipeA and\r\nConnectNamedPipe functions; data receiving and sending—with the help of the ReadFile and WriteFile API calls. Below\r\nis an example of the ListenToConnection function used by this transport:\r\n \r\n__int64 __fastcall PipeTransport::ListenToConnection(PIPE_TRANSPORT *this)\r\n{\r\n __int64 result;\r\n unsigned int v3;\r\n int hDataPipe;\r\n HANDLE EventA;\r\n PIPE_TRANSPORT *i;\r\n this-\u003eisServer = 1;\r\n result = CreatePipeNoBlockingMode(\u0026this-\u003ehControlPipeRead, \u0026this-\u003ehControlPipeWrite, 0i64, 0);\r\n v3 = result;\r\n if ( (int)result \u003c 0 )\r\n return result;\r\n hDataPipe = (unsigned int)CreateNamedPipeA(\r\n this-\u003etransportPipePath,\r\n 0x40080003u,\r\n 0,\r\n 0xFFu,\r\n 0x100000u,\r\n 0x100000u,\r\n 0,\r\n 0i64);\r\n if ( hDataPipe \u003c 0 )\r\n return -GetLastError();\r\n EventA = CreateEventA(0i64, 1, 0, 0i64);\r\n this-\u003epipeOverlapped.hEvent = EventA;\r\n if ( !EventA )\r\n return -GetLastError();\r\n this-\u003ehDataPipeInstance = hDataPipe;\r\n if ( !ConnectNamedPipe(hDataPipe, \u0026this-\u003epipeOverlapped) )\r\n {\r\n result = -GetLastError();\r\n if ( result != -ERROR_IO_PENDING \u0026\u0026 result != -ERROR_PIPE_CONNECTED )\r\n return result;\r\n v3 = 0;\r\n }\r\n if ( PipeTransport::ActiveConnectionsList )\r\n {\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 38 of 55\n\nthis-\u003enext = PipeTransport::ActiveConnectionsList;\r\n for ( i = PipeTransport::ActiveConnectionsList; i-\u003enext; this-\u003enext = i )\r\n i = i-\u003enext;\r\n i-\u003enext = this;\r\n }\r\n else\r\n {\r\n PipeTransport::ActiveConnectionsList = this;\r\n }\r\n this-\u003enext = 0i64;\r\n if ( !PipeTransport::ConnectionsListHead\r\n \u0026\u0026 !CreateThread(0i64, 0i64, (LPTHREAD_START_ROUTINE)PipeTransport::CleanupInactiveConnections, 0i64, 0, 0\r\n {\r\n return -GetLastError();\r\n }\r\n return v3;\r\n}\r\n \r\nIn server mode, all incoming connections created by pipe transports are placed in a single global (within the backdoor\r\ninstance) list of active connections (PipeTransport::ActiveConnectionsList in the above example). A separately created\r\nthread goes through the list every 100 seconds, closing every active connection (PipeTransport::CleanupInactiveConnections\r\nin the above list).\r\nNote that adding connections to and removing from PipeTransport::ActiveConnectionsList is done without any\r\nsynchronization primitives (such as critical sections or mutexes) despite the fact that the list may be accessed by several\r\nthreads.\r\nThis transport probably serves as an option for communication between logically isolated network segments and a C2\r\n(through a relay node) for when transmission of data (commands) via the usual protocols is problematic.\r\nSSL\r\nThis transport belongs to the session layer according to the hierarchy provided in the \"Overview of the transport subsystem\r\ncomponents\" section and implements the SSL protocol. It is registered in a factory as ssl or ssl3.\r\nSimilarly to the TCP transport, it wraps around the functions of the WolfSSL library statically linked to the examined\r\nsample. This library implements the entire scope of operations involved in initiating/terminating connections and\r\nsending/receiving data.\r\nA version of the transport named ssl implements TLS version 1.3, and the ssl3 version implements SSL version 3.\r\nServer certificate is not verified when a connection is initiated.\r\nHTTP (HTTPS)\r\nThis is a session transport in this backdoor's terms. It works on top of the underlying (transport-layer) transport, whose job\r\nis to ensure reliable data transmission between hosts. Note that this transport does not follow the HTTP specification.\r\nThe HTTP transport is implemented in the form of two logical components:\r\n1. Interface\r\n2. Separate thread that sends and receives data\r\nThe interface interacts with the thread through a series of anonymous pipes. For example, when calling the interface method\r\nSendBuf(TRANSPORT *this, char *buf, DWORD bufLen) for sending data, the data to be sent is simply written to the\r\nappropriate pipe, while the thread handles all the work of sending and receiving data across the network.\r\nOnly one copy of the thread is created every time. It processes every existing HTTP transport connection, each of which\r\nis registered globally as part of the backdoor process.\r\nThe main transport parameters (host, url, protocol version, and additional headers) are defined in the transport configuration\r\nstring.\r\nA connection is established as follows:\r\n1. The client sends a POST request to the url specified in the config.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 39 of 55\n\n2. When the server accepts the connection, it sends the response, \"HTTP/ 200″, in contrast to the standard success\r\nresponse, \"HTTP/ 200 OK\". It is worth noting that the protocol uses the Content-Length header in a non-standard\r\nway. Unlike the standard HTTP, where this header contains the message body length (the part following the headers\r\nand double CRLF), in this protocol, Content-Length contains the length of the entire HTTP packet including the\r\nheaders and message body. The body of each of the above requests/responses contains an extra header formatted\r\nas follows:\r\n \r\nstruct HttpChunkHeader\r\n{\r\n char encodeKey; // Key used to encrypt the header\r\n DWORD dataSize; // Size of the data that follows the header\r\n QWORD connectionId; // Connection identifier\r\n char checkSum; // Checksum\r\n}\r\n \r\nThe checkSum is calculated as a byte sum modulo 2 (XOR) of the entire preceding part of the header. After calculating the\r\nchecksum, all the packet headers and data, with the exception of encodeKey, are encoded with the single-byte XOR cipher\r\nusing the encodeKey. No data is sent with these headers, although the format allows it. The purpose of exchanging headers\r\nwhen initiating a connection is to communicate to the client the server-generated identifier of the new connection,\r\nconnectionId.\r\nAfter a connection is established, the hosts exchange data. The data is transmitted unchanged, with no added HTTP headers\r\nor transformations.\r\nThe transport can run in two modes:\r\n1. \"Connection: keep-alive\" leaves the transport connection open after sending/receiving all of the data.\r\n2. \"Connection: close\" closes the connection every time after all of the data is sent/received. The choice of mode\r\ndepends on the headers in the connection requests.\r\nThe protocol provides an option to resume the transport connection if there are transport-layer connection errors. So, if there\r\nwas a client-side transport error, it reconnects to the server, sending the appropriate connectionId of the interrupted\r\nconnection in the request header (HttpChunkHeader).\r\nThe HTTPS transport is the above http transport running on top of the SSL transport.\r\nPROXY-HTTP (PROXY-HTTPS)\r\nThis transport wraps around the HTTP/HTTPS transport, adding a HTTP proxy connection feature that supports\r\nauthentication. The transport supports two authentication methods: Basic and NTLM.\r\nWhen connecting to an HTTP proxy, the client first tries the Basic authentication method. Failing that, it attempts to get\r\nauthenticated with NTLM.\r\n9.4. Application-layer transports\r\nAs mentioned above, protocols in this application layer run on top of the session-layer transports. Unlike the lower-layer\r\nprotocols, the functions for creating these transports are not registered in a factory but instantiated directly by the transport\r\ninterface when creating a new connection. So, the backdoor has a rigidly defined set of application-layer transports that\r\ncannot be extended with a system of network plugins.\r\nThe backdoor sample we are analyzing contains four application-layer transports: auth, control, client, and agent.\r\nIn transport configuration, the application-layer protocol is defined by the value of the !type parameter:\r\nssl://igloogawk.com|!type=auth. Application-layer transport may also be defined using the keyword raw, which signifies that\r\nthe connection uses no protocols belonging to the layer in question and sends raw data.\r\nThe control, client, and agent transports run on top of the auth transport. Let's take a closer look at each.\r\nAUTH\r\nThe main function of this transport is to provide a mechanism for sending session control commands to the application-layer\r\nprotocols above it. To implement this, the auth transport defines the SendSessionControlCmd and RecvSessionControlCmd\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 40 of 55\n\nmethods, and implements a connection procedure of its own by redefining the EstablishConnection method. The backdoor\r\nsamples we studied have no other transports apart from auth that provide session control on the application level.\r\nThis transport uses two connection modes: direct and relay. It implements the following protocol when connecting directly:\r\n1. The client connects to the server on the transport and session levels.\r\n2. The client sends the server a COMMAND_CONNECT command (code = 0×00) without arguments.\r\n3. If the server responds with a COMMAND_CONNECT_RESPONSE command (code = 0×01) without arguments,\r\nthe connection is considered to be established.\r\n4. If the server responds to the client with a COMMAND_RECONNECT command (code = 0×08), the connection\r\nprocedure restarts at Step 1.\r\nHere's a diagram of the process:\r\nIn the above description and the descriptions of the other application-layer transports, all commands are sent/received with\r\nthe help of the SendSessionControlCmd/RecvSessionControlCmd methods.\r\nA connection through a relay node (referred to as \"proxy\" going forward) is established as follows:\r\n1. The client connects to the proxy on the transport and session levels.\r\n2. The client sends the proxy a COMMAND_CONNECT_TO_PEER command (code = 0×02), providing\r\na configuration string for a connection to the next intermediate or final network route point as an argument.\r\n3. The proxy establishes a direct connection to the next point if it's the final point or uses the protocol we are describing\r\nif it's another relay node.\r\n4. Upon connecting to the relay node, the proxy sends the client a COMMAND_CONNECTION_ESTABLISHED\r\ncommand (code = 0×06) without arguments. The connection is then considered to be established, and data\r\ntransmissions begins.\r\nHere's a diagram of this protocol:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 41 of 55\n\nCommands in this transport are sent in the following format:\r\n \r\ntypedef struct\r\n{\r\n char key;\r\n char commandValueDecodingTableId;\r\n char encodedCommand;\r\n int argumentSize;\r\n char headerCheckSum;\r\n char argumentData[];\r\n} COMMAND_PACKET;\r\n \r\nThe argumentSize field is converted to big-endian when the packet is generated. Other header elements are not modified\r\nduring packet generation. Also when a packet is generated, the system calculates a headerCheckSum value as a byte sum\r\nmodulo 2 (XOR) of the commandDecodingTableId, encodedCommand, and argumentSize fields. This sum is checked when\r\nthe command is received.\r\nAfter the checksum is calculated, each byte of the generated packet including its headers and data is XORed with the key\r\nvalue. The key is randomly generated during packet generation. The key is guaranteed to be non-zero.\r\nCommand codes are not transmitted explicitly. They are encoded using a substitution table that must be the same for the\r\nclient, server, and every relay node. encodedCommand is the command value after encoding with the table in question.\r\ncommandValueDecodingTableId is the identifier of the table among all tables of this type in the backdoor. Although there\r\ncan be several substitution tables used by various transports, the samples under review use just one, with the number 1,\r\nshown below:\r\n \r\nchar commandEncodingTable = [0x24, 0x64, 0x13, 0xA4, 0xB2, 0x2C, 0x4F, 0x9F, 0xAF];\r\n \r\nAGENT/CLIENT\r\nThe agent and client transports are used as the main application-layer transport when setting up a connection with the C2.\r\nEstablishing a connection with the help of these transport requires an extra host that we will designate as the auth server.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 42 of 55\n\nThis is not a name used in any of the samples—we introduce it here for convenience. We will designate the hosts connected\r\nby the above transports as Host 1 and Host 2. The agent and client transports are paired transports in the sense that if Host\r\n1 is connected to the auth server through agent, the Host 2 may be connected to the auth server only through the client\r\ntransport, and vice versa.\r\nThe connection that the backdoor instance uses to receive commands to execute has the agent type. The usage context\r\nsuggests that the connection used by the backdoor operator to send commands to the auth server for the backdoor to execute\r\nhas the client type.\r\nA possible interaction pattern is presented in the diagram below:\r\nEach backdoor sample is capable of functioning as an auth server, which enables it to use virtually any running backdoor\r\ninstance (such as previously infected machines on the target network or compromised servers on the Internet) as a C2 as\r\nlong as the operator can connect to the host with the backdoor as the client. A logical connection between the infected host\r\nand the backdoor operator is created, which may be relayed through numerous compromised hosts.\r\nThe auth server accepts a new agent connection on the lower transport levels and establishes a connection via the auth\r\ntransport protocol, with the agent and client running on top of it. This uses the connection option without a relay node (see\r\nthe description of the auth transport). After that, the system searches for a suitable free client connection. If none\r\nis available, the new connection remains in standby. If a suitable client connection is available or appears, the auth server\r\nmediates a session between that and the newly accepted connection. This is followed by transmission of data, which the auth\r\nserver also mediates. The above is true for a new client connection, too. Two connections, auth and client, that are free\r\n(in standby mode) are considered suitable if both have the client authenticity validation flag set or neither does. This\r\nparameter, NEED_TO_ENSURE_CLIENT_AUTHENCITY, is set in the transport configuration (serialization key =\r\n0×30047).\r\nLet's take a closer look at the protocol for connections between the agent and client hosts.\r\nAs mentioned above, the auth server mediates a session between the client and agent hosts, which they use to exchange data.\r\nThe data is encrypted with an RC4 session key that the parties generate while connecting. The parties agree on a session key\r\nusing the elliptic-curve Diffie—Hellman ephemeral (ECDHE) protocol. The encryption helps to hide the contents\r\nof transmitted data from the auth server.\r\nThe session initiation protocols differ depending on the aforementioned NEED_TO_ENSURE_CLIENT_AUTHENCITY\r\n(key = 0x30047) parameter. If this parameter is set, the session initiation procedure gets extra steps to authenticate the\r\ncorrespondents, namely to make sure that the connecting parties are not some outside clients not authorized by the backdoor\r\noperators.\r\nLet's take a closer look at the session initiation protocol from the standpoint of a client that implements the agent transport.\r\n1. The client initiates a connection with the auth server (referred to as the server in this description) on the transport and\r\nsession levels.\r\n2. The client sends the server a COMMAND_CONNECT_AGENT command (code = 0×3) with a connect request\r\nargument, which contains data required for establishing a connection. The connect request contains the following\r\nserialized data:\r\nEphemeral public key to generate a session key (serialization key = 0x50052)\r\nData compression type (serialization key = 0x30051)\r\nClient authenticity validation flag NEED_TO_ENSURE_CLIENT_AUTHENCITY (serialization key =\r\n0x30047)\r\n3. If there is no need to validate authenticity (NEED_TO_ENSURE_CLIENT_AUTHENCITY == 0), the server finds\r\na suitable client, and then sends the agent a COMMAND_CONNECT_CLIENT command (code = 0x4) with\r\na connect response argument. This argument includes the following:\r\nConnect request message received from the client when initiating a session with the auth server . The message\r\ncontents are described in item 2. 1;\r\nString peer info representing the client details (for example, \":\"), serialization key 0xC0048. The client is sent\r\na COMMAND_CONNECT_AGENT command with a connect response argument, which includes the same\r\ntypes of components: a connection request and host details. After exchanging the data, the parties agree on a\r\nsession key, and the connection is considered to be established. Data is exchanged via the server.\r\n4. If the authenticity of the parties needs to be validated (NEED_TO_ENSURE_CLIENT_AUTHENCITY == 1), the\r\nserver sends the agent a COMMAND_RECONNECT, command with a message to sign argument (serialization key\r\n= 0x5004F)—a randomly generated 32-byte array.\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 43 of 55\n\n5. The agent calculates the digital signature of the received message by using a digital signature pattern based on the\r\nEdwards-curve Digital Signature Algorithm (EdDSA). The private key is defined in the transport configuration\r\n(serialization key = 0x50049). Then the agent sends he server another COMMAND_CONNECT_AGENT command\r\nwith a connect request signed argument, which includes the following:\r\nMessage to sign (serialization key = 0x5004F)\r\nIts digital signature (serialization key = 0x50050)\r\nPublic key for validating the signature (serialization key = 0x5004E)\r\nNewly generated ephemeral public key for generating a session key (serialization key = 0x50052)\r\nData compression type (serialization key = 0x30051)\r\nClient authenticity validation flag NEED_TO_ENSURE_CLIENT_AUTHENCITY (serialization key =\r\n0x30047)\r\n6. The server validates the message it received, which must satisfy the following conditions:\r\nThe received message to sign must match the sent message.\r\nThe received public key for message validation (serialization key = 0x5004E) must match the public key\r\ndefined earlier on the server. For the samples under review, this is a hard-coded array: 6E 98 0C 6B 8F 5F 70\r\n5C 27 61 54 05 03 DF 64 C5 FA 28 92 5D 5A 94 6C 21 F7 7F 4F 00 B4 11 E5 A1.\r\nThe digital signature received must be valid. If authentication is successful, a session is initiated as described\r\nin item 3. The initiation and authentication procedure for client connections follows the same steps, except\r\nthat the COMMAND_CONNECT_CLIENT command is sent in place of the\r\nCOMMAND_CONNECT_AGENT.\r\nHere's a diagram of this protocol:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 44 of 55\n\nTransmission of data via connections of this type uses buffers preceded by headers of the following format:\r\n\u003cdata size, 4 bytes\u003e [\u003cmessage number seq_num, 4 bytes\u003e\u003cuncompressed data size, 4 bytes\u003e \u003cthe data itself\u003e]\r\nThe message number, uncompressed data size, and the data itself are encrypted with the RC4 session key generated\r\npreviously as the connection was established. When a party receives a new data buffer, it sends a confirmation in the form\r\nof an empty buffer with the following header:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 45 of 55\n\n\u003cdata size == 4, 4 bytes\u003e [ \u003cmessage number seq_num, 4 bytes\u003e \u003cnumber of the buffer reception of which is conf\r\nEvery field in the packet save for the first one is encrypted with the session key. Note that data whose receipt was not\r\nconfirmed will not be resent. The mechanism is used for recording session data transfer statistics, which will be covered\r\nbelow.\r\nData can be pre-compressed with LZ4 prior to transmission as long as both parties support the algorithm. Available\r\ncompression modes are defined in the transport configuration (COMPRESSION_TYPE, serialization key = 0×30051), with\r\nvalues from 1 through 3. If the compression mode N is set, the party will support every mode from 1 through N. Parties that\r\nexchange data during a session agree on a compression mode with the smallest value available for each of the parties.\r\nIn the sample under review, the COMPRESSION_TYPE set to 1 means that data will be transmitted uncompressed. The\r\nparameter values 2 or 3 correspond to LZ4. The modes 2 and 3 do not differ in any way.\r\nA buffer transmitted during a session will not be compressed in the following cases:\r\n1. The size of a random 256-byte section of the compressed buffer constitutes more than 80% of the uncompressed size.\r\n2. The buffer size is less than 256 bytes.\r\n3. The buffer could not be compressed.\r\nBoth parties to the session record statistics on data they exchange, and the figures thus calculated are serialized in the parties'\r\nconnection configurations. The following are calculated:\r\n1. Compressed data send rate (COMPRESSED_DATA_SEND_RATE, serialization key = 0x30055)\r\n2. Uncompressed data send rate (RAW_DATA_SEND_RATE, serialization key = 0x3004B)\r\n3. Compressed data receive rate (COMPRESSED_DATA_RECV_RATE, serialization key = 0x30056)\r\n4. Uncompressed data receive rate (RAW_DATA_RECV_RATE, serialization key = 0x3004C)\r\nNo functionality we found in the samples under review made use of the above statistics.\r\nCONTROL\r\nThe control transport is an auxiliary transport whose function is to receive information about the status of the auth server.\r\nThe current implementation of the backdoor only supports receiving the number of standby agent connections, separately for\r\nthose with and without authentication.\r\nA client that uses this transport might be a way for the backdoor operator to trace new agent connections from backdoors\r\nand initiating client connections to these.\r\nEach of the samples we studied implements a version of the control transport client, but it does so incompletely, as it only\r\nconnects to the auth server, without sending any commands to it or receiving any information.\r\nThe connection initiation protocol resembles the similar auth and client transport protocols, except that the client is always\r\nauthenticated when a control connection is initiated. The command codes and argument contents are different as well.\r\nA connection is established as follows:\r\n1. The client connects to the server on the lower transport levels.\r\n2. The client sends the server a COMMAND_CONNECT_CONTROL command (code = 0×5) with an argument that\r\ncontains the serialized flag NEED_TO_ENSURE_CLIENT_AUTHENTICITY (0×30047). The meaning of that\r\nparameter in the context of this protocol defines which type of connections we want to receive information about:\r\nauthenticated (flag set to 1) or unauthenticated (flag set to 0).\r\n3. As in the case with the message to sign from the agent transport, upon receiving the command, the server generates\r\na random 32-byte message and sends it to the client in serialized form (key = 0×5004F) as an argument for the\r\nCOMMAND_RECONNECT command (code = 0×8).\r\n4. The client signs the message with an EdDSA signature using a private key stored in the transport configuration and\r\nsends the server another COMMAND_CONNECT_CONTROL command (0×5), with the following serialized data\r\nas arguments:\r\nMessage to sign (serialization key = 0x5004F)\r\nIts digital signature (serialization key = 0x50050)\r\nPublic key for validating the signature (serialization key = 0x5004E)\r\nNewly sent parameter NEED_TO_ENSURE_CLIENT_AUTHENTICITY (serialization key = 0x30047)\r\n5. The server validates the response it receives according to the criteria listed in the descriptions of the client and agent\r\ntransports. The public key sent for validation may match either the client and agent connection validation public key,\r\nor the second possible key:\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 46 of 55\n\nchar pubKey2 = [0xB8, 0x29, 0x7D, 0xF4, 0x02, 0x42, 0x32, 0xEF, 0x60, 0xA3, 0x80, 0x23, 0x91, 0x4F, 0x5D, 0x12\r\n \r\nIf validation fails, the server closes the connection. If validation is successful, the server sends the client\r\na COMMAND_CONNECTION_ESTABLISHED command (code = 0×6), the connection is considered to be established,\r\nand the server proceeds to process commands from the client.\r\nHere's a diagram of this process:\r\nAs mentioned above, the client may send only one command to the server: CONTROL_GET_FREE_AGENTS_COUNT\r\n(code = 0×4): get the number of free agent connections, with or without the authentication requirement, depending on the\r\nvalue of the NEED_TO_ENSURE_CLIENT_AUTHENTICITY parameter specified when the connection was initiated. This\r\ncommand may have an argument—the serialized value in seconds (key = 0×3004A) of the timeout for waiting for new\r\nconnections if there are no free ones. The server sends the result of the command execution as an argument for\r\na COMMAND_CONNECTION_ESTABLISHED command (0×06). In the absence of free connections, the server sends the\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 47 of 55\n\nclient a COMMAND_CONTROL_NO_DATA command (0×07) without arguments. After a response is sent, the command\r\nreceiving and processing cycle repeats.\r\nConclusion\r\nOur analysis suggests that the Dark River group launches one-off attacks on thoroughly selected targets, as evidenced\r\nby targeted email campaigns that consider the nature of the target companies' operations. The attacks are hard to attribute,\r\nas for each target, the group deploys a separate network infrastructure unrelated to any other malicious campaigns.\r\nThe group's main tool, the MataDoor backdoor, has a modular architecture, with a complex, thoroughly designed system\r\nof network transports and flexible options for communication between the backdoor operator and an infected machine. The\r\ncode analysis suggests that the developers invested considerable resources into the tool.\r\nOur study shows that Russian defense industry enterprises remain an object of targeted attacks that utilize increasingly\r\ncomplex and sophisticated tools. The Dark River group typifies these activities aimed at espionage and theft of confidential\r\ninformation.\r\nAuthors: Denis Kuvshinov and Maxim Andreev, with the participation of the incident response and threat intelligence\r\nteams.\r\nApplications\r\nVerdicts by Positive Technologies products\r\nYARA rules\r\nexploit_win_ZZ_CVE202140444__Exploit__MSHTML__RCE__Artifact\r\ntool_win_ZZ_OfficeTemplate__Downloader__Encoding__Artifact\r\napt_win_CN_APT41__Trojan__ExportEngineLoaderString\r\nBehavioral rules\r\nTrojan.Win32.Evasion.a\r\nTrojan.Win32.Generic.a\r\nTrojan.MachineLearning.Generic.a\r\nCreate.Process.NetworkScanner.NetworkScanning\r\nCreate.Process.Reg.RegistryModify\r\nCreate.Process.Regsvr32.RestrictionsBypass\r\nCreate.Process.Rundll32.RestrictionsBypass\r\nCreate.Process.Whoami.Reconnaissance\r\nCreate.Query.WMI.CheckVM\r\nDelete.Process.TerminateProcess.Evasion\r\nRead.NetShare.RPC.Enumeration\r\nRead.Process.Handle.Enumeration\r\nRead.Registry.Key.NetAdapterId\r\nRead.Registry.Key.NetInterfaces\r\nRead.System.Info.Reconnaissance\r\nRead.System.RemoteResources.Enumeration\r\nRead.File.Name.Enumeration\r\nNetwork rules\r\nBACKDOOR [PTsecurity] Matadoor Magic 10009938\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 48 of 55\n\nBACKDOOR [PTsecurity] Matadoor Magic 10009939\r\nBACKDOOR [PTsecurity] Matadoor Magic 10009940\r\nBACKDOOR [PTsecurity] Matadoor CnC 10009941\r\nBACKDOOR [PTsecurity] Matadoor 10009946\r\nBACKDOOR [PTsecurity] Possible Matadoor HTTP in UDP Request 10009947\r\nBACKDOOR [PTsecurity] Possible Matadoor HTTP in UDP Response 10009948\r\nPT Sandbox\r\nBACKDOOR [PTsecurity] Possible Matadoor 10009942\r\nBACKDOOR [PTsecurity] Possible Matadoor HTTP in UDP 10009950\r\nBACKDOOR [PTsecurity] Possible Matadoor 10009951\r\nBACKDOOR [PTsecurity] Possible Matadoor Response 10009952\r\nBACKDOOR [PTsecurity] Possible Matadoor Multiple HTTP Request 10009955\r\nBACKDOOR [PTsecurity] Possible Matadoor Multiple HTTP Response 10009956\r\nBACKDOOR [PTsecurity] Possible Matadoor HTTP in UDP 10009959\r\nBACKDOOR [PTsecurity] Possible Matadoor Multiple HTTP in UDP Request 10009960\r\nMITRE\r\nID Name Description\r\nInitial access\r\nT1566.001\r\nPhishing: Spearphishing\r\nAttachment\r\nThe Dark River group uses phishing email campaigns with\r\nmalicious attachments\r\nExecution\r\nT1059.003\r\nCommand and Scripting\r\nInterpreter: Windows Command\r\nShell\r\nThe Dark River malware features remote command shell\r\nfunctionality\r\nT1106 Native API\r\nThe Dark River malware uses WinAPI functions to run new\r\nprocesses\r\nT1129 Shared Modules\r\nThe Dark River malware can download additional modules\r\nto execute malicious functions\r\nPersistence\r\nT1543.003\r\nCreate or Modify System Process:\r\nWindows Service\r\nThe Dark River group creates malicious services to gain\r\npersistence on a target host\r\nDefense\r\nEvasion\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 49 of 55\n\nT1622 Debugger Evasion\r\nSome samples of the Dark River malware are packed with\r\nthe Themida protection tool, which detects debuggers\r\nT1140\r\nDeobfuscate/Decode Files\r\nor Information\r\nMataDoor executables are obfuscated with a protector\r\nto hamper detection and analysis\r\nT1036.004 Masquerading: Masquerade Task\r\nor Service\r\nMataDoor executable file names imitate the names\r\nof legitimate executables deployed on a target host\r\nT1112 Modify Registry MataDoor stores its configuration in encrypted form inside\r\nthe registry\r\nT1027.002\r\nObfuscated Files or Information:\r\nSoftware Packing\r\nEvery MataDoor sample contains separate functions\r\nshielded by a protector via virtualization\r\nT1620 Reflective Code Loading MataDoor reflectively loads PE modules with plugins into\r\nthe address space of its process\r\nT1218.010\r\nSystem Binary Proxy Execution:\r\nRegsvr32\r\nMataDoor can be started with the help of the Regsvr32\r\nsystem utility\r\nT1218.011\r\nSystem Binary Proxy Execution:\r\nRundll32\r\nMataDoor can be started with the help of the Rundll32\r\nsystem utility\r\nDiscovery\r\nT1083 File and Directory Discovery\r\nMataDoor can collect information about files\r\nand directories on a compromised host\r\nT1135 Network Share Discovery\r\nMataDoor can collect information about shared network\r\nresources in a compromised network\r\nT1046 Network Service Discovery\r\nMataDoor can collect information about hosts with\r\npredefined open ports\r\nT1057 Process Discovery MataDoor can collect information about processes running\r\non an infected host\r\nT1018 Remote System Discovery MataDoor can collect information about the availability\r\nof hosts in a compromised network\r\nT1082 System Information Discovery\r\nMataDoor can collect information about the OS version\r\nand the computer name of a local machine\r\nT1016\r\nSystem Network Configuration\r\nDiscovery\r\nMataDoor can collect information about the IP and MAC\r\naddresses of the local network interface of a compromised\r\nhost\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 50 of 55\n\nT1049\r\nSystem Network Connections\r\nDiscovery\r\nMataDoor can collect information about active TCP\r\nconnections on a compromised host\r\nT1033 System Owner/User Discovery\r\nMataDoor obtains and sends to the C2 the name of the\r\ncurrent user of the compromised host\r\nT1124 System Time Discovery MataDoor can collect information about the system time on\r\nan infected host\r\nCollection\r\nT1560.002\r\nArchive Collected Data: Archive\r\nvia Library\r\nMataDoor can compress collected data with a statically\r\nlinked zlib library\r\nT1074.001 Data Staged: Local Data Staging\r\nMataDoor can save intermediate data collection results\r\nlocally to send to the C2 later\r\nT1005 Data from Local System MataDoor can harvest data from an infected host\r\nCommand\r\nand control\r\nT1071 Application Layer Protocol\r\nMataDoor malware can use application-layer protocols,\r\nsuch as SMB, and an HTTP-like custom protocol\r\nto communicate with the C2\r\nT1132 Data Encoding: Standard Encoding MataDoor can compress the data it sends over networks\r\nwith LZ4\r\nT1572.001\r\nEncrypted Channel: Symmetric\r\ncryptography MataDoor encrypts traffic with RC4\r\nT1572.002\r\nEncrypted Channel: Asymmetric\r\ncryptography\r\nMataDoor contains a statically linked WolfSSL library for\r\nencrypting traffic\r\nT1008 Fallback Channels MataDoor can use alternative configurations to connect to\r\nthe C2\r\nT1095 Non-Application Layer Protocol MataDoor can connect on top of the TCP protocol and via\r\na custom protocol based on UDP datagrams\r\nT1571 Non-Standard Port MataDoor can connect to the C2 on ports that are not\r\ntypical of the protocol being used\r\nT1572 Protocol Tunneling\r\nMataDoor builds a hierarchical system of protocols, where\r\na lower layer encapsulates a higher one, thus tunneling the\r\ntraffic\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 51 of 55\n\nT1090.001 Proxy: Internal Proxy MataDoor can set up a channel to the C2 through a proxy\r\nlocated on a compromised network\r\nT1090.002 Proxy: External Proxy MataDoor can set up a channel to the C2 through a proxy\r\nlocated on an external network\r\nT1090.003 Proxy: Multi-hop Proxy MataDoor can set up a channel to the C2 through a chain of\r\nrelay nodes\r\nExfiltration\r\nT1030 Data Transfer Size Limits\r\nMataDoor can divide data it sends into chunks of variable\r\nsize\r\nT1041 Exfiltration Over C2 Channel MataDoor can send data over an existing C2\r\ncommunication channel\r\nIOCs\r\nFile indicators\r\nLoader service\r\nsha256 sha1 md5\r\n2019322c33b648c9d3f7c8a17a990860044c03ed7bd2fc9e82139c22e9bc5635 3d4c3856f86c1dac1fe644babe87f1e5b6c6636f 1f19f7db272cc5e\r\n207f386ebeb29e64e6b7fd10929217e1a664f06e6cc503e8798f57e0af2e5267 3f8016bafb700595490b732b92f8501201f0c9af 01f3a22bf3e4091\r\n2ba653faef17d9ea623be1138f6f420be27c95d8ad7ee1ea0d15ae718895176d bf8f0b845c8f13b4386b7204add3c5d2e504b4c6 4d1e16e2b91424\r\n748b9e94dc62e1fa364e9daec7d4bbb94a69b304cb81e1a1b6d302be47381a94 9cc89d708fcc2b114f6589d8077f66395d4b68ba fd7de2b8572f35f\r\n9b632505c27fa8ee58f680599fcc0b1794439af17a8c95df9f413e227e34798c 8a3d32cb67bbf600c81577f4c2dd0a5e601c43d4 538505d57722f6\r\nb822db93cde13ee2b2faf41e5a6096782bda7a71ef028641d2ce6ad9db777b67 d3d38d113fcaf3ea2e1b8bc5c32182141f918246 b52439640b7f0e0\r\nc8399484d20c0ebed376cc8147e003cf4d930b5130392ae0e14cee0cec42d484 6da222a04b4d0ad74f7ab186d235b55a9bcf7a18 cc26e5fda0083f7\r\nec70414b2295392cf7200b99747922a5648c4d2882140bd04c7661030aabe928 ae0bf4a92b37da3ca4dbd965bc646a747b7ceaf4 317f1027095bc4\r\nMataDoor\r\nsha256 sha1 md5\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 52 of 55\n\n0085a02b9ba24afd266116df43acbd4f57fc8822af4929e7d17b59f2ceae9418 9320a614916bbfaa31853d785ffe0ed0fc7b54f4 79fc7ed090bc935\r\n3c1cfc2b8b7e5c2d713ec5f329aa58a6b56a08240199761ba6da91e719d30705 87e3e59f6653ae1306461bf9683bda92f442d77f fe93382464347b\r\n566835ce413271ddca8d5014c912dda8ba7e5e45573a7da6ba8e20d72541a2ca 73d6694a0339cc4083f66395b6b4b3da324e2113 6f736eac915c2b6\r\n660bfbeeaf674e4e95c43bb61d7d0aec88154527e1218e51c1cb75d77c8acdda 6251126c3a44d5f8a72f0790ae8aba1b195cb5b2 610303b58eb5d0\r\nec1205a050693f750dd6a984b68eb2533539a34a5602744127d1b729b22f42fd 73055a139a248cccb2b6f4360f072f7626b4ce7c 20ee5ab5724339\r\nfdf50a01a8837c9f4280f3e7f7e336f3cbf93a30c78b48aa50c05b45a7f2ee5b 4a65848af705b2d2b23af0b0795f0ec8bfdc0c69 34e3e94f9955c10\r\nPhishing documents\r\nsha256 sha1 md5\r\n0b06fb7f53bb7963ec2ff89d832b831763706e44d206a4d0a8c813ebee633e22 f463b1cf8d6dd8004edf047b4dea3c4e283f0ffb fcbe52f671d2f20\r\n2e068beb40f8901b698d4fc2f5766564c8324d5ba95fb0a0ffa841f5da5c7e72 178b11323f921c0216bedefdd575a9c5a055b9fa 98e94d7be1d59c\r\n4f544e8756373520e98ed12b921ea7e05a93cf0152405ef3ac65133f7c8660a1 e0f4924aeb8befbf6a78411f910d2c148de7c5ff c587cdbadc3573\r\n84674acffba5101c8ac518019a9afe2a78a675ef3525a44dceddeed8a0092c69 4b35d14a2eab2b3a7e0b40b71955cdd36e06b4b9 41dacae2a33ee71\r\na1797d212560de7fd187d0771e8948bd8e0e242bed0ca07665f78076f4e23235 09413b5d9d404398bc163bfe239e5f8d149ff412 a1fc74b7fb10525\r\nb0a4a1998a1be57d5b9b9ce727d473f46dfc849a3369ee8323d834bebf5ca70a 647497d00704316a7414d357834ed3f7f111a85f bb93392daece23\r\nd00073956786fb8a6b7168b243fa2ea8bb3dff345c020913638ce45c44b78dde 6924b5219448733c43be7f569b1040d468b939f1 0818cda2299b35\r\nNetwork indicators\r\nfetchbring.com\r\ncameoonion.com\r\nkixthstage.com\r\nfledscuba.com\r\ncapetipper.com\r\ncravefool.com\r\ntrendparlye.com\r\nmerudlement.com\r\nipodlasso.com\r\naliveyelp.com\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 53 of 55\n\nbeez.com\r\nbestandgood.com\r\nbettertimator.com\r\nbiowitsg.com\r\ncakeduer.com\r\ncasgone.com\r\ndiemonge.com\r\ne5afaya.com\r\neditngo.com\r\neimvivb.com\r\nendlessutie.com\r\nflowuboy.com\r\nfutureinv-gp.com\r\nganjabuscoa.com\r\ngetmyecoin.com\r\niemcvv.com\r\ninteractive-guides.com\r\ninvestsportss.com\r\nismysoulmate.com\r\njustlikeahummer.com\r\nmetaversalk.com\r\nmlaycld.com\r\nmoveandtry.com\r\nmyballmecg.com\r\nnuttyhumid.com\r\notopitele.com\r\noutsidenursery.com\r\nprimventure.com\r\npursestout.com\r\nreasonsalt.com\r\nsearching4soulmate.com\r\nspeclaurp.com\r\nsureyuare.com\r\ntarzoose.com\r\nwemobiledauk.com\r\nwharfgold.com\r\nxdinzky.com\r\nzeltactib.com\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 54 of 55\n\nSource: https://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nhttps://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/\r\nPage 55 of 55",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://www.ptsecurity.com/ww-en/analytics/pt-esc-threat-intelligence/dark-river-you-can-t-see-them-but-they-re-there/"
	],
	"report_names": [
		"dark-river-you-can-t-see-them-but-they-re-there"
	],
	"threat_actors": [
		{
			"id": "67bf0462-41a3-4da5-b876-187e9ef7c375",
			"created_at": "2022-10-25T16:07:23.44832Z",
			"updated_at": "2026-04-10T02:00:04.607111Z",
			"deleted_at": null,
			"main_name": "Careto",
			"aliases": [
				"Careto",
				"The Mask",
				"Ugly Face"
			],
			"source_name": "ETDA:Careto",
			"tools": [
				"Careto"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "34eea331-d052-4096-ae03-a22f1d090bd4",
			"created_at": "2025-08-07T02:03:25.073494Z",
			"updated_at": "2026-04-10T02:00:03.709243Z",
			"deleted_at": null,
			"main_name": "NICKEL ACADEMY",
			"aliases": [
				"ATK3 ",
				"Black Artemis ",
				"COVELLITE ",
				"CTG-2460 ",
				"Citrine Sleet ",
				"Diamond Sleet ",
				"Guardians of Peace",
				"HIDDEN COBRA ",
				"High Anonymous",
				"Labyrinth Chollima ",
				"Lazarus Group ",
				"NNPT Group",
				"New Romanic Cyber Army Team",
				"Temp.Hermit ",
				"UNC577 ",
				"Who Am I?",
				"Whois Team",
				"ZINC "
			],
			"source_name": "Secureworks:NICKEL ACADEMY",
			"tools": [
				"Destover",
				"KorHigh",
				"Volgmer"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "4d5f939b-aea9-4a0e-8bff-003079a261ea",
			"created_at": "2023-01-06T13:46:39.04841Z",
			"updated_at": "2026-04-10T02:00:03.196806Z",
			"deleted_at": null,
			"main_name": "APT41",
			"aliases": [
				"WICKED PANDA",
				"BRONZE EXPORT",
				"Brass Typhoon",
				"TG-2633",
				"Leopard Typhoon",
				"G0096",
				"Grayfly",
				"BARIUM",
				"BRONZE ATLAS",
				"Red Kelpie",
				"G0044",
				"Earth Baku",
				"TA415",
				"WICKED SPIDER",
				"HOODOO",
				"Winnti",
				"Double Dragon"
			],
			"source_name": "MISPGALAXY:APT41",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "e698860d-57e8-4780-b7c3-41e5a8314ec0",
			"created_at": "2022-10-25T15:50:23.287929Z",
			"updated_at": "2026-04-10T02:00:05.329769Z",
			"deleted_at": null,
			"main_name": "APT41",
			"aliases": [
				"APT41",
				"Wicked Panda",
				"Brass Typhoon",
				"BARIUM"
			],
			"source_name": "MITRE:APT41",
			"tools": [
				"ASPXSpy",
				"BITSAdmin",
				"PlugX",
				"Impacket",
				"gh0st RAT",
				"netstat",
				"PowerSploit",
				"ZxShell",
				"KEYPLUG",
				"LightSpy",
				"ipconfig",
				"sqlmap",
				"China Chopper",
				"ShadowPad",
				"MESSAGETAP",
				"Mimikatz",
				"certutil",
				"njRAT",
				"Cobalt Strike",
				"pwdump",
				"BLACKCOFFEE",
				"MOPSLED",
				"ROCKBOOT",
				"dsquery",
				"Winnti for Linux",
				"DUSTTRAP",
				"Derusbi",
				"ftp"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "732597b1-40a8-474c-88cc-eb8a421c29f1",
			"created_at": "2025-08-07T02:03:25.087732Z",
			"updated_at": "2026-04-10T02:00:03.776007Z",
			"deleted_at": null,
			"main_name": "NICKEL GLADSTONE",
			"aliases": [
				"APT38 ",
				"ATK 117 ",
				"Alluring Pisces ",
				"Black Alicanto ",
				"Bluenoroff ",
				"CTG-6459 ",
				"Citrine Sleet ",
				"HIDDEN COBRA ",
				"Lazarus Group",
				"Sapphire Sleet ",
				"Selective Pisces ",
				"Stardust Chollima ",
				"T-APT-15 ",
				"TA444 ",
				"TAG-71 "
			],
			"source_name": "Secureworks:NICKEL GLADSTONE",
			"tools": [
				"AlphaNC",
				"Bankshot",
				"CCGC_Proxy",
				"Ratankba",
				"RustBucket",
				"SUGARLOADER",
				"SwiftLoader",
				"Wcry"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "f5bf6853-3f6e-452c-a7b7-8f81c9a27476",
			"created_at": "2023-01-06T13:46:38.677391Z",
			"updated_at": "2026-04-10T02:00:03.064818Z",
			"deleted_at": null,
			"main_name": "Careto",
			"aliases": [
				"The Mask",
				"Ugly Face"
			],
			"source_name": "MISPGALAXY:Careto",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "2a24d664-6a72-4b4c-9f54-1553b64c453c",
			"created_at": "2025-08-07T02:03:24.553048Z",
			"updated_at": "2026-04-10T02:00:03.787296Z",
			"deleted_at": null,
			"main_name": "BRONZE ATLAS",
			"aliases": [
				"APT41 ",
				"BARIUM ",
				"Blackfly ",
				"Brass Typhoon",
				"CTG-2633",
				"Earth Baku ",
				"GREF",
				"Group 72 ",
				"Red Kelpie ",
				"TA415 ",
				"TG-2633 ",
				"Wicked Panda ",
				"Winnti"
			],
			"source_name": "Secureworks:BRONZE ATLAS",
			"tools": [
				"Acehash",
				"CCleaner v5.33 backdoor",
				"ChinaChopper",
				"Cobalt Strike",
				"DUSTPAN",
				"Dicey MSDN",
				"Dodgebox",
				"ForkPlayground",
				"HUC Proxy Malware (Htran)"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "a2b92056-9378-4749-926b-7e10c4500dac",
			"created_at": "2023-01-06T13:46:38.430595Z",
			"updated_at": "2026-04-10T02:00:02.971571Z",
			"deleted_at": null,
			"main_name": "Lazarus Group",
			"aliases": [
				"Operation DarkSeoul",
				"Bureau 121",
				"Group 77",
				"APT38",
				"NICKEL GLADSTONE",
				"G0082",
				"COPERNICIUM",
				"Moonstone Sleet",
				"Operation GhostSecret",
				"APT 38",
				"Appleworm",
				"Unit 121",
				"ATK3",
				"G0032",
				"ATK117",
				"NewRomanic Cyber Army Team",
				"Nickel Academy",
				"Sapphire Sleet",
				"Lazarus group",
				"Hastati Group",
				"Subgroup: Bluenoroff",
				"Operation Troy",
				"Black Artemis",
				"Dark Seoul",
				"Andariel",
				"Labyrinth Chollima",
				"Operation AppleJeus",
				"COVELLITE",
				"Citrine Sleet",
				"DEV-0139",
				"DEV-1222",
				"Hidden Cobra",
				"Bluenoroff",
				"Stardust Chollima",
				"Whois Hacking Team",
				"Diamond Sleet",
				"TA404",
				"BeagleBoyz",
				"APT-C-26"
			],
			"source_name": "MISPGALAXY:Lazarus Group",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "32a223a8-3c79-4146-87c5-8557d38662ae",
			"created_at": "2022-10-25T15:50:23.703698Z",
			"updated_at": "2026-04-10T02:00:05.261989Z",
			"deleted_at": null,
			"main_name": "Lazarus Group",
			"aliases": [
				"Lazarus Group",
				"Labyrinth Chollima",
				"HIDDEN COBRA",
				"Guardians of Peace",
				"NICKEL ACADEMY",
				"Diamond Sleet"
			],
			"source_name": "MITRE:Lazarus Group",
			"tools": [
				"RawDisk",
				"Proxysvc",
				"BADCALL",
				"FALLCHILL",
				"WannaCry",
				"MagicRAT",
				"HOPLIGHT",
				"TYPEFRAME",
				"Dtrack",
				"HotCroissant",
				"HARDRAIN",
				"Dacls",
				"KEYMARBLE",
				"TAINTEDSCRIBE",
				"AuditCred",
				"netsh",
				"ECCENTRICBANDWAGON",
				"AppleJeus",
				"BLINDINGCAN",
				"ThreatNeedle",
				"Volgmer",
				"Cryptoistic",
				"RATANKBA",
				"Bankshot"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "f32df445-9fb4-4234-99e0-3561f6498e4e",
			"created_at": "2022-10-25T16:07:23.756373Z",
			"updated_at": "2026-04-10T02:00:04.739611Z",
			"deleted_at": null,
			"main_name": "Lazarus Group",
			"aliases": [
				"APT-C-26",
				"ATK 3",
				"Appleworm",
				"Citrine Sleet",
				"DEV-0139",
				"Diamond Sleet",
				"G0032",
				"Gleaming Pisces",
				"Gods Apostles",
				"Gods Disciples",
				"Group 77",
				"Guardians of Peace",
				"Hastati Group",
				"Hidden Cobra",
				"ITG03",
				"Jade Sleet",
				"Labyrinth Chollima",
				"Lazarus Group",
				"NewRomanic Cyber Army Team",
				"Operation 99",
				"Operation AppleJeus",
				"Operation AppleJeus sequel",
				"Operation Blockbuster: Breach of Sony Pictures Entertainment",
				"Operation CryptoCore",
				"Operation Dream Job",
				"Operation Dream Magic",
				"Operation Flame",
				"Operation GhostSecret",
				"Operation In(ter)caption",
				"Operation LolZarus",
				"Operation Marstech Mayhem",
				"Operation No Pineapple!",
				"Operation North Star",
				"Operation Phantom Circuit",
				"Operation Sharpshooter",
				"Operation SyncHole",
				"Operation Ten Days of Rain / DarkSeoul",
				"Operation Troy",
				"SectorA01",
				"Slow Pisces",
				"TA404",
				"TraderTraitor",
				"UNC2970",
				"UNC4034",
				"UNC4736",
				"UNC4899",
				"UNC577",
				"Whois Hacking Team"
			],
			"source_name": "ETDA:Lazarus Group",
			"tools": [
				"3CX Backdoor",
				"3Rat Client",
				"3proxy",
				"AIRDRY",
				"ARTFULPIE",
				"ATMDtrack",
				"AlphaNC",
				"Alreay",
				"Andaratm",
				"AngryRebel",
				"AppleJeus",
				"Aryan",
				"AuditCred",
				"BADCALL",
				"BISTROMATH",
				"BLINDINGCAN",
				"BTC Changer",
				"BUFFETLINE",
				"BanSwift",
				"Bankshot",
				"Bitrep",
				"Bitsran",
				"BlindToad",
				"Bookcode",
				"BootWreck",
				"BottomLoader",
				"Brambul",
				"BravoNC",
				"Breut",
				"COLDCAT",
				"COPPERHEDGE",
				"CROWDEDFLOUNDER",
				"Castov",
				"CheeseTray",
				"CleanToad",
				"ClientTraficForwarder",
				"CollectionRAT",
				"Concealment Troy",
				"Contopee",
				"CookieTime",
				"Cyruslish",
				"DAVESHELL",
				"DBLL Dropper",
				"DLRAT",
				"DRATzarus",
				"DRATzarus RAT",
				"Dacls",
				"Dacls RAT",
				"DarkComet",
				"DarkKomet",
				"DeltaCharlie",
				"DeltaNC",
				"Dembr",
				"Destover",
				"DoublePulsar",
				"Dozer",
				"Dtrack",
				"Duuzer",
				"DyePack",
				"ECCENTRICBANDWAGON",
				"ELECTRICFISH",
				"Escad",
				"EternalBlue",
				"FALLCHILL",
				"FYNLOS",
				"FallChill RAT",
				"Farfli",
				"Fimlis",
				"FoggyBrass",
				"FudModule",
				"Fynloski",
				"Gh0st RAT",
				"Ghost RAT",
				"Gopuram",
				"HARDRAIN",
				"HIDDEN COBRA RAT/Worm",
				"HLOADER",
				"HOOKSHOT",
				"HOPLIGHT",
				"HOTCROISSANT",
				"HOTWAX",
				"HTTP Troy",
				"Hawup",
				"Hawup RAT",
				"Hermes",
				"HotCroissant",
				"HotelAlfa",
				"Hotwax",
				"HtDnDownLoader",
				"Http Dr0pper",
				"ICONICSTEALER",
				"Joanap",
				"Jokra",
				"KANDYKORN",
				"KEYMARBLE",
				"Kaos",
				"KillDisk",
				"KillMBR",
				"Koredos",
				"Krademok",
				"LIGHTSHIFT",
				"LIGHTSHOW",
				"LOLBAS",
				"LOLBins",
				"Lazarus",
				"LightlessCan",
				"Living off the Land",
				"MATA",
				"MBRkiller",
				"MagicRAT",
				"Manuscrypt",
				"Mimail",
				"Mimikatz",
				"Moudour",
				"Mydoom",
				"Mydoor",
				"Mytob",
				"NACHOCHEESE",
				"NachoCheese",
				"NestEgg",
				"NickelLoader",
				"NineRAT",
				"Novarg",
				"NukeSped",
				"OpBlockBuster",
				"PCRat",
				"PEBBLEDASH",
				"PLANKWALK",
				"POOLRAT",
				"PSLogger",
				"PhanDoor",
				"Plink",
				"PondRAT",
				"PowerBrace",
				"PowerRatankba",
				"PowerShell RAT",
				"PowerSpritz",
				"PowerTask",
				"Preft",
				"ProcDump",
				"Proxysvc",
				"PuTTY Link",
				"QUICKRIDE",
				"QUICKRIDE.POWER",
				"Quickcafe",
				"QuiteRAT",
				"R-C1",
				"ROptimizer",
				"Ratabanka",
				"RatabankaPOS",
				"Ratankba",
				"RatankbaPOS",
				"RawDisk",
				"RedShawl",
				"Rifdoor",
				"Rising Sun",
				"Romeo-CoreOne",
				"RomeoAlfa",
				"RomeoBravo",
				"RomeoCharlie",
				"RomeoCore",
				"RomeoDelta",
				"RomeoEcho",
				"RomeoFoxtrot",
				"RomeoGolf",
				"RomeoHotel",
				"RomeoMike",
				"RomeoNovember",
				"RomeoWhiskey",
				"Romeos",
				"RustBucket",
				"SHADYCAT",
				"SHARPKNOT",
				"SIGFLIP",
				"SIMPLESEA",
				"SLICKSHOES",
				"SORRYBRUTE",
				"SUDDENICON",
				"SUGARLOADER",
				"SheepRAT",
				"SierraAlfa",
				"SierraBravo",
				"SierraCharlie",
				"SierraJuliett-MikeOne",
				"SierraJuliett-MikeTwo",
				"SimpleTea",
				"SimplexTea",
				"SmallTiger",
				"Stunnel",
				"TAINTEDSCRIBE",
				"TAXHAUL",
				"TFlower",
				"TOUCHKEY",
				"TOUCHMOVE",
				"TOUCHSHIFT",
				"TOUCHSHOT",
				"TWOPENCE",
				"TYPEFRAME",
				"Tdrop",
				"Tdrop2",
				"ThreatNeedle",
				"Tiger RAT",
				"TigerRAT",
				"Trojan Manuscript",
				"Troy",
				"TroyRAT",
				"VEILEDSIGNAL",
				"VHD",
				"VHD Ransomware",
				"VIVACIOUSGIFT",
				"VSingle",
				"ValeforBeta",
				"Volgmer",
				"Vyveva",
				"W1_RAT",
				"Wana Decrypt0r",
				"WanaCry",
				"WanaCrypt",
				"WanaCrypt0r",
				"WannaCry",
				"WannaCrypt",
				"WannaCryptor",
				"WbBot",
				"Wcry",
				"Win32/KillDisk.NBB",
				"Win32/KillDisk.NBC",
				"Win32/KillDisk.NBD",
				"Win32/KillDisk.NBH",
				"Win32/KillDisk.NBI",
				"WinorDLL64",
				"Winsec",
				"WolfRAT",
				"Wormhole",
				"YamaBot",
				"Yort",
				"ZetaNile",
				"concealment_troy",
				"http_troy",
				"httpdr0pper",
				"httpdropper",
				"klovbot",
				"sRDI"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434259,
	"ts_updated_at": 1775826748,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/217d82f6f483a96b10c4e6c670bdcc4a70c940fc.pdf",
		"text": "https://archive.orkl.eu/217d82f6f483a96b10c4e6c670bdcc4a70c940fc.txt",
		"img": "https://archive.orkl.eu/217d82f6f483a96b10c4e6c670bdcc4a70c940fc.jpg"
	}
}