{
	"id": "c48afa0a-b779-4224-8f3e-23e55399f7af",
	"created_at": "2026-04-06T00:06:21.196637Z",
	"updated_at": "2026-04-10T03:37:49.664414Z",
	"deleted_at": null,
	"sha1_hash": "2d9fd1da93d5cbf4810b0394889d9097ea1f5d1f",
	"title": "APT28 Operation Phantom Net Voxel",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 3044362,
	"plain_text": "APT28 Operation Phantom Net Voxel\r\nBy Amaury G.,\u0026nbsp;Charles M.\u0026nbsp;and\u0026nbsp;Sekoia TDR\r\nPublished: 2025-09-16 · Archived: 2026-04-05 21:45:37 UTC\r\nThis post was originally distributed as a private FLINT report to our customers on 12 August 2025.\r\nTable of contents\r\nIntroduction\r\nInfection chain overview\r\nTechnical analysis\r\nLure documents analysis\r\nInfection – VBA Analysis\r\nSecond stage DLL and steganography\r\nShellcode\r\nCovenant \u0026 Koofr interactions\r\nBeardShell\r\nSlimAgent\r\nConclusion\r\nIOCs and Technical Details\r\nWeaponized Office documents\r\nPublic cloud infrastructure\r\nHashes\r\nYARA\r\nPython scripts\r\nIntroduction\r\nSekoia.io’s Threat Detection and Response (TDR) team closely monitors APT28 as one of its highest-priority threat\r\nactors. In early 2025 a trusted partner provided two previously unseen malware samples attributed to APT28. These\r\nsamples did not correspond to any publicly documented infection chain at the time, so we began to take a closer look. A few\r\nmonths later, on 21 June 2025,  CERT-UA published a report on the BeardShell and Covenant framework, attributing them\r\nto APT28. By analysing the samples, we established that the partner’s samples and those described by CERT-UA were\r\nidentical. By correlating CERT-UA’s findings with our own, we uncovered additional weaponized Office documents and\r\nsubtle techniques that have not yet been documented publicly.\r\nKnown by no fewer than 28 aliases – among them Sofacy, Fancy Bear, BlueDelta, Forest Blizzard and TAG-110 – APT28 is\r\nidentified by intelligence services as operated by Russia’s General Staff Main Intelligence Directorate (GRU), specifically\r\nthe 85th Main Special Service Centre (GTsSS) of Military Unit 26165. \r\nThroughout 2025, this intrusion set has drawn attention across the cybersecurity community by being the subject of multiple\r\nreports including a joint advisory of 21 international partners or a report of the French ANSSI. In January 2025, we shared\r\nour findings on the Double-Tap campaign, a Russia-nexus APT operation potentially linked to APT28 that targeted\r\ndiplomatic channels in Central Asia and Kazakhstan for cyber espionage. This campaign remains active and has shifted its\r\noperations to targets in Tajikistan, as noted by Recorded Future in May 2025, although a conclusive attribution between\r\nUAC-0063 and APT28 has yet to be established.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 1 of 35\n\nThis report presents our current insights and serves to complement CERT-UA’s analysis of the new techniques\r\ndeployed by APT28 in this new campaign based on our own investigation.\r\nInfection chain overview\r\nCombining CERT-UA’s findings with our own analysis, the following figure presents an overview of the infection chain.\r\nFigure 1 – Overall infection chain\r\nAs documented by CERT-UA, the infection chain begins with the Office document being delivered via a private Signal chat.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 2 of 35\n\nFigure 2 – Preview of Signal conversation (source: CERT-UA)\r\nThese exchanges exemplify a spearphishing campaign in which a user, posing as a colleague or superior, urges the recipient\r\nto open and complete the malicious Office document. By invoking compensation decisions and threatening legal action, the\r\nsender creates a false sense of urgency, manipulating the target with references to penalties and prompts to liaise with\r\nhigher-level management for further details.\r\nWhy using Signal Desktop?\r\nWe were surprised by the use of Signal as an infection vector by APT28. However, after conducting several tests and\r\nreviewing the Signal Desktop source code, we discovered that Signal Desktop does not implement the MOTW security\r\nmechanism.\r\nMOTW (Mark of the Web) is a Windows security feature based on NTFS Alternate Data Streams (ADS), designed to\r\nprevent the easy execution of files originating from the Internet. Since Signal does not apply MOTW, macros embedded in\r\nreceived documents are not blocked by Microsoft Office’s security mechanisms—unlike documents downloaded from the\r\nweb or received through email clients such as Outlook.\r\nThe document retrieved by the victim embeds multiple malicious macros: a primary routine plus several auxiliary methods\r\nthat together implement a user-level COM hijack to load a malicious DLL. Once loaded this DLL extracts a shellcode from\r\na valid PNG file, windows.png , which loads a .NET assembly executable. This new executable corresponds to the\r\nGruntHTTPStager component of the Covenant framework. Its primary function is to establish an API-driven channel to\r\nthe Koofr cloud infrastructure and await some additional payloads.\r\nAccording to CERT-UA, this first first part of the infection chain leads to the download of two files, sample-03.wav and\r\nPlaySndSrv.dll . We were unable to retrieve these samples and therefore could not analyse them. PlaySndSrv.dll is\r\nreported to decrypt and extract a second-stage payload from sample-03.wav , named BeardShell. This C++ malware uses\r\nthe icedrive cloud-storage service as its command and control channel to receive and execute PowerShell commands.\r\nIn the following section of this report, we will analyze in detail the various stages of the infection chain. We will also\r\ndescribe SlimAgent, a spyware that CERT-UA observed on the same infected server as BeardShell even if they cannot\r\nconfirm a direct link between it and this infection chain. The only part not covered by our analysis is related to the\r\ndeployment of BeardShell (via the two files sample-03.wav and PlaySndSrv.dll ).\r\nTechnical analysis\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 3 of 35\n\nLure documents analysis\r\nIn its report, CERT-UA identifies one malicious Word document dubbed Акт.doc which we have been unable to obtain.\r\nFrom our previous investigation, we obtained a total of eleven Office documents. Several of these share distinct similarities,\r\nwhile others appear incomplete and were likely used as test files during the weaponisation phase.\r\nMD5sum Filename\r\n915179579ab7dc358c41ea99e4fcab52 Акт.doc\r\nb6e3894c17fb05db754a61ac9a0e5925 tmsnrb41da2y867.tmp\r\n66007a1ca6d07ebb4ed85bf82e79719d [UNKNOWN]\r\n608877a9e11101da53bce99b0effc75b СЛУЖБОВА ХАРАКТЕРИСТИКА.doc\r\n2632fa8fc67dd2fd5c5a6275465dcc95 tmsnrb41da2y867.tmp\r\n0fbc2bf2f66fc72c521a9b8561bab1da Акт_про_передачу_обладнання_в_експлуатацію_150425.doc\r\n3b4ea6079ac9f154e0d4ec2cb6d05431 1a#U0410.doc\r\n7de7febec6bed06c49efb4e2c3dd23e1 attachment.doc\r\n7de7febec6bed06c49efb4e2c3dd23e1 zrazok-raport-matdopomoga-forma-dlya-zapovnennya-v3.doc\r\nbbfb92161cb71825a16e49e2aa4d2750 lorem.doc\r\n1498f1df4ca0e9cf23babe00cf34ed3d lorem.doc\r\nList of weaponized Office documents related to this infection chain\r\nThe analysis of spearphishing lure documents reveals a consistent focus on Ukrainian military administrative procedures.\r\nAmong the documents, we identified forms for military evaluation, administrative procedure for requesting medical\r\ncompensation, personnel reports or military permission, all typical of active-duty personnel under martial law\r\nconditions. These also include logistical records such as drone delivery receipts by a specialized unit. All those documents\r\nwould plausibly be found in the internal workflow of brigade-level military administration. It suggests targeting of on-the-ground soldiers or personnel embedded within military HR, logistics, or command structures.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 4 of 35\n\nFigure 3 – Preview of the document СЛУЖБОВА ХАРАКТЕРИСТИКА.doc before the macros are executed\r\n(which will make the document readable and correct in Ukrainian)\r\nWe assess that the prevalence of injury-related documentation may also suggest a potential targeting interest in identifying\r\nwounded personnel, their units, and associated chains of command – possibly to assess attrition, operational readiness, or\r\npsychological resilience within specific formations.\r\nAdditionally, logistics documents related to material transfers (observation or attack drones) could demonstrate efforts to\r\ntrace equipment provisioning, identify supply chains, or anticipate the deployment of high-value systems in-theatre.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 5 of 35\n\nGiven the consistent formatting, terminology, and use of official military nomenclature in Ukrainian, these documents are\r\nlikely designed to appear credible to military administrative staff. They are highly likely used by Russian military\r\nintelligence to gather cyber intelligence on frontline combatants, possibly on specific units in the Ukrainian military\r\ntheatre. This activity is coherent with GRU mandate and APT28’s previous operations.\r\nInfection – VBA Analysis\r\nThe infection begins with Visual Basic macros embedded in the Word documents. For this part, our analysis is based on\r\nmacros extracted from СЛУЖБОВА ХАРАКТЕРИСТИКА.doc (MD5sum: 608877a9e11101da53bce99b0effc75b ).\r\nRem Attribute VBA_ModuleType=VBAModule\r\nOption VBASupport 1\r\nOption Explicit#If Win64 Or Win32 Then\r\n  #If VBA7 Then\r\nWhen run, these macros first verify that the host operating system is Windows and then detect whether the VBA\r\nenvironment is version 7 (Office 2010+) or an earlier release. Then, Windows API functions are declared. The format of\r\nthese declarations depend on the VBA version:\r\n‘ VBA6\r\nPublic Declare Function GetImageResolution Lib \"kernel32\" Alias \"GetFileAttributesW\" (ByVal lFN As Long) As Long\r\n‘ VBA7\r\nPublic Declare PtrSafe Function GetImageResolution Lib \"kernel32\" Alias \"GetFileAttributesW\" (ByVal lFN As LongPtr) As Lon\r\nThe next steps of the macro are the following:\r\nSwitches the document in Print Layout with the following command\r\nActiveDocument.ActiveWindow.View.Type = wdPrintView\r\nDeobfuscates data by replacing character pairs\r\nDim b() As Byte\r\nReDim b(1)\r\n \r\nDim bb() As Byte\r\nReDim bb(1)\r\nbb(0) = 86\r\nbb(1) = 4\r\nb(0) = 250\r\nb(1) = 8\r\nWith ActiveDocument.Content.Find\r\n .Execute FindText:=b, ReplaceWith:=bb, Replace:=wdReplaceAll, MatchCase:=msoTrue\r\nEnd With\r\nAdds persistence by crafting this command and executing it using the CreateProcessW API\r\nreg.exe add HKCU\\Software\\classes\\CLSID\\{2227A280-3AEA-1069-A2DE-08002B30309D}\\InProcServer32 /d\r\n\"C:\\ProgramData\\prnfldr.dll\" /f . The param /reg:64 is added for x64 architecture.\r\nCalls the GetFileAttributesW function to check the existence of these two files\r\n%allusersprofile%/prnfldr.dll\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 6 of 35\n\n%localappdata%/windows.png\r\nIf one of these files exists, the macro exits.\r\nCalls the GetFileAttributesW function on this directory C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319 in\r\norder to verify that the v4.0.30319 of the .NET Framework is installed on the host\r\nDrops prnfldr.dll and windows.png , flags them as hidden\r\nExecutes the following command: regsvr32.exe /n /i \"C:\\ProgramData\\prnfldr.dll\" . The /i parameter results\r\nin calling the DllInstall function of the prnfldr.dll file.\r\nAt the end of this process, the macros has dropped two files on the filesystem: a library, %allusersprofile%\\prnfldr.dll\r\n(i.e C:\\ProgramData\\prnfldr.dll ), and a PNG file, %localappdata%\\windows.png (i.e C:\\Users\\\r\n\u003cuser\u003e\\AppData\\Local\\windows.png ).\r\nSecond stage DLL and steganography\r\nThe second stage of the infection chain we analyzed is the prnfldr.dll. In the CERT-UA report, this stage corresponds to the\r\nctec.dll library deployed in the %APPDATA%\\microsoft\\protect\\ directory.\r\nUpon initial access, reg.exe is used to add an InProcServer32 registry key at HKCU\\Software\\Classes\\CLSID{2227A280-\r\n3AEA-1069-A2DE-08002B30309D} pointing to C:\\ProgramData\\prnfldr.dll . This action registers a new COM server under\r\nthe CLSID {2227a280-3aea-1069-a2de-08002b30309d} (also referred to as CLSID_Printers). Thereafter, at each user logon,\r\nexplorer.exe automatically loads this server into its process space, causing the DLL’s DllMain entry point to execute on\r\nevery sign-in. To avoid waiting for a logoff and subsequent logon, the accompanying VBA macro immediately invokes\r\nregsvr32.exe with the /i switch, triggering the DLL’s installation routine ( DllInstall ) solely to bypass any DllMain\r\nchecks.\r\nThe DllMain routine begins by calling GetModuleFileNameW . If it detects that the hosting process is regsvr32.exe , it\r\nimmediately returns and defers further actions to DllInstall . Otherwise, when the DLL is loaded as a COM server, it\r\ntakes steps to preserve the genuine Printer COM server’s functionality. First, it calls LoadLibrary with the exact path to the\r\nlegitimate prnfldr.dll . Then, for each exported function, it calls GetProcAddress on that same prnfldr.dll to retrieve\r\nthe authentic function address. In this way, the malicious DLL transparently proxies every original call, ensuring that all\r\nstandard printing operations continue to work as expected.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 7 of 35\n\nFigure 4 – Screenshot of the DllMain function of the prnfldr.dll malicious library\r\nThereafter, a new thread is spawned to load the next stage only when executing under the explorer.exe process.\r\nFigure 5 – Main thread creation\r\nThe DecryptAndLoadNextStage function deciphers the file path %LocalAppData%\\windows.png . As with the other\r\nencrypted strings in this sample, it employs a basic single-byte XOR cipher, with the key prefixed to the data. The DLL then\r\nproceeds into an endless loop, performing the following actions:\r\nCreates a mutex named msOfficeLocker__w\r\nLoads the next stage from the windows.png file\r\nThe windows.png file is a valid PNG file that represents the windows background.\r\nFigure 6 – Preview of a windows.png image that embeds a shellcode\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 8 of 35\n\nIn this PNG file, 4 bytes are used to represent each pixel (R-G-B and Transparency). The LSB (Least Significant Bit) of each\r\nvalue is used to create the next payload that is composed of:\r\nthe size of the data + 20 (SHA1 size)\r\nthe data\r\nthe SHA1 of the data\r\nThe data is an AES-CBC encrypted blob that contains:\r\nthe key (32 bytes)\r\nthe encrypted content\r\nthe IV (16 bytes)\r\nOnce decrypted, the data reveals shellcode that initiates the next phase of the infection chain. In one recovered document,\r\nthe windows.png file is replaced by koala.png, yet the extracted payload in both cases remains identical.\r\nFigure 7 – Preview of the Koala.png image that embeds a shellcode\r\nShellcode\r\nThe shellcode extracted from the PNG file is designed to load a new PE executable, identified as the HTTP Grunt Stager\r\nmodule of Covenant. Because this component is a .NET assembly, the shellcode must initialise the Common Language\r\nRuntime environment. It then invokes the following functions in sequence (non-exhaustive):\r\nCLRCreateInstance : Initiatilize the Common Language Runtime\r\nICLRMetaHost::GetRuntime with v4.0.30319 as parameter in order to load the v4.0.30319 of the .NET framework\r\nICorRuntimeHost::Start to start the CLR\r\nICorRuntimeHost::CreateDomain to create an AppDomain with the name aXbpOzzF\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 9 of 35\n\nFigure 8 – Shellcode – CLR Loading and starting\r\nNext, the SafeArrayAccessData function is invoked to create an array intended to store the next stage.\r\nFigure 9 – Shellcode – Next stage copying\r\nAfterwards, the array is loaded into the AppDomain via the AppDomain-\u003eLoad_3 function, and its entry point is obtained\r\nusing the _Assembly::get_EntryPoint function.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 10 of 35\n\nFigure 10 – Shellcode – Get entrypoint\r\nFinally, the _MethodInfo::Invoke_3 function is called to call the next stage entry point.\r\nFigure 11 – Shellcode – Invoke method\r\nThe loaded PE is obfuscated: its strings are encrypted using a ten-character XOR key, and all function and variable names\r\nare entirely randomized. According to CERT-UA, this PE forms part of the Covenant framework, serving as the HTTP Grunt\r\nStager.\r\nCovenant \u0026 Koofr interactions\r\nThroughout this campaign, APT28 leveraged Covenant and its C2Bridge functionality to interact with the Koofr cloud\r\ninfrastructure API, uploading files for reconnaissance and downloading additional payloads to extend the infection chain. It\r\nis worth noting that Koofr is a legitimate secure cloud-storage service that enables users to store, synchronize, and share data\r\nwhile aggregating multiple external cloud accounts into a single interface.\r\nBased on our findings of all Office documents, we extracted the embedded credentials used by the customized COVENANT\r\nmalware to interact with Koofr cloud infrastructure. We accessed the associated accounts, and analyzed their contents. This\r\nprocess uncovered two distinct accounts:\r\njakub B registered with jakub2233@tutamail[.]com using the storage container ID 133bd1da-4d2b-454f-9029-\r\n87f46ab30ca7\r\nAlan Smith registered with Alan_Smith2304@outlook[.]com using the storage container ID 90af1701-2aff-461d-b962-b32dec0f1a13\r\nIn total, over the two storage volumes, we were able to find 115 files spread across multiple folders.\r\nWe counted 42 unique partial GUIDs, meaning there are probably 42 different compromised hosts, where the earlier file\r\ncreation was on 3 December 2024. Please note that these compromised hosts may include those created by analysts within a\r\nsandbox environment.\r\nThe .NET executables appear to use randomized names, for example xhdelebf.ntf.exe or 3af3begb.0ak.exe . The\r\nsamples are a Gruntpayload from the Covenant framework usually used by red teamers to establish a C2 channel to\r\nexfiltrate command results and retrieve additional malicious modules, but with a twist. Within the Covenant framework, an\r\nattacker can implement a custom outbound command-and-control protocol by creating a C2Bridge and a BridgeListener\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 11 of 35\n\nwithout editing any Covenant code. Here the attacker seems to have implemented a custom C2Bridge leveraging the Koofr\r\nAPI as a C2, relying entirely on file upload and download to the Koofr service for its communication.\r\nThe following section describes in detail the network exchanges we observed during the in-memory execution of Covenant.\r\nStep 1\r\nUpon in-memory execution it connects to a hardcoded Koofr account and verifies the existence of two directories, Keeping\r\nand Tansfering . If either folder is missing, the malware creates both. \r\nNext, it retrieves a Covenant-provided identifier (the GUID of the compromised host), splits the string, and takes only the\r\nfinal segment to name a new parent directory. Inside that directory, it recreates the Keeping and Tansfering subfolders\r\nfor subsequent file operations.\r\nFigure 12 – Covenant \u0026 Koofr interaction – Directories creation\r\nStep 2\r\nOnce the folder structure is established, Grunt uses hybrid encryption to perform a secure key exchange. The encryption\r\nprocess follows the same steps documented on this GitHub repository, but here all data flows through file uploads and\r\ndownloads on the Koofr cloud storage. \r\nThis indicates APT28 most likely leverages a custom C2bridges to operate the Covenant’s Grunt server-side\r\ncomponent and to automate Koofr interactions.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 12 of 35\n\nFigure 13 – Covenant \u0026 Koofr interaction – Secure key exchange\r\nStep 3\r\nWe managed to dump the AES session key during the execution of the implant which allowed us to decrypt the\r\ncommunications and have deeper insights on that part of the execution chain. After establishing the secure channel, the\r\nimplant polls the Tansfering folder for new “modules” (which appear to be Covenant “Tasks”) using GET requests. Once a\r\nfile appears in the Tansfering folder, the implant downloads that file, decrypts the EncryptedMessage field’s value and\r\nloads it into memory.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 13 of 35\n\nFigure 14 – Covenant \u0026 Koofr interaction – Overall view of interactions\r\nIn the example above, the implant uploaded a PNG file to the Keeping folder, which turned out to be a screenshot of our\r\nsandbox. On top of the ScreenShot Task, the implant downloaded multiple Tasks to perform reconnaissance actions such as\r\nARP scans, IP information requests, tracert and process enumeration. Each Task’s output is uploaded to Koofr immediately\r\nafter execution, and the implant deletes each module file afterwards.\r\nAt this point, based on the details of their computer-technical investigation, the CERT-UA assumes that COVENANT was\r\nused to download the executable file PlaySndSrv.dll and sample-03.wav that permit the extraction and execution of\r\nBeardShell. We did not observe these downloads, maybe due to our sandboxed environment, suggesting that the later\r\nimportant stages of the execution chain are sent manually by APT28.\r\nBeardShell\r\nAs explained earlier in this report, we were unable to recover the sample nor the shellcode used to load the BeardShell\r\nbackdoor. Nevertheless, we were able to analyze BeardShell in early 2025. This malware uses a cloud storage service named\r\nicedrive as C2 channel. From a high-level standpoint, its workflow is simple:\r\nOn startup, it executes the SystemInfo command and uploads the output to icedrive\r\nIt ensures the existence of a designated directory in the cloud service, creating one if absent\r\nEvery four hours, it checks the directory for operator-uploaded files. If a file is present, the malware downloads and\r\ndecrypts it, executes the embedded commands, deletes the file from the directory, and uploads the execution result to\r\nthe storage root\r\nFigure 15 – BeardShell – Overview of interactions with icedrive\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 14 of 35\n\nBeardShell analysis\r\nBeardShell is developed as a C++ DLL and makes extensive use of encrypted strings. It relies on the same single-byte XOR\r\ncipher employed in the infection chain’s second stage, with each key prefixed to its corresponding ciphertext. Some strings\r\nare decrypted in place, while others are resolved during program initialisation via routines invoked by the _initterm\r\nfunction. One of these encrypted values is a hardcoded bearer token that enables access to an icedrive cloud-storage account.\r\nBeyond this XOR-based string encryption, no additional obfuscation techniques were identified (unless we consider that\r\nusing templates and lambda expressions in C++ is an obfuscation technique).\r\nSince DllMain is inert, the backdoor’s true entry point is its sole exported function, ServiceMain . This routine first\r\ncreates a mutex named Buster and then spawns the main thread, which immediately performs a basic anti-analysis check:\r\nit terminates if the system has only one processor or under 2 GB of RAM.\r\nOnce the anti-analysis checks are cleared, BeardShell generates a unique identifier derived from host details by calling:\r\nGetCurrentHwProfileW: this function retrieves a GUID string for the hardware profile\r\nNetWkstaGetInfo with the parameter 100. This results in retrieving information the host name and domain name\r\nNetWkstaUserGetInfo with parameter 1 to retrieve the workstation name and the username\r\nAll these strings are concatenated then hashed with the FNV4 algorithm.\r\n//String format used for FNV4 hashing\r\n\u003cGUID\u003e\u003cLocal Computer Name\u003e\u003cDomain Name\u003e\u003cUsername\u003e\u003cWorkstation name\u003e\r\nThe computed hash is combined with the fixed value 6401 , separated by an underscore, producing a directory name such\r\nas 6401_1911134395795255480 . BeardShell then uses this folder within the icedrive workspace to poll for commands to\r\nexecute.\r\nOn each execution, the malware’s first step is to invoke the SystemInfo command. This procedure gathers a host\r\nfingerprint and uploads the resulting data to icedrive. Notably, it employs the same JSON-based interface that operators use\r\nto issue commands to a remote PowerShell session. To perform SystemInfo , it sends the following three payloads to its\r\ncommand handler:\r\n1. {} : Creates a new PowerShell session. Since this is the first session, it is assigned the identifier “0”.\r\n2. { \"id\":0,\"cmd\":\"SystemInfo\"} : Executes the SystemInfo command in session 0\r\n3. {} : Closes all sessions\r\nThis can be observed in the following figure.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 15 of 35\n\nFigure 16 – BeardShell – Initial sequence used to carry out SystemInfo execution\r\nInternally, BeardShell commands are JSON objects that contain three required properties:\r\nA task_id integer: Used to identify the current task. The associated number is notably used to return the result of\r\nthe command\r\nA cmd_id integer: BeardShell is composed of 7 commands identified by an integer (from 1 to 7)\r\nA data JSON object that corresponds to the parameters of the command\r\nOnce this series of commands has run, BeardShell uploads the output as a file to its hardcoded icedrive storage. Thereafter,\r\nevery four hours it polls its designated directory on icedrive for any new file, downloading and executing each one it finds.\r\nPowerShell execution\r\nBeardShell is a C++ backdoor designed to execute PowerShell commands. C++ is a native language that produces\r\nunmanaged code, whereas PowerShell relies on the .NET framework and runs managed code. Implementing the backdoor in\r\n.NET would have been more straightforward. Instead, BeardShell uses Windows API interfaces to load the Common\r\nLanguage Runtime (CLR) before executing managed code.\r\nBeardShell employs the same approach as the shellcode that launches Covenant’s Grunt Stager. It begins by initialising the\r\nCLR, then loads the System.Management.Automation assembly, which supplies the classes required to create PowerShell\r\ninstances. Also, BeardShell exposes several operations, the first ( Command 1 ) instantiates a fresh, empty PowerShell\r\ninstance. This instance may then be used to invoke commands (Command 2 via AddCommand) or execute scripts\r\n(Command 3 via AddScript).\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 16 of 35\n\nBeardShell commands\r\nBeardShell implements 7 commands identified by a command ID between 1 and 7. Its internal representation of commands\r\nare in JSON and followed this schema:\r\n{“task_id”: X, “cmd_id”:Y, data: {}}\r\nIn addition to the cmd_id , a command also have:\r\nA task_id which seems to be here for the operator as it has no effect for the backdoor point of view\r\nA data field which expects another JSON object that corresponds to the command parameters\r\nThe following table presents the seven commands implemented by BeardShell, their purposes and parameters expected.\r\nCommand ID Purpose Parameters\r\n1 Create a PowerShell instance {}\r\n2 Execute a command {“id”=0, “cmd”=”\u003ccommand\u003e”}\r\n3 Execute a script {“id”=0, “script”=”\u003cb64(command)\u003e”}\r\n4 Stop a command {“id”=0}\r\n5 Close PowerShell instance {“id”=0}\r\n6 Get BeardShell status  {}\r\n7 Close all PowerShell instances {}\r\nBeardShell – Command list description\r\nThe first command is used to create an empty PowerShell instance that is waiting for something to execute. Multiple\r\nPowerShell instances can be executed at the same time. In order to differentiate and choose in which instance to execute\r\nsomething, BeardShell returns the following messages after a PowerShell instantiation: “ Terminal created with id = X ”,\r\nwith “ X ” a number. This ID must then be used in BeardShell commands. As an example, commands 2 and 3, which are\r\nrespectively used to execute a command ( AddCommand function) or a script ( AddScript ), take this ID as argument.\r\nCommands 6 and 7 require no parameter as they are not relative to a specific PowerShell instance.\r\nHere is an example of the result for the command 6:\r\n{\"task_id\":40,\"cmd_id\":6,\"data\":{\"type\":6,\"json\":\"eyIwIjp7InRhc2tfaWQiOjEsInN0YXRlIjoiaW5pdGlhbGl6ZSJ9LCIxIjp7InRhc2tfaWQ\r\nThe decoded base64 corresponds to this JSON:\r\n{\"0\":{\"task_id\":1,\"state\":\"initialize\"},\"1\":{\"task_id\":40,\"state\":\"initialize\"},\"2\":{\"task_id\":42,\"state\":\"initialize\"}}\r\nIn this example, 3 PowerShell instances are instantiated (of ID 0, 1 and 2) and are waiting for a command to execute. States\r\nobserved are:\r\ninitialize\r\nstarted \r\ncompleted\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 17 of 35\n\nC2 Communications\r\nBeardShell uses an icedrive account as a C2 channel. In the workspace of this account, BeardShell creates a directory with a\r\nname like 6401_1911134395795255480 , where 1911134395795255480 is the FNV4 hash of a fingerprint of the host. This\r\ndirectory is used by the operators to upload files corresponding to commands to execute. This hash is most of the time the\r\nsame after reboot (it changes only some information, like the workstation name or username is changed).\r\nOn the other hand, BeardShell uploads results into the root of the workspace. We have seen that BeardShell commands use a\r\nJSON representation. But a layer of encryption is also added when files are uploaded to the cloud storage service.\r\nFirst, considering the name of the file, uploaded files don’t need a special name as BeardShell downloaded commands from\r\nits own directory. On the other hand, responses are uploaded to the root directory. Thus, these files start with the name of the\r\nhost directory followed by 9 random characters and an extension like: 6401_1911134395795255480-4vas3yJlv.bmp .\r\nThe file extension is randomly chosen between:\r\nbmp\r\ngif\r\njpeg\r\npng\r\ntiff\r\nThe chosen extension defines the header added at the beginning of the file and, for some extensions, a footer. The only\r\npurpose of these headers and footers are to masquerade as a valid file. The following table represents the header and footer\r\nbytes value associated with each extension.\r\nExtension Corresponding header Corresponding footer\r\nbmp 42 4D None\r\ngif 47 49 46 38 39 61 00 3B\r\njpeg\r\nFF D8 FF E0 00 10 4A 46 49 46 00 01 01 01\r\n00\r\nFF D9\r\npng\r\n89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48\r\n44 52\r\n00 00 00 00 49 45 4E 44 AE 42 60\r\n82\r\ntiff 49 49 2A 00 None\r\nBeardShell – Headers and footers associated with each extension\r\nFile contents are protected and later restored using the ChaCha20-Poly1305 authenticated encryption algorithm devised by\r\nD. J. Bernstein. ChaCha20 acts as the stream cipher while Poly1305 fulfils the authentication role. The scheme requires a\r\n32-byte key and produces a 16-byte authentication tag. Although this sample embeds the key\r\n( F9685510DD90C05856950D86C12CF7A2CC9D148AACC187DDDDFCE0C9EDAE6EE3 ), we cannot confirm whether it is reused across\r\nother variants, as only one specimen is available.\r\nThe payload (i.e. the file without header and footer) is constructed as follows:\r\nOffset (without\r\nheader)\r\nSize (in\r\nbytes)\r\nDescription\r\n0 4\r\nInteger representing the size of the payload (ie the size of the file\r\nwithout the size of the header and the footer)\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 18 of 35\n\n4 10\r\nThe IV/Nonce used by the ChaCha20-Poly1305 cryptographic\r\nalgorithm.\r\n16 16\r\nThe TAG generated by the ChaCha20-Poly1305 cryptographic\r\nalgorithm.\r\n32 4 Random integer, 10 to 15\r\n36 [10:15]\r\nRandom values. The size of this field is defined by the previous field\r\n(thus, between 10 and 15).\r\n46-51 Encrypted ChaCha20-Poly1305 payload.\r\nPython scripts for generating and parsing these files are provided in Appendix: Python scripts. A YARA rule is also shared to\r\ndetect encrypted files by inspecting their header, footer and overall size.\r\nMiscellaneous\r\nBeardShell includes Run-Time Type Information (RTTI), which provides insight into its overall code architecture. Hence,\r\nthanks to the use of lambda expressions by the developers (that can generate RTTI), we have the prototype of two functions:\r\n  void PwrshlInvoker::execute_command(struct Command \u0026\u0026)\r\nIceDrive::check_result()\r\nThis indicates the name of two classes: PwrshlInvoker and IceDrive that respectively contain a function named\r\nexecute_command and check_result .\r\nMoreover, we identified another class called WinHttpWrapper\u003cIceDrive\u003e . This is particularly noteworthy because the\r\nIceDrive class, named after the cloud storage service, serves as the template parameter for WinHttpWrapper . This could\r\nsuggest that the implementation of BeardShell is designed to be easily extended to other cloud storage services.\r\nSlimAgent\r\nAccording to the CERT-UA, the implant SlimAgent is a malicious DLL dropped on the same server as BeardShell. Although\r\nCERT-UA’s report did not establish a direct link to the infection chain, we choose to add its analysis in this report. Of\r\nparticular interest this code shares similarities with the prnfldr.dll in the way it is loaded.\r\nThis malware (MD5sum : 889B83D375A0FB00670AF5276816080E ) is written in C++, and has for main functionality\r\nkeylogging and screenshooting. Notably, this malware does not implement any C2 communication or data exfiltration\r\nmechanisms. \r\nThe DllMain first verifies if it has been launched by rundll32.exe using GetModuleFileNameW , in that case, the\r\nDllMain exits. Else It proceeds to load the real eapphost.dll from System32, and then sets up export function pointers to\r\nproxy calls to the real eapphost.dll , ensuring that any expected functionality is preserved while its malicious code\r\nexecutes.\r\nA last check is done in the DllMain , the malware main thread is started only if SlimAgent is executed in explorer.exe .\r\nWhile we lack traces of execution of this malware on a victim system, the checks present indicate that the malware is\r\nsupposed to be injected in explorer.exe . Interestingly, there is no direct link between the real eapphost.dll and\r\nexplorer.exe on an uncompromised host, meaning there is probably some action done by the operator to deploy\r\nSlimAgent. It is possible that the operator leverages COM hijacking again, to establish persistence, and execute the implant\r\nin the correct context, but it could also leverage a different technique.\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 19 of 35\n\nIn both execution cases, a new thread containing the main malware code is created. SlimAgent loads a public RSA key from\r\n.data section, prepares a path for data exfiltration C:\\Users\\\u003cuser\u003e\\AppData\\Local\\Temp\\Desktop_\u003cDD-MM-YYYY_HH-MM-SS\u003e.svc and proceed to launch the main loop, handling screenshot capture, keylogging and clipboard copying functions.\r\nOnly one Desktop_\u003ctimestamp\u003e.svc is created during the malware execution. For the content of this file, the malware\r\nemploys a hybrid encryption scheme using the Windows CryptoAPI. It generates a random AES-256 session key, which it\r\nuses to encrypt the data. The session key itself is then exported encrypted by the aforementioned embedded RSA public key,\r\nusing CryptoAPI’s SIMPLEBLOB format.\r\nThe decrypted exfiltration file is in HTML format, the JPEG screenshots taken every 5 seconds, the process name,\r\nkeystrokes and clipboard data are stored between HTML tags with specific colors, such as green for the process, black for\r\nthe clipboard.\r\nThe keylogging functionality is layout aware, and encodes special keys in unicode (i.e. : [BKSP] for backspace).\r\nInterestingly, the keylogging function can trigger a screenshot when the buffer is more than 4 characters long, followed by\r\nan Enter press, most likely detecting passwords.\r\nThe last worth mentioning part of the keylogger, is its ability to track mouse movement, and windows focus. When changing\r\nwindows, the current keystrokes data are logged to the file along with the full process name running the windows in the\r\nDesktop.svc file.\r\nFigure 17 – SlimAgent – Example Desktop.svc exfiltration file decrypted content. The header of this figure\r\nis a blue text that contains the date, hour and current process path. The footer is a green text that contains the\r\nname of the current window and, in black, the clipboard content.\r\nSimilarities with the second stage DLL\r\nThe loading mechanism is very similar to the one used in the second stage DLL: the malicious prnfldr.dll library, the\r\nDLL used to extract a shellcode from the PNG file. As a reminder, this library configures itself as a proxy in its DllMain\r\nfunction only if it is not executed by regsvr32.exe . The main thread of prnfldr.dll is executed if the current module is\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 20 of 35\n\nexplorer.exe (via its DllMain function) or via its DllInstall function which is called by the regsvr32.exe binary in\r\nthe visual basic macros. Even if this process is not exceptional and the base code of the implementations are different these\r\nsimilarities could suggest that they are tied.\r\nThe first figure below shows how SlimAgent configures itself as a proxy for the real eapphost.dll while the second figure\r\ncorresponds to the code of the prnfldr.dll malicious library.\r\nFigure 18 – SlimAgent – Extract of DllMain – Proxy DLL set up\r\nFigure 19 – Second stage DLL – Extract of DllMain – Proxy DLL set up\r\nThese two figures show the similarities of the two codes. We also observe some differences:\r\nEmbedded strings are encrypted in the second figure;\r\nThe if… collapsed item in the second figure (third line) corresponds to the deallocation of the path_dll C++\r\nstring. On the other hand, SlimAgent doesn’t use C++ strings but C strings (which has a simpler deallocation\r\nmechanism).\r\nConclusion\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 21 of 35\n\nIn this campaign APT28 focuses on Ukrainian military command and administration. The infection chain is sophisticated\r\nand highly likely to be reused in the coming years thanks to its robust design. \r\nThis operation marks a clear technical step up over previous attacks with the integration of the open source Covenant\r\nframework and the use of third party cloud services Koofr and icedrive for covert communications. We also observed novel\r\nobfuscation methods embedding payloads inside PNG files, a technique never before seen in APT28 activity. The\r\nCovenant-based reconnaissance via the Koofr API combined with automatic or manual dropping of BeardShell modules\r\ndemonstrates the group’s operational flexibility. In August 2025, we observed this infection chain reused in a public cloud\r\nenvironment (Filen) via a weaponized Excel document, confirming that APT28 continues to recycle and adapt this infection\r\nchain. Some aspects remain unclear, as noted by CERT-UA, including the precise deployment mechanism of BeardShell and\r\nthe link of SlimAgent.\r\nAPT28 now wields a hardened toolset that blends open source components and legitimate cloud infrastructure to evade\r\ndetection and maintain long term access. TDR team will continue to track this campaign closely and enhance our\r\ndetections to anticipate its next evolutions.\r\nIOCs and Technical Details\r\nWeaponized Office documents\r\nMD5sum Filename\r\nCreation date\r\n(metadata)\r\n915179579ab7dc358c41ea99e4fcab52 Акт.doc [UNKNOWN\r\nf21b63ddd7d2a773eb21a065015cdd01 lorem.doc 2024-02-13\r\n66007a1ca6d07ebb4ed85bf82e79719d [UNKNOWN] 2024-12-05\r\nbbfb92161cb71825a16e49e2aa4d2750 zrazok-raport-matdopomoga-forma-dlya-zapovnennya-v3.doc 2024-12-05\r\n608877a9e11101da53bce99b0effc75b СЛУЖБОВА ХАРАКТЕРИСТИКА.doc 2024-12-18\r\n3b4ea6079ac9f154e0d4ec2cb6d05431 1a#U0410.doc 2024-12-18\r\n7de7febec6bed06c49efb4e2c3dd23e1 attachment.doc 2024-12-18\r\n1498f1df4ca0e9cf23babe00cf34ed3d lorem.doc 2025-04-01\r\n0fbc2bf2f66fc72c521a9b8561bab1da Акт_про_передачу_обладнання_в_експлуатацію_150425.doc 2025-04-15\r\nb6e3894c17fb05db754a61ac9a0e5925 tmsnrb41da2y867.tmp 2025-06-16\r\n2632fa8fc67dd2fd5c5a6275465dcc95 tmsnrb41da2y867.tmp 2025-06-16\r\n81159738f7ffb50d5bc3c75e5e0ac546 [UNKNOWN] 2025-08-04\r\nPublic cloud infrastructure\r\nName Legitimate service APIs used for C2 communications\r\nKoofr app.koofr.net\r\nicedrive api.icedrive.net\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 22 of 35\n\nFilen\r\ngateway.filen.io\r\ngateway.filen.net\r\ngateway.filen-1.net\r\ngateway.filen-2.net\r\ngateway.filen-3.net\r\ngateway.filen-4.net\r\ngateway.filen-5.net\r\ngateway.filen-6.net\r\negest.filen.io\r\negest.filen.net\r\negest.filen-1.net\r\negest.filen-2.net\r\negest.filen-3.net\r\negest.filen-4.net\r\negest.filen-5.net\r\negest.filen-6.net\r\ningest.filen.io\r\ningest.filen.net\r\ningest.filen-1.net\r\ningest.filen-2.net\r\ningest.filen-3.net\r\ningest.filen-4.net\r\ningest.filen-5.net\r\ningest.filen-6.net\r\nHashes\r\nSecond stage DLL\r\n2338f420d66ef191c5a419353da2c12b\r\n766a89de96c50df2e33b42f05218c22e\r\n8cb79686725831395879227658c0dd5f\r\nd802290cb9e5c3fed1ba1a8daf827882\r\n72c90b34fc75b251df525258c543be11\r\n8169a4e2e826d82b57cc98bc71ea6d7e\r\n2cd2bd837e2a2554c9c34a1564388e0b\r\nHTTP Grunt Stager module of Covenant\r\nAs this module is executed in-memory, this list is not exhaustive.\r\nf442db1753a7475842607307a439870e\r\n8edc3c4868f2ef688c5250119c8aa6bb\r\nC60991effda994e4168ec2a63406cd6a\r\n8f916b6661e013ffbf318ed78e24a7c2\r\nBeardShell\r\nd802290cb9e5c3fed1ba1a8daf827882\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 23 of 35\n\nSlimAgent\r\n889b83d375a0fb00670af5276816080e (source: CERT-UA)\r\nMiscellaneous\r\nbd76f54d26bf00686da42f3664e3f2ae sample-03.wav (source: CERT-UA)\r\n5ddc34c5a9a2a1dc97c79d8777d54f14 windows.png\r\n50199e69c6a23ce935267be72372de0a windows.png\r\nb52c71318815836126f1257a180a74e7 windows.png\r\nbef42c5c079fe43c8353b24c607d9e4d Koala.png\r\n82bb741aa37df26772188643bd7b3c84 Default.png\r\nYARA\r\nrule APT_APT28_phantomnetvoxel_BeardShell: STABLE {\r\n meta:\r\n malware = \"BeardShell\"\r\n intrusion_set = \"APT28\"\r\n source = \"Sekoia.io\"\r\n creation_date = \"2025-02-25\"\r\n classification = \"TLP:GREEN\"\r\n hash = \"5d938b4316421a2caf7e2e0121b36459\"\r\n strings:\r\n $rtti1 = \"@Pwrshl\"\r\n $rtti2 = \"$WinHttpWrapper@\"\r\n $CLSID_CorRuntimeHost = {23 67 2F CB 3A AB D2 11 9C 40 00 C0 4F A3 0A 3E}\r\n $NetWkstaUserGetInfo = \"NetWkstaUserGetInfo\"\r\n $GetCurrentHwProfileW = \"GetCurrentHwProfileW\"\r\n $XOR_decryption = {50 88 54 24 07 88 4C 24 06 0F B6 44 24 06 0F B6 4C 24 07 31 C8 59 c3}\r\n \r\n condition:\r\n uint16be(0) == 0x4d5a and all of them and filesize \u003c 4MB\r\n}\r\nrule APT_APT28_phantomnetvoxel_ModifiedGruntStager: RESEARCH {\r\n meta:\r\n description = \"Test rule to detect NET stage based on the decryption routine. Maybe need to add some conditions\"\r\n source = \"Sekoia.io\"\r\n creation_date = \"2025-05-22\"\r\n classification = \"TLP:WHITE\"\r\n hash = \"f442db1753a7475842607307a439870e\"\r\n hash = \"c60991effda994e4168ec2a63406cd6a\"\r\n hash = \"8edc3c4868f2ef688c5250119c8aa6bb\"\r\n strings:\r\n // loop used to decrypt strings.\r\n $ = {\r\n 73 ?? ?? ?? 0A\r\n 0A\r\n 16\r\n 0b\r\n 2B 25\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 24 of 35\n\n06\r\n02\r\n07\r\n91\r\n7E ?? ?? ?? 04\r\n07\r\n7E ?? ?? ?? 04\r\n6F ?? ?? ?? 0A\r\n5D\r\n6F ?? ?? ?? 0A\r\n61\r\nD2\r\n6F ?? ?? ?? 0A\r\n07\r\n 17\r\n 58\r\n 0B\r\n 07\r\n 02\r\n 8e\r\n 69\r\n 32 d5\r\n 06\r\n 6f ?? ?? ?? 0A\r\n 2A\r\n }\r\n condition:\r\n uint16be(0) == 0x4d5a and\r\n all of them\r\n}\r\nrule APT_APT28_phantomnetvoxel_SecondStageDLL_Steganography : HUNTING {\r\n meta:\r\n intrusion_set = \"APT28\"\r\n description = \"Detects DLL based on the loop that extracts encrypted payloads from pixels\"\r\n source = \"Sekoia.io\"\r\n creation_date = \"2025-08-11\"\r\n classification = \"TLP:WHITE\"\r\n hash = \"2338f420d66ef191c5a419353da2c12b\"\r\n hash = \"766a89de96c50df2e33b42f05218c22e\"\r\n hash = \"8cb79686725831395879227658c0dd5f\"\r\n hash = \"d802290cb9e5c3fed1ba1a8daf827882\"\r\n hash = \"72c90b34fc75b251df525258c543be11\"\r\n hash = \"8169a4e2e826d82b57cc98bc71ea6d7e\"\r\n strings:\r\n /*\r\n0x180004dc4 41FFC1 inc r9d\r\n0x180004dc7 4183F908 cmp r9d, 8\r\n0x180004dcb 7CC3 jl 0x180004d90\r\n0x180004dcd 440FB64B01 movzx r9d, byte ptr [rbx + 1]\r\n0x180004dd2 4533D2 xor r10d, r10d\r\n0x180004dd5 6666660F1F840000000000 nop word ptr [rax + rax]\r\n */\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 25 of 35\n\n$ = {41 FF C1 41 83 F9 08 7C ?? 44 0F B6 4B ?? 45 33 D2 66 66 66 0F 1F 84 00}\r\n $ = \"DllRegisterServer\"\r\n condition:\r\n uint16be(0) == 0x4d5a and filesize \u003c 750KB and\r\n all of them\r\n}\r\nrule APT_APT28_phantomnetvoxel_Beardshell_Encrypted_communications : HUNTING {\r\n meta:\r\n malware = \"Beardshell\"\r\n intrusion_set = \"ATP28\"\r\n description = \"Detects format of uploaded/downloaded file of Beardshell\"\r\n source = \"Sekoia.io\"\r\n creation_date = \"2025-07-23\"\r\n classification = \"TLP:WHITE\"\r\n strings:\r\n $TIFFHeader = {49 49 2A 00}\r\n $BMPHeader = {42 4D}\r\n $GIFHeader = {47 49 46 38 39 61}\r\n $GIFFooter = {00 3B}\r\n $JPEGHeader = {FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00}\r\n $JPEGFooter = {FF D9}\r\n$PNGHeader = {89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52}\r\n $PNGFooter = {00 00 00 00 49 45 4e 44 ae 42 60 82}\r\n \r\n condition:\r\n ($TIFFHeader at 0 and uint32(0x4) == filesize-4)\r\n or\r\n ($BMPHeader at 0 and uint32(0x2) == filesize-2)\r\n or\r\n ($GIFHeader at 0 and $GIFFooter at filesize-2 and uint32(0x6) == filesize-8)\r\n or\r\n ($JPEGHeader at 0 and $JPEGFooter at filesize-2 and uint32(0xf) == filesize-0x11)\r\n or\r\n ($PNGHeader at 0 and $PNGFooter at filesize-0xc and uint32(0x10) == filesize-0x1C)\r\n}\r\nrule APT_APT28_phantomnetvoxel_malicious_vba_thisdocument_variant1_win : HUNTING {\r\n meta:\r\n intrusion_set = \"APT28\"\r\n description = \"These macro are used in a lure document that drop a DLL and a PNG file once executed. The PNG conce\r\n source = \"Sekoia.io\"\r\n creation_date = \"2025-07-24\"\r\n classification = \"TLP:WHITE\"\r\n // sha256 of doc used\r\n hash = \"41e116d1ee5c60dd31c2d15415f513e6c3807ca16630f7185cbb9e6a0cdbf592\"\r\n hash = \"89684b10d5eaa8d5c09c5a72c621acb6f8d107d5746d44bb7e9462ec6e4cf758\"\r\n hash = \"a610e249e3987103ebdb66ecf8198903afca93b1dcaf077fdecf80f371e9842d\"\r\n hash = \"ab5638089c42c6154345016962950dab8cf3092a23d5bf26ca051bbeaae8ea53\"\r\n hash = \"d040ce57289c3bd96b53a21cf77e411fe78b041606fb757e44d1da008a6296d7\"\r\n hash = \"f54af3cbf646ef363ab6b4663afd70a34e098233237d3d09bc6581342d1d0b38\"\r\n strings:\r\n $ = \"Private Sub Document_Open()\" nocase ascii\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 26 of 35\n\n$ = \"GetImageResolution(StrPtr(\" nocase ascii\r\n $ = \"SearchShape(\" nocase ascii\r\n $ = \"syswow64 = VBA.Environ(\" nocase ascii\r\n $ = \"GetImageResolution(StrPtr(\" nocase ascii\r\n $ = \"CreateImage(\" nocase ascii\r\n $ = \"GetRGBA(\" nocase ascii\r\n $ = \"Call getPixel(\" nocase ascii\r\n $ = \"DeleteImage(StrPtr(\" nocase ascii\r\n $ = \"ReadImage(\" nocase ascii\r\n $ = \"GetImageSize(\" nocase ascii\r\n $ = \"Unblur\" nocase ascii\r\n \r\n condition:\r\n all of them\r\n and filesize \u003e 15KB and filesize \u003c 40KB\r\n}\r\nrule APT_APT28_phantomnetvoxel_malicious_vba_module1_variant2_win : HUNTING {\r\n meta:\r\n intrusion_set = \"APT28\"\r\n description = \"These macro are used in a lure document that drop a DLL and a PNG file once executed. The PNG conce\r\n source = \"Sekoia.io\"\r\n creation_date = \"2025-07-24\"\r\n classification = \"TLP:WHITE\"\r\n // sha256 of doc used\r\n hash = \"8f049b3a100747167eb87fb3a134e446d9057f179b4f334a5a4006369605095a\"\r\n hash = \"20987f7163c8fe466930ece075cd051273530dfcbe8893600fd21fcfb58b5b08\"\r\n hash = \"57253f322504e0a8256d46f31c19e228b8c55a14ee18e759936c71941c8ee4ad\"\r\n strings:\r\n $ = \"CreateObject(\\\"ADODB.Stream\\\")\" nocase ascii\r\n $ = \".Open\" ascii\r\n $ = \".Type\" ascii\r\n $ = \".LoadFromFile\" ascii\r\n $ = \".Close\" ascii\r\n $ = \"\u0026H4D\" ascii\r\n $ = \"\u0026H5A\" ascii\r\n $ = \"\u0026H90\" ascii\r\n $ = \"VBA.LenB(\" ascii\r\n condition:\r\n filesize \u003c 5KB\r\n and all of them\r\n}\r\nrule APT_APT28_phantomnetvoxel_malicious_vba_module_variant1_win : HUNTING {\r\n meta:\r\n intrusion_set = \"APT28\"\r\n description = \"These macro are used in a lure document that drop a DLL and a PNG file once executed. The PNG conce\r\n source = \"Sekoia.io\"\r\n creation_date = \"2025-07-24\"\r\n classification = \"TLP:WHITE\"\r\n // sha256 of doc used\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 27 of 35\n\nhash = \"41e116d1ee5c60dd31c2d15415f513e6c3807ca16630f7185cbb9e6a0cdbf592\"\r\n hash = \"89684b10d5eaa8d5c09c5a72c621acb6f8d107d5746d44bb7e9462ec6e4cf758\"\r\n hash = \"a610e249e3987103ebdb66ecf8198903afca93b1dcaf077fdecf80f371e9842d\"\r\n hash = \"ab5638089c42c6154345016962950dab8cf3092a23d5bf26ca051bbeaae8ea53\"\r\n hash = \"d040ce57289c3bd96b53a21cf77e411fe78b041606fb757e44d1da008a6296d7\"\r\n hash = \"f54af3cbf646ef363ab6b4663afd70a34e098233237d3d09bc6581342d1d0b38\"\r\n strings:\r\n $declare1 = \"Public Declare PtrSafe Function\" nocase ascii\r\n $declare2 = \"Public Declare Function\" nocase ascii\r\n $libkernel32 = \"Lib \\\"kernel32\\\" Alias \\\"\" nocase ascii\r\n $function1 = \"CreateImage\" nocase ascii\r\n $function2 = \"ReadImag\" nocase ascii\r\n $function3 = \"GetImageSize\" nocase ascii\r\n $function4 = \"WriteImage\" nocase ascii\r\n $function5 = \"CloseImage\" nocase ascii\r\n $function6 = \"WaitForImage\" nocase ascii\r\n $function7 = \"DeleteImage\" nocase ascii\r\n $function8 = \"GetImageResolution\" nocase ascii\r\n $alias1 = \"CreateProcessW\" nocase ascii\r\n $alias2 = \"ReadFile\" nocase ascii\r\n $alias3 = \"GetFileSize\" nocase ascii\r\n $alias4 = \"WriteFile\" nocase ascii\r\n $alias5 = \"CloseHandle\" nocase ascii\r\n $alias6 = \"Sleep\" nocase ascii\r\n $alias7 = \"CreateFileW\" nocase ascii\r\n $alias8 = \"GetFileAttributesW\" nocase ascii\r\n condition:\r\n filesize \u003c 10KB\r\n and ($declare1 or $declare2)\r\n and $libkernel32\r\n and 4 of ($function*)\r\n and 4 of ($alias*)\r\n}\r\nrule APT_APT28_phantomnetvoxel_malicious_vba_thisdocument_variant2_win : HUNTING {\r\n meta:\r\n intrusion_set = \"APT28\"\r\n description = \"These macro are used in a lure document that drop a DLL and a PNG file once executed. The PNG conce\r\n source = \"Sekoia.io\"\r\n creation_date = \"2025-07-24\"\r\n classification = \"TLP:WHITE\"\r\n // sha256 of doc used\r\n hash = \"20987f7163c8fe466930ece075cd051273530dfcbe8893600fd21fcfb58b5b08\"\r\n hash = \"57253f322504e0a8256d46f31c19e228b8c55a14ee18e759936c71941c8ee4ad\"\r\n strings:\r\n $ = \"Private Sub Document_Open()\" nocase ascii\r\n $ = \"Private Sub Document_Close()\" nocase ascii\r\n $ = \"ThisDocument.ActiveWindow.View.Type\" nocase ascii\r\n $ = \"ThisDocument.name\" nocase ascii\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 28 of 35\n\n$ = \"VBA.Environ(\" nocase ascii\r\n $ = \"FileExists(\" nocase ascii\r\n $ = \"CreateFolder\" nocase ascii\r\n $ = \".CopyFile\" nocase ascii\r\n $ = \"\\\"Temp\\\"\" nocase ascii\r\n condition:\r\n all of them\r\n and filesize \u003c 3KB\r\n}\r\nrule APT_APT28_phantomnetvoxel_malicious_vba_module2_variant2_win : HUNTING {\r\n meta:\r\n intrusion_set = \"APT28\"\r\n description = \"These macro are used in a lure document that drop a DLL and a PNG file once executed. The PNG conce\r\n source = \"Sekoia.io\"\r\n creation_date = \"2025-07-24\"\r\n classification = \"TLP:WHITE\"\r\n // sha256 of doc used\r\n hash = \"8f049b3a100747167eb87fb3a134e446d9057f179b4f334a5a4006369605095a\"\r\n hash = \"20987f7163c8fe466930ece075cd051273530dfcbe8893600fd21fcfb58b5b08\"\r\n hash = \"57253f322504e0a8256d46f31c19e228b8c55a14ee18e759936c71941c8ee4ad\"\r\n strings:\r\n $declare1 = \"Public Declare PtrSafe Function\" nocase ascii\r\n $declare2 = \"Public Declare Function\" nocase ascii\r\n $lib1 = \"Lib \\\"kernel32\\\" Alias \\\"\" nocase ascii\r\n $lib2 = \"Lib \\\"advapi32.dll\\\" Alias \\\"\" nocase ascii\r\n $function1 = \"CP\" nocase ascii\r\n $function2 = \"WFSO\" nocase ascii\r\n $function3 = \"IsU\" nocase ascii\r\n $function4 = \"RCKE\" nocase ascii\r\n $function5 = \"RSVE\" nocase ascii\r\n $function6 = \"RCK\" nocase ascii\r\n $alias1 = \"CreateProcessW\" nocase ascii\r\n $alias2 = \"WaitForSingleObject\" nocase ascii\r\n $alias3 = \"IsUserAnAdmin\" nocase ascii\r\n $alias4 = \"RegCreateKeyExW\" nocase ascii\r\n $alias5 = \"RegSetValueExW\" nocase ascii\r\n $alias6 = \"RegCloseKey\" nocase ascii\r\n \r\n condition:\r\n filesize \u003c 10KB\r\n and ($declare1 or $declare2)\r\n and ($lib1 or $lib2)\r\n and 3 of ($function*)\r\n and 3 of ($alias*)\r\n}\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 29 of 35\n\nPython scripts\r\nimport png\r\nimport binascii\r\nfrom Crypto.Cipher import AES\r\n\"\"\"\r\nScript used to extract the shellcode or .NET stage from the PNG file\r\n`pip install pycryptodome pypng`\r\n\"\"\"\r\nDEBUG = False\r\nKEY_SIZE = 32\r\nIV_SIZE = 16\r\nHASH_SIZE = 20\r\ndef process_file(name: str = \"windows.png\") -\u003e None:\r\n r=png.Reader(name)\r\n (width, height, gen, config) = r.read()\r\n l = list(gen)\r\n \r\n size = compute_size(l)\r\n if DEBUG:\r\n print(\"Size of data in {} : {}\".format(name, hex(size)))\r\n \r\n \r\n payload = extract_data(l, size, 32)\r\n \r\n # here, sha1(payload[:-20]) should be equal to payload[-20:]\r\n # But it doesn't matter.\r\n KEY = payload[:KEY_SIZE]\r\n IV = payload[-(IV_SIZE+HASH_SIZE):-HASH_SIZE]\r\n sha1 = payload[-HASH_SIZE:]\r\n cipher_text = payload[KEY_SIZE:-(IV_SIZE+HASH_SIZE)]\r\n \r\n if DEBUG:\r\n print(binascii.hexlify(cipher_text))\r\n print(binascii.hexlify(KEY))\r\n print(binascii.hexlify(IV))\r\n print(binascii.hexlify(sha1))\r\n \r\n cipher = AES.new(KEY, AES.MODE_CBC, iv=IV)\r\n plaintext = cipher.decrypt(cipher_text)\r\n \r\n # This part is used to only recover the PE.\r\n # Comment this line to recover the whole shellcode\r\n idx = plaintext.find(b\"MZ\")\r\n if idx \u003e= 0:\r\n stage2_name = name + \".extracted_stage2\"\r\n f = open(stage2_name, \"bw\")\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 30 of 35\n\nf.write(plaintext[0:])\r\n f.close()\r\n print(\"Next stage found. Written to {}.extracted_stage2\".format(name))\r\n else:\r\n print(\"Error while trying to find the next stage\")\r\n \r\ndef extract_data(l: list[int], size: int, offset: int)-\u003e bytes:\r\n k = 0\r\n current_byte = 0\r\n payload = b\"\"\r\n index = 0\r\n while index \u003c= size*8:\r\n j = (offset+index) % len(l[0])\r\n i = (offset+index) // len(l[0])\r\n \r\n if k == 8:\r\n payload += current_byte.to_bytes()\r\n current_byte = 0\r\n k = 0\r\n \r\n ec = l[i][j] % 2\r\n current_byte += (ec\u003c\u003ck)\r\n k += 1\r\n index += 1\r\n \r\n return payload\r\n \r\ndef compute_size(l: list[int]) -\u003e int:\r\n size = 0\r\n for i in range(0, 32):\r\n size *=2\r\n size += l[0][31-i]%2\r\n return size\r\nif __name__ == \"__main__\":\r\n # list of png that contains the next stage.\r\n names = [\"windows1.png\", \"windows2.png\", \"windows3.png\", \"Koala.png\"]\r\n for name in names:\r\n process_file(name)\r\n#!/usr/bin/env python3\r\nimport os\r\nimport struct\r\nimport argparse\r\nfrom Crypto.Cipher import ChaCha20_Poly1305\r\n\"\"\"\r\nScript used to generate Beardshell commands as tiff file\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 31 of 35\n\n`pip install pycryptodome`\r\nExamples for the 'command' paramater :\r\n '{\"task_id\": 0, \"cmd_id\":1, \"data\": {}}'\r\n '{\"task_id\": 0, \"cmd_id\":2, \"data\": {\"id\":0,\"cmd\":\"SystemInfo\"}}'\r\n '{\"task_id\": 0, \"cmd_id\":3, \"data\": {\"id\":0,\"script\":\"U3lzdGVtSW5mbw==\"}}'\r\n '{\"task_id\": 0, \"cmd_id\":4, \"data\": {\"id\":0}}'\r\n '{\"task_id\": 0, \"cmd_id\":5, \"data\": {\"id\":0}}'\r\n '{\"task_id\": 0, \"cmd_id\":6, \"data\": {}}'\r\n '{\"task_id\": 0, \"cmd_id\":6, \"data\": {}}'\r\nNB: It is recommended to update the task_id.\r\nFor example, Beardshell returns nothing if a command 1 is emited with a task_id already used\r\n(but the command succeeds)\r\n\"\"\"\r\n# This script only generates tiff files\r\nHEADERS = {\r\n \"tiff\": b\"II*\\x00\",\r\n}\r\ndef encrypt_and_dump(plaintext: bytes, key: bytes, ext: str, out_path: str) -\u003e None:\r\n ext = ext.lower()\r\n if ext not in HEADERS:\r\n raise ValueError(\"Ext not supported. Only 'tiff' is supported here\")\r\n header = HEADERS[ext]\r\n nonce = os.urandom(12)\r\n # Arbitrary choosen AAD of 0x10 bytes (it must be in [0x0A,0x1E])\r\n aad = b\"A\" * 16\r\n cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)\r\n cipher.update(aad)\r\n ciphertext, tag = cipher.encrypt_and_digest(plaintext)\r\n # Compute \"data_size\"\r\n # = nonce (12) + tag (16) + 4 (size of aad size) + len(aad) + len(ciphertext)\r\n data_size = 12 + 16 + 4 + len(aad) + len(ciphertext)\r\n with open(out_path, \"wb\") as f:\r\n f.write(header)\r\n f.write(struct.pack(\"\u003cI\", data_size+4))\r\n f.write(nonce)\r\n f.write(tag)\r\n f.write(struct.pack(\"\u003cI\", len(aad)))\r\n f.write(aad)\r\n f.write(ciphertext)\r\n print(f\"[+] File generated '{out_path}'\")\r\ndef main() -\u003e None:\r\n p = argparse.ArgumentParser(\r\n description=\"Script used to create a Beardshell command file\"\r\n )\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 32 of 35\n\np.add_argument(\"command\", help=\"Plaintext command like '{\\\"task_id\\\": 1, \\\"cmd_id\\\":1, \\\"data\\\": {}}'\")\r\n p.add_argument(\"output\", help=\"Output filename\")\r\n args = p.parse_args()\r\n key = bytes.fromhex(\"F9685510DD90C05856950D86C12CF7A2CC9D148AACC187DDDDFCE0C9EDAE6EE3\")\r\n ext = \"tiff\"\r\n if len(key) != 32:\r\n p.error(\"Keysize must be 32 bytes (64 hex chars)\")\r\n pt = args.command.encode(\"utf-8\")\r\n encrypt_and_dump(pt, key, ext, args.output)\r\nif __name__ == \"__main__\":\r\n main()\r\nimport struct\r\nimport binascii\r\nimport argparse\r\nfrom Crypto.Cipher import ChaCha20_Poly1305\r\n\"\"\"\r\nScript used to decrypt Beardshell response files\r\n`pip intall pycryptodome`\r\npython3 decrypt.py \u003cfilename\u003e\r\nThis script automatically parses and decrypts the file based on the extension name.\r\n\"\"\"\r\nDEBUG = False\r\ndef decrypt_file(path_in: str, key: bytes, ext: str) -\u003e bytes:\r\n ext = ext.lower()\r\n if ext == \"png\":\r\n magic_len = 0x10\r\n elif ext == \"tiff\":\r\n magic_len = 4\r\n elif ext == \"gif\":\r\n magic_len = 6\r\n elif ext == \"bmp\":\r\n magic_len = 2\r\n elif ext == \"jpeg\":\r\n magic_len = 0xf\r\n else:\r\n raise ValueError(f\"Extension not supported: {ext}\")\r\n with open(path_in, \"rb\") as f:\r\n # skip the header\r\n f.seek(magic_len)\r\n # read the size of the cipher bloc\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 33 of 35\n\ndata_size = struct.unpack(\"\u003cI\", f.read(4))[0]-4\r\n if DEBUG:\r\n print(\"DataSize: {}\".format(hex(data_size)))\r\n # nonce ChaCha20-Poly1305 = 12 bytes\r\n nonce = f.read(12)\r\n if DEBUG:\r\n print(binascii.hexlify(nonce))\r\n \r\n # Poly1305 tag = 16 bytes\r\n tag = f.read(16)\r\n if DEBUG:\r\n print(binascii.hexlify(tag))\r\n # AAD len ([0xA:0x1E])\r\n aad_len = struct.unpack(\"\u003cI\", f.read(4))[0]\r\n if not (0x0A \u003c= aad_len \u003c= 0x1E):\r\n raise ValueError(f\"Invalid AAD length: {aad_len}\")\r\n if DEBUG:\r\n print(hex(aad_len))\r\n aad = f.read(aad_len)\r\n # We can compute the overall size of the header\r\n header_size = 12 + 16 + 4 + aad_len\r\n ciph_len = data_size - header_size\r\n ciphertext = f.read(ciph_len)\r\n # Decryption and tag verification\r\n cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)\r\n cipher.update(aad)\r\n plaintext = cipher.decrypt_and_verify(ciphertext, tag)\r\n return plaintext\r\ndef main() -\u003e None:\r\n p = argparse.ArgumentParser(description=\"Decrypts a beardshell file with ChaCha20-Poly1305\")\r\n p.add_argument(\"input\", help=\"Input filename\")\r\n args = p.parse_args()\r\n key = bytes.fromhex(\"F9685510DD90C05856950D86C12CF7A2CC9D148AACC187DDDDFCE0C9EDAE6EE3\")\r\n if len(key) != 32:\r\n p.error(\"Key size must be 32 bytes (64 hex chars).\")\r\n if args.input.endswith(\"tiff\"):\r\n ext = \"tiff\"\r\n elif args.input.endswith(\"bmp\"):\r\n ext= \"bmp\"\r\n elif args.input.endswith(\"jpeg\"):\r\n ext= \"jpeg\"\r\n elif args.input.endswith(\"png\"):\r\n ext= \"png\"\r\n elif args.input.endswith(\"gif\"):\r\n ext= \"gif\"\r\n else:\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 34 of 35\n\np.error(\"Extension not recognized\")\r\n try:\r\n pt = decrypt_file(args.input, key, ext)\r\n except Exception as e:\r\n print(\"Error during decryption:\", e)\r\n exit(1)\r\n print(pt)\r\nif __name__ == \"__main__\":\r\n main()\r\nShare\r\nAPT APT28 CTI Espionage GRU Malware PhantomNetVoxel russia ukraine\r\nShare this post:\r\nSource: https://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nhttps://blog.sekoia.io/apt28-operation-phantom-net-voxel/\r\nPage 35 of 35",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://blog.sekoia.io/apt28-operation-phantom-net-voxel/"
	],
	"report_names": [
		"apt28-operation-phantom-net-voxel"
	],
	"threat_actors": [
		{
			"id": "b98eb1ec-dc8b-4aea-b112-9e485408dd14",
			"created_at": "2022-10-25T16:07:23.649308Z",
			"updated_at": "2026-04-10T02:00:04.701157Z",
			"deleted_at": null,
			"main_name": "FunnyDream",
			"aliases": [
				"Bronze Edgewood",
				"Red Hariasa",
				"TAG-16"
			],
			"source_name": "ETDA:FunnyDream",
			"tools": [
				"Chinoxy",
				"Filepak",
				"FilepakMonitor",
				"FunnyDream",
				"Keyrecord",
				"LOLBAS",
				"LOLBins",
				"Living off the Land",
				"Md_client",
				"PCShare",
				"ScreenCap",
				"TcpBridge",
				"Tcp_transfer",
				"ccf32"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "d0d996a0-98e2-49fd-b55e-97ba053c4ed0",
			"created_at": "2024-07-25T02:00:04.423466Z",
			"updated_at": "2026-04-10T02:00:03.679863Z",
			"deleted_at": null,
			"main_name": "UAC-0063",
			"aliases": [],
			"source_name": "MISPGALAXY:UAC-0063",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "730dfa6e-572d-473c-9267-ea1597d1a42b",
			"created_at": "2023-01-06T13:46:38.389985Z",
			"updated_at": "2026-04-10T02:00:02.954105Z",
			"deleted_at": null,
			"main_name": "APT28",
			"aliases": [
				"Pawn Storm",
				"ATK5",
				"Fighting Ursa",
				"Blue Athena",
				"TA422",
				"T-APT-12",
				"APT-C-20",
				"UAC-0001",
				"IRON TWILIGHT",
				"SIG40",
				"UAC-0028",
				"Sofacy",
				"BlueDelta",
				"Fancy Bear",
				"GruesomeLarch",
				"Group 74",
				"ITG05",
				"FROZENLAKE",
				"Forest Blizzard",
				"FANCY BEAR",
				"Sednit",
				"SNAKEMACKEREL",
				"Tsar Team",
				"TG-4127",
				"STRONTIUM",
				"Grizzly Steppe",
				"G0007"
			],
			"source_name": "MISPGALAXY:APT28",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "e3767160-695d-4360-8b2e-d5274db3f7cd",
			"created_at": "2022-10-25T16:47:55.914348Z",
			"updated_at": "2026-04-10T02:00:03.610018Z",
			"deleted_at": null,
			"main_name": "IRON TWILIGHT",
			"aliases": [
				"APT28 ",
				"ATK5 ",
				"Blue Athena ",
				"BlueDelta ",
				"FROZENLAKE ",
				"Fancy Bear ",
				"Fighting Ursa ",
				"Forest Blizzard ",
				"GRAPHITE ",
				"Group 74 ",
				"PawnStorm ",
				"STRONTIUM ",
				"Sednit ",
				"Snakemackerel ",
				"Sofacy ",
				"TA422 ",
				"TG-4127 ",
				"Tsar Team ",
				"UAC-0001 "
			],
			"source_name": "Secureworks:IRON TWILIGHT",
			"tools": [
				"Downdelph",
				"EVILTOSS",
				"SEDUPLOADER",
				"SHARPFRONT"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "ae320ed7-9a63-42ed-944b-44ada7313495",
			"created_at": "2022-10-25T15:50:23.671663Z",
			"updated_at": "2026-04-10T02:00:05.283292Z",
			"deleted_at": null,
			"main_name": "APT28",
			"aliases": [
				"APT28",
				"IRON TWILIGHT",
				"SNAKEMACKEREL",
				"Group 74",
				"Sednit",
				"Sofacy",
				"Pawn Storm",
				"Fancy Bear",
				"STRONTIUM",
				"Tsar Team",
				"Threat Group-4127",
				"TG-4127",
				"Forest Blizzard",
				"FROZENLAKE",
				"GruesomeLarch"
			],
			"source_name": "MITRE:APT28",
			"tools": [
				"Wevtutil",
				"certutil",
				"Forfiles",
				"DealersChoice",
				"Mimikatz",
				"ADVSTORESHELL",
				"Komplex",
				"HIDEDRV",
				"JHUHUGIT",
				"Koadic",
				"Winexe",
				"cipher.exe",
				"XTunnel",
				"Drovorub",
				"CORESHELL",
				"OLDBAIT",
				"Downdelph",
				"XAgentOSX",
				"USBStealer",
				"Zebrocy",
				"reGeorg",
				"Fysbis",
				"LoJax"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "d2516b8e-e74f-490d-8a15-43ad6763c7ab",
			"created_at": "2022-10-25T16:07:24.212584Z",
			"updated_at": "2026-04-10T02:00:04.900038Z",
			"deleted_at": null,
			"main_name": "Sofacy",
			"aliases": [
				"APT 28",
				"ATK 5",
				"Blue Athena",
				"BlueDelta",
				"FROZENLAKE",
				"Fancy Bear",
				"Fighting Ursa",
				"Forest Blizzard",
				"G0007",
				"Grey-Cloud",
				"Grizzly Steppe",
				"Group 74",
				"GruesomeLarch",
				"ITG05",
				"Iron Twilight",
				"Operation DealersChoice",
				"Operation Dear Joohn",
				"Operation Komplex",
				"Operation Pawn Storm",
				"Operation RoundPress",
				"Operation Russian Doll",
				"Operation Steal-It",
				"Pawn Storm",
				"SIG40",
				"Sednit",
				"Snakemackerel",
				"Sofacy",
				"Strontium",
				"T-APT-12",
				"TA422",
				"TAG-0700",
				"TAG-110",
				"TG-4127",
				"Tsar Team",
				"UAC-0028",
				"UAC-0063"
			],
			"source_name": "ETDA:Sofacy",
			"tools": [
				"ADVSTORESHELL",
				"AZZY",
				"Backdoor.SofacyX",
				"CHERRYSPY",
				"CORESHELL",
				"Carberp",
				"Computrace",
				"DealersChoice",
				"Delphacy",
				"Downdelph",
				"Downrage",
				"Drovorub",
				"EVILTOSS",
				"Foozer",
				"GAMEFISH",
				"GooseEgg",
				"Graphite",
				"HATVIBE",
				"HIDEDRV",
				"Headlace",
				"Impacket",
				"JHUHUGIT",
				"JKEYSKW",
				"Koadic",
				"Komplex",
				"LOLBAS",
				"LOLBins",
				"Living off the Land",
				"LoJack",
				"LoJax",
				"MASEPIE",
				"Mimikatz",
				"NETUI",
				"Nimcy",
				"OCEANMAP",
				"OLDBAIT",
				"PocoDown",
				"PocoDownloader",
				"Popr-d30",
				"ProcDump",
				"PythocyDbg",
				"SMBExec",
				"SOURFACE",
				"SPLM",
				"STEELHOOK",
				"Sasfis",
				"Sedkit",
				"Sednit",
				"Sedreco",
				"Seduploader",
				"Shunnael",
				"SkinnyBoy",
				"Sofacy",
				"SofacyCarberp",
				"SpiderLabs Responder",
				"Trojan.Shunnael",
				"Trojan.Sofacy",
				"USB Stealer",
				"USBStealer",
				"VPNFilter",
				"Win32/USBStealer",
				"WinIDS",
				"Winexe",
				"X-Agent",
				"X-Tunnel",
				"XAPS",
				"XTunnel",
				"Xagent",
				"Zebrocy",
				"Zekapab",
				"carberplike",
				"certutil",
				"certutil.exe",
				"fysbis",
				"webhp"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775433981,
	"ts_updated_at": 1775792269,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/2d9fd1da93d5cbf4810b0394889d9097ea1f5d1f.pdf",
		"text": "https://archive.orkl.eu/2d9fd1da93d5cbf4810b0394889d9097ea1f5d1f.txt",
		"img": "https://archive.orkl.eu/2d9fd1da93d5cbf4810b0394889d9097ea1f5d1f.jpg"
	}
}