{
	"id": "c07b5d1a-d16f-4ae3-bcb8-dffbe9fbf889",
	"created_at": "2026-04-06T00:12:50.962501Z",
	"updated_at": "2026-04-10T03:36:11.176664Z",
	"deleted_at": null,
	"sha1_hash": "14522467f54a5de81dc1265ad3f19cfb20ba7a3d",
	"title": "OysterLoader Unmasked: The Multi-Stage Evasion Loader",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 412697,
	"plain_text": "OysterLoader Unmasked: The Multi-Stage Evasion Loader\r\nBy Pierre Le Bourhis\r\nPublished: 2026-02-12 · Archived: 2026-04-05 15:27:35 UTC\r\nIntroduction\r\nOysterLoader, also known as Broomstick and CleanUp, is a malware developed in C++, composed of multiple stages,\r\nbelonging to the loader (A.k.a.: downloader) malware family. First reported in June 2024 by Rapid7, it is mainly distributed\r\nvia web sites impersonating legitimate software which are often IT software for instance: PuTTy, WinSCP, Google\r\nAuthenticator and Ai software. The loader is primarily employed in campaigns leading to Rhysida ransomware. \r\nAccording to Expel reports, OysterLoader is used by the Rhysida ransomware group which is closely associated with the\r\nWIZARD SPIDER nebula. Besides, the loader is also used to distribute commodity malware such as Vidar, the most\r\nwidespread infostealer by January 2026. According to Huntress, OysterLoader is also distributed via Gootloader. Based on\r\nour observations and other reports on this threat, it is unclear whether the malware is proprietary to Rhydida ransomware\r\ngroup and friends or sold as MaaS on private marketplaces.\r\nSince its apparition, the malware’s code has evolved, and analysis by various security vendors highlighted some regressions\r\nbetween its first and current versions, particularly in Command-and-Control (C2) content and in code obfuscation. \r\nMalware Analysis\r\nThe malware is distributed mainly through fake websites that copy legitimate software. It is disguised as a software installer\r\nand serves as a MicroSoft Installer (MSI). The MSI is often signed to appear benign. The infection chain is composed of\r\nfour stages:\r\n1. Stage 1 – Packer [TextShell]\r\n2. Stage 2 – Custom shellcode [TextShell]\r\n3. Stage 3 – Intermediate DLL acting as a downloader\r\n4. Stage 4 – OysterLoader core\r\nStage 1 – Obfuscator\r\nThe main function of the packer is to load in memory the next stage that is stored “shuffled”. According to Huntress‘ report\r\non Gootlader that mentions OysterLoader, the packer (also called obfuscator in the report) is another malware named\r\nTextShell.\r\nTo load it, it allocates a memory area with necessary permissions (Read, Write, Execute) and makes a raw copy of the data\r\nin the newly allocated memory. The copy is made in a bunch of eight bytes. Besides, the code of the initial stage is full of\r\nuseless API calls to legitimate DLL, the objectives being to avoid execution in particular environments. The packer also\r\nembedded a simple anti-debug trap that is present multiple times in the packer. \r\nBesides, the first stage employs common techniques such API hammering and dynamic API resolution.\r\nLegitimate API calls flooding-hammering\r\nIn order to hide its malicious code, the loader attempts to make hundreds of calls to legitimate DLLs.  In malware, these\r\ncalls often serve no operational purpose. They don’t meaningfully change the environment. They look legitimate, though,\r\nand that’s the point. Their purposes are various:\r\nBreak heuristic detectors “lots of GDI calls – mimikate graphics tool).\r\nDistract reverse-engineers during static analysis.\r\nMislead sandboxes (some sandboxes don’t fully emulate GDI calls).\r\nIntroduce chaotic paths in decompiled code.\r\nThe malware code is encapsulated with legitimate calls, sometimes, only the prologue of the function is filled with DLLs\r\ncalls, sometimes the epilogue also contains these patterns.\r\nRevokeDragDrop(0);\r\nDC = GetDC(0);\r\nSolidBrush = CreateSolidBrush(0x75051u);\r\nUnrealizeObject(SolidBrush);\r\nSetMapMode(DC, 2);\r\nv2 = CreateSolidBrush(0xFACB9Fu);\r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 1 of 10\n\nUnrealizeObject(v2);\r\nSetCommBreak(0);\r\nptr_buff_struct_mem = allocate_mem;\r\n*(_DWORD *)\u0026allocate_mem-\u003eblob0[0x1AD5B] = 0xE539234E;\r\nOaBuildVersion();\r\nOaBuildVersion();\r\n*(_DWORD *)\u0026ptr_buff_struct_mem-\u003eblob0[0x1A0D8] = 0x2AD4A690;\r\nSetBkColor(DC, 0x6C072Au);\r\nSetMapMode(DC, 4);\r\nif ( IsDebuggerPresent() )\r\n{\r\nwhile ( 1 );\r\n}\r\nv4 = CreateSolidBrush(0x202417u);\r\nUnrealizeObject(v4);\r\n*(_QWORD *)\u0026ptr_buff_struct_mem-\u003eblob0[0x1A6B9] = 0x475717F412B7ABBCLL;\r\n*(_QWORD *)\u0026ptr_buff_struct_mem-\u003eblob0[0x1A6C1] = 0xBA8581BA8183F21BuLL;\r\n*(_QWORD *)\u0026ptr_buff_struct_mem-\u003eblob0[0x1A6C9] = 0x3A48C871C86971C8LL;\r\n...\r\nCode 1. Extract of decompiled code employing API flooding\r\nIn the above example, the only relevant code are (highlighted in blue):\r\n1. Accessing a global variable (here allocate_mem)\r\n2. Copying data at specifics offset ( DWORD and QWORD value)\r\nA more intentional anti-analysis mechanism is the IsDebuggerPresent() check; if a debugger is detected, the malware\r\nenters an infinite loop ( while(1); ), effectively freezing execution and preventing dynamic analysis. This kind of anti-debugging trick is far from being advanced—analysts can easily bypass it by patching the IsDebuggerPresent function in\r\nkernel32.dll, for example by replacing its epilogue with xor eax, eax; ret , forcing it to always report that no debugger is\r\nattached. Together, these techniques illustrate how malware authors pad their binaries with noise and simple anti-debugging\r\ntraps to slow down analysts and automated detection systems.\r\nTo ease the analysis of this stage, the following script was used to clean up calls to legitimate DLLs that are irrelevant\r\nregarding the malware behaviour.\r\nDynamic API resolution\r\nDynamic API resolution often implemented through custom hashing algorithms is a widespread technique in modern\r\nmalware, allowing threats to hide their real API dependencies and evade static detection. The packer is no exception: it relies\r\non dynamically imported APIs, but each sample uses a slightly different hashing algorithm, adding variability that\r\ncomplicates signature-based analysis. This lightweight yet effective obfuscation method remains a staple across many\r\nmalware families.\r\nFor instance, the first stage analysed in this report uses the following algorithm, which computes a hash by iterating over\r\neach character of an API name and updating a 32‑bit accumulator with the formula h = (h * 0x2001 + ord(ch)). The\r\nfollowing Python script can be used to identify the function corresponding to the hash in the sample.\r\nimport pefile\r\n# OysterLoader only needs ntdll and kernel32\r\ndlls_path = [\r\nr\"C:\\Windows\\System32\\kernel32.dll\",\r\nr\"C:\\Windows\\System32\\ntdll.dll\"\r\n]\r\nexports = []\r\nfor pe in map(lambda dll: pefile.PE(dll), dlls_path):\r\nfor exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:\r\n if exp.name:\r\n exports.append(exp.name.decode('utf-8'))\r\ndef hash_function(name: str) -\u003e int:\r\n h = 0\r\n for ch in name:\r\n h = (h * 0x2001 + ord(ch)) \u0026 0xFFFFFFFF\r\n return h\r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 2 of 10\n\nhashes = [0x9866A947, 0x895E0804, 0xEA1023BE, 0x8F1E88B1, 0x5CD5A5AA]\r\nfor h in hashes:\r\nfor name in exports:\r\n if hash_function(name) == h:\r\n print(f\"[+] Match found: {name} for hash: 0x{h:x}\")\r\n break\r\nCode 2. Python snippet to resolve the API function\r\nFigure 1. Output of the Python snippet used to resolve API hash\r\nInitial stage workflow\r\nThe main component of the first stage of the loader works as follows:\r\n1. Dynamically resolves the following function using a custom hashing algorithm NtAllocateVirtualMemory ,\r\nLdrGetDllHandle , RtlInitUnicodeString , LdrLoadDll , LdrGetProcedureAddress .\r\n2. Uses NtAllocateVirtualMemory to allocate memory with RWX permissions.\r\n3. Copies data into the allocated buffer.\r\n4. Dynamically resolves additional functions using uniquely the pair of functions from ntdll: LdrLoadDll ,\r\nLdrGetProcedureAddres . This pair is used to load the following functions: LoadLibrary , GetProcAddress ,\r\nExitProcess , VirtualProtect , exit , ShowWindow , InternetOpenW that will be necessary for next stages.\r\n5. Executes a specific fixed offset in the shellcode previously allocated.\r\nNote that the structure of this initial stage is pretty straightforward regardless of the numerous noisy DLL calls flooding the\r\nbinary.\r\n In this primary stage, the loader saves particular information in a dedicated structure that we named core for this analysis.\r\nThe core structure that will also be used by the next stage has this composition:\r\nstruct core\r\n{\r\n uint8_t compressed_data[136512];\r\n _QWORD entypoint;\r\n _BYTE config[6940];\r\n _QWORD LoadLibraryA;\r\n _QWORD ExitProcess;\r\n _QWORD GetProcAddress;\r\n _QWORD VirtualProtect;\r\n _QWORD unknown0;\r\n _QWORD exit;\r\n _BYTE flags[8];\r\n _QWORD ShowWindow;\r\n _QWORD unknown1;\r\n _QWORD InternetOpenW;\r\n};\r\nCode 3. Internal C structure of the stage 1 of OysterLoader\r\nStage 2 – Shellcode\r\nThe code executed at this stage is as shellcode, though extremely dependent on the previous stage as it requires access to the\r\ncore structure mainly to access the LoadLibrary and GetProcAddress couple to dynamically load another bunch of\r\nfunctions. It needs to have the offset of a blob of data (named compressed_data in the core structure). \r\nOnce executed, the stage-2 shellcode immediately transfers control to a bespoke LZMA decompression routine,\r\ncomprising a substantial part of the payload’s codebase. The implementation clearly mirrors the standard LZMA range\r\ndecoder: it maintains a large set of bit-probability models, performs the characteristic 11-bit probability updates, and\r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 3 of 10\n\ntransitions through the well-known LZMA state machine to distinguish literal bytes from matches and repeated match\r\nlengths. \r\nAfter reconstructing the original payload, the shellcode performs a pass of relocation fixups, scanning the decompressed\r\nbuffer for relative CALL (E8) and JMP (E9) opcodes and rewriting their offsets to absolute addresses-an essential\r\nadaptation for position-independent code running at unpredictable memory locations. With the payload’s code layout\r\nrecovered, the shellcode proceeds to resolve its imports dynamically by manually calling LoadLibraryA and\r\nGetProcAddress , populating an ad-hoc import table through function pointers stored in the staged data region. These\r\nresolved APIs are then used to adjust memory protections via VirtualProtect , clearing non-executable bits and\r\ntransitioning the decompressed region to an executable state. Finally, with all dependencies resolved and memory prepared,\r\nthe shellcode transfers control to the newly reconstructed payload, invoking its entry point and continuing execution into the\r\nnext stage of the malware.\r\nFigure 2. OysterLoader stage 2 shellcode graph overview\r\nA custom Python script has been used to inflate the LZMA compressed buffer; a script is available on this Gist.\r\nOysterLoader’s use of a custom LZMA implementation rather than standard compression tools serves multiple strategic\r\npurposes that are characteristic of good malware development skills. While the compression parameters (lc=3, lp=0, pb=2)\r\nremain standard for optimal performance, the custom header format and modified bitstream prevent automated analysis by\r\ncommon tools like 7-Zip, xz-utils, or Python’s lzma module. \r\nThe custom header structure (storing properties at specific blob offsets rather than using the standard 13-byte LZMA header)\r\nalso serves an evasion purpose – signature-based detection systems looking for standard LZMA magic bytes (0x5D for\r\nproperties, or .xz/.lzma file signatures) will fail to identify this as compressed data. Additionally, the non-standard bitstream\r\nformat means that even with a reconstructed proper LZMA header with correct parameters, standard decompression will fail\r\ndue to subtle modifications in the probability models or range decoder implementation. \r\nOnce decompressed, the shellcode also copies some addresses (for instance DLL functions that were loaded in the previous\r\nstage) and that will be used by stage 3. \r\nStage 3 – Downloader\r\nThis third stage is composed of a set of common functions going from language verification, keyboard layout identification\r\nto anti-debug, assembly un-alignment to trick decompiler. However, this is the pre-final stage, it acts as an environment test,\r\nthis is also the first time the malware communicates with a server. \r\nStage 3 – Environment verification\r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 4 of 10\n\nAn interesting function remains in the sample, even though it is never invoked. It was likely used during development and\r\nmay have been inadvertently left in the final build. The function checks whether the host executing the malware has its\r\nsystem language set to Russian.\r\nHook verification, by executing fourteen times a Beep for 2 seconds followed by Sleep during 4.5 seconds, at the end of this\r\nloop, the function computes the saved time (before beep, sleep loop) and the current time to calculate the elapsed time of\r\nsleep. Overall, the function acts as a timing measurement wrapper around repeated sleep intervals, likely used for\r\ndebugging, delay insertion, or basic anti-analysis timing checks. \r\nThe main function uses EnumProcess to count the number of running processes, if the count is below 60, the malware\r\nexits. Otherwise it creates a mutex h6p#dx!\u0026fse?%AS! , the prefix value appears to be shared among different versions and\r\nbuilds of the loader. \r\nVirusTotal: `behaviour:”h6p#dx!\u0026fse?%AS!”` (First tracked campaign)\r\nVirusTotal: `behaviour:”s6p1dx!\u0026fse?%AS!”` (New campaign November 2025)\r\nVirusTotal: `behaviour:”dx!\u0026fse?%AS!”` (Generic approach)\r\nStage 3 – C2 communication\r\nOnce these preliminary actions are validated, the malware starts interacting with the first layer of C2 servers. It\r\ncommunicates over HTTPS, the first message acts as a registration as indicated by the C2 URL ( /reg ), additionally the bot\r\ngenerates two random strings composed only of alpha lower, upper cases and digits. The first random is sent in the HTTP\r\nheader x-amz-cf-id this value is used to identify the bot and the second one is sent in the Content-Encoding, this value is\r\nused as the botnet or campaign identifier. For this first request the User-Agent header is set to WordPressAgent . The\r\nmalware disguises its C2 traffic as legitimate HTTP activity using generic paths, spoofed headers such as Content-Encoding and x-amz-cf-id , and a fake WordPressAgent user-agent to blend in with normal web traffic and evade simple\r\ndetection by security tools that rely on protocol and behavioral anomalies.\r\nIf the text success is received by the infected host, the malware continues its execution. Then the bot sends a second GET\r\nrequest on the /login endpoint. For this request, the user-agent changed to FingerPrint . This is a surprising\r\nmodification as legitimate software devices do not change this value once the HTTP communication has started.\r\nThe C2 responds with an image (c.f.: the Content-Type set to image/x-icon ), the malware uses steganography to hide the\r\nnext stage payload as an icon. The data are in the following format: the first size byte defines the size of the obfuscated data.\r\nFollowed by junk data that composed a real image (c.f. Annex 1 – ico image). The function searches for the endico pattern,\r\nonce it finds it, itdecrypts the data using RC4 algorithm along with a hardcoded key in the binary. \r\nThe bot expected to find a valid PE from the downloaded data, as it expects to have for the first two bytes the MZ value.\r\nBased on the different sample we analysed, the RC4 key remains the same: \r\nvpjNm4FDCr82AtUfhe39EG5JLwuZszKPyTcXWVMHYnRgBkSQqxzBfb6m75HZV3UyRY8vPxDna4WC2KMAgJjQqukrFdELXeGNSws9SBFXnYJ6ExMyu97KCebD5mTwaUj42NPAvHdk\r\nThe following link to Gist hosts a script to locate and decrypt the next stage from the HTTP GET /login response.\r\nOnce decrypted the PE is written into a file name COPYING3.dll in the %APPDATA% directory. The DLL is executed by the\r\ntask scheduler. The task name COPYING runs every 13 minutes and is configured as below:\r\nC:\\Windows\\System32\\schtasks.exe /Create /SC MINUTE /MO 13 /TN \"COPYING3\" /TR\r\n\"C:\\Windows\\System32\\rundll32.exe C:\\Users\\\u003credacted\u003e\\AppData\\Roaming\\\u003c15 random alphanum\u003e\\COPYING3.dll\r\nDllRegisterServer\"\r\nNB: The name of the dropped DLL and the scheduled task name changed for each version/campaign. In latest campaigns we\r\ndenote the following names:\r\nCOPYING3  \r\nVisualUpdater\r\nAlphaSecurity\r\nDetectorSpywareSecurity\r\nStage 4 – COPYING3 – Core\r\nCOPYING3.dll is valid PE32+ file, that exposed only two functions: DllRegisterServer and the default DllEntrypoint ,\r\nthe default entry point looks familiar as the threat actor reused the same layer of obfuscation as described previously for\r\nstage 0 of the infection (e.g. copy of a shellcode in a specific buffer that executes itself and does the custom LZMA inflation\r\nfollowed by the reallocation fixup before giving the execution flow to the final sequence of code). \r\nAn unusual way to execute the last stage is present in OysterLoader infection. In more common situations where DLL is\r\nused, the previous stage executes a specific export of the DLL or uses one of the DLL hijacking techniques. However, in this\r\ncurrent scenario, the previous stage was previously called the DllEntrypoint before the DllRegisterServer .\r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 5 of 10\n\nThis stage is the core of the malware as its primary behaviour is to interact with the C2 to retrieve additional payload.\r\nC2 interaction\r\nThe malware implements a straightforward HTTP-based command and control protocol operating over port 80 without\r\nencryption. It maintains connectivity with three hardcoded C2 servers ( 85.239.53[.]66 , 51.222.96[.]108 , and\r\n135.125.241[.]45 ) using a robust fallback mechanism that cycles through each server with three retry attempts spaced 9\r\nseconds apart before moving to the next. \r\nThe communication follows a dual-endpoint pattern:\r\n1. /api/kcehc (“check” spelled backwards) to indicate that a new infected host has been compromised and exfiltrate\r\nvictim host fingerprint.\r\n2. POST requests to /api/jgfnsfnuefcnegfnehjbfncejfh that acts as a beacon endpoint where it received C2 orders,\r\nstatus, additional payload, etc.\r\nThe loader uses a custom and changing message structure. The first data that is sent to the C2 is a JSON completely encoded\r\nwith a custom algorithm based on Base64 encoding. The algorithm uses a random and unique shift value that varies for\r\neach message, besides it also uses a non-default Base64 alphabet order\r\n(yog/ N3fj5ISmbep=Wu2k+BZcP0t4CYR1dQxHUaXEwGDKJV7i9ML6snhzrlqO8vAFT ). The random shift value is generated for each\r\nmessage using Mersenne Twister algorithm.\r\nThe non-standard alphabet is hardcoded in the malware and seems to be preserved among OysterLoader versions. To\r\nimprove the grasp of OysterLoader server communication we develop a Python script that brute force the shifting value to\r\ndecode the message. \r\nP.S. The reason why the bruteforce attack is efficient here is because of the length of the Base64 alphabet where the\r\ndeveloper must ensure the encoding remains functional which consequently requires a shifting value between 1 and 65.\r\nThe decoding script is available on this Gist; note that if the Base64 custom alphabet changes in future versions, the script\r\nshould be updated too. We plug a stdin reader to facilitate a PCAP reader with the script. For the showcases sample of this\r\npaper, the following command is used:\r\ntshark -r capture.pcap -T fields -e http.file_data| python com-decoder.py \r\nAll traffic uses WinINet APIs ( InternetOpenW , InternetConnectW , HttpOpenRequestW , HttpSendRequestA ) with a\r\ndistinctive User-Agent string Mozilla/6.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)\r\nChrome/91.0.4472.124 Safari/537.36 and exchanges data in JSON format. The raw JSON only contains a unique key\r\nnamed content which contains the message encoded with the custom Base64 algorithm. The content of the decoded JSON\r\nare: \r\n/api/kcehc expected Request Body (System Info):\r\n{\r\n\"a1\": \"[timestamp counter]\",\r\n\"a2\": \"[Hardcoded value in the binary]\",\r\n\"a3\": \"[username]\",\r\n\"a4\": \"[computer name]\",\r\n\"a5\": \"[domain info]\",\r\n\"a6\": \"[os version string]\",\r\n\"a7\": \"[domain name]\",\r\n\"a8\": \"[hardcoded version number]\",\r\n\"a9\": \"[PE type: EXE or DLL]\",\r\n\"a10\": \"[Windows Version]\"\r\n}\r\n/api/jgfnsfnuefcnegfnehjbfncejfh Request Body (Status Beacon):\r\n{\r\n\"b1\": timestamp counter,\r\n\"b2\": random token,\r\n\"b3\": status message,\r\n\"b4\": execution result\r\n}\r\nNB: In the above JSON schema, all fields are not required for each message.\r\nC2 response when a new payload is to execute: \r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 6 of 10\n\n{\r\n\"r1\": command type,\r\n\"r2\": payload file name,\r\n\"r3\": execution parameter flag,\r\n\"r4\": additional config flag,\r\n\"r5\": payload data encoded in standard Base64\r\n}\r\nC2 endpoint and protocol update\r\nIn the latest bot version, the JSON fingerprint format has been overhauled, to include running-process data. The original\r\nkeys ( a1 – a10 ) are now t1 – t12 : t1 – t10 retain their previous meanings, while t11 and t12 carry arrays of\r\nprocess names and their Process Identifier (PID) (c.f.: Annex 2 – Example of exfiltrated fingerprint).\r\nAt the same time, the Base64 alphabet used for encoding shifts dynamically during C2 exchanges. Although the bot still\r\nstarts with its original shuffle key to encode the outbound fingerprint, the C2’s JSON response adds a new field, tk , which\r\nprovides an updated replacement alphabet. From that point forward, the bot applies this new tk string in its decoding\r\nroutine for all subsequent communications.\r\nOnce decoded the returned JSON from the POST request to /api/v2/facade is as follows:\r\n{\r\n\"ti\": identifier,\r\n\"tk\": the new Base64 substituion alphabet,\r\n\"tr\": the final URL resource to beacon, eg: `/api/v2/\u003ctr\u003e,\r\n\"tt\": \"\"\r\n}\r\nIn the new version, a third endpoint has been added. Previously, the bot used two URLs—one for pings and fingerprint\r\nexfiltration and another for beaconing. Now the initial phase is split across two endpoints: it first issues an empty GET\r\nrequest to /api/v2/init , then sends the encoded fingerprint via POST to /api/v2/facade . The original beaconing URL\r\nis now defined in the response of the /api/v2/facade by the JSON key tr.\r\nThe new URLs path are:\r\n/api/v2/init (check)\r\n/api/v2/facade (send fingerprint and receive new Base64 alphabet)\r\n/api/v2/YgePIY5zPSoGUjzRx7C50MTx6EzABXIPd (beacon)\r\nHunting – Infrastructure\r\nOysterLoader malware operates with a two-tiered server infrastructure.\r\n1. The initial layer, the delivery server, handles the initial connection via the /reg and /login URLs. Its primary role is to\r\nhost the steganographically concealed next stage of the malware. This stage has shown consistency, with the\r\ndeveloper maintaining the same communication patterns and URLs.\r\n2. The second layer functions as the final C2 server. This server is responsible for victim interaction, including\r\ncollecting information and issuing commands and orders. Unlike the delivery stage, the C2 server has been updated\r\ntwice since the initial report on OysterLoader.\r\nDate URL\r\nFirst observed version May 2024 to October 2025\r\n/api/connect\r\n/api/session\r\nJune 2025 to September 2025\r\n/api/kcehc\r\n/api/jgfnsfnuefcnegfnehjbfncejfh\r\nDecember 2025\r\n/api/v2/init\r\n/api/v2/facade\r\n/api/v2/YgePIY5zPSoGUjzRx7C50MTx6EzABXIPd\r\n(also /api/v2/1X54xw9ocWxlx9p2VWGxZ4ljAtr )\r\nAs of January 2026, the latest OysterLoader C2 are:\r\nhxxps://grandideapay[.]com/api/v2/facade\r\nhxxp://nucleusgate[.]com/api/v2/facade\r\nhxxps://cardlowestgroup[.]com/api/v2/facade\r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 7 of 10\n\nhxxps://socialcloudguru[.]com/api/v2/facade\r\nhxxps://coretether[.]com/api/v2/facade\r\nhxxps://registrywave[.]com/api/v2/facade\r\nConclusion\r\nOysterLoader, also known as Broomstick and CleanUp, is an sophisticated, multi-stage malware loader developed in C++\r\nthat continues to pose a relevant threat into early 2026. Its primary objective is to facilitate malicious campaigns, notably\r\nthose conducted by the Rhysida ransomware group, and to distribute commodity malware like Vidar in a business model\r\nthat remains unclear at the time of writing this report.\r\nThe detailed analysis presented in this report highlights a comprehensive set of techniques employed by the loader across its\r\nfour stages, designed specifically to ensure persistence and evade detection:\r\nEvasion \u0026 Obfuscation: The initial stage leverages excessive legitimate API call hammering and simple anti-debugging traps to thwart static analysis. Furthermore, dynamic API resolution employs a custom hashing algorithm\r\nthat varies between samples, complicating signature-based detection.\r\nStealthy Delivery: The core payload is delivered in a highly obfuscated manner. Stage 2 utilises a custom LZMA\r\ndecompression routine with a non-standard header and modified bitstream, effectively bypassing common\r\ndecompression and analysis tools.\r\nAdvanced C2 Communication: The final stage implements a robust C2 communication protocol that features a\r\ndual-layer server infrastructure and highly-customized data encoding. It uses a non-default Base64 alphabet with a\r\nunique, random shift value for each message, making traffic analysis and automated decoding challenging. The\r\nmalware also employs techniques to blend in, such as spoofing HTTP headers and user-agents, while environment\r\nchecks (like process counting) act as an additional layer of anti-analysis protection.\r\nThe constant evolution in OysterLoader’s code, including updated C2 endpoints and JSON fingerprinting schemas, signals\r\nthe high level of activity and commitment from the threat actors. The quality and complexity of the malware’s development\r\nstrongly suggest that OysterLoader will remain a significant and persistent threat in the near term. \r\nTo protect our customers from OysterLoader, Sekoia.io analysts will continue to proactively monitor this threat.\r\nAnnexes\r\nAnnex 1 – ico image\r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 8 of 10\n\nAnnex 2 – Example of exfiltrated fingerprint\r\n{ 't1': '',\r\n 't10': 'Windows 10 Pro | 19044.1288',\r\n 't11': '7-Zip 23.01 (x64) | Mozilla Firefox (x64 en-US) | Mozilla '\r\n 'Maintenance Service | Node.js | Microsoft .NET Host - 8.0.3 (x64) '\r\n '| Microsoft ASP.NET Core 8.0.3 Targeting Pack (x64) | Microsoft '\r\n '.NET Targeting Pack - 8.0.3 (x64) | '\r\n 'Microsoft.NET.Sdk.Maui.Manifest-8.0.100 (x64) | Microsoft .NET '\r\n 'Toolset 8.0.203 (x64) | Microsoft Visual C++ 2022 X64 Additional '\r\n \u003cREDACTED\u003e\r\n '.NET Host FX Resolver - 9.0.1 (x64) | Microsoft .NET Host FX '\r\n 'Resolver - 8.0.3 (x64) | Microsoft.NET.Sdk.Aspire.Manifest-8.0.100 '\r\n '(x64) | ',\r\n 't12': '[System Process] (PID: 0) | System (PID: 4) | Registry (PID: 100) '\r\n '| smss.exe (PID: 344) | csrss.exe (PID: 436) | wininit.exe (PID: '\r\n \r\n\u003cREDACTED\u003e\r\n '(PID: 4744) | SppExtComObj.Exe (PID: 3160) | svchost.exe (PID: '\r\n '3696) | svchost.exe (PID: 1560) | svchost.exe (PID: 5548) | '\r\n 'sysmon.exe (PID: 6956) | unsecapp.exe (PID: 6992) | svchost.exe '\r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 9 of 10\n\n'(PID: 6544) | vt-windows-event-stream.exe (PID: 5672) | '\r\n 'vt-windows-event-stream.exe (PID: 5660) | '\r\n 'vt-windows-event-stream.exe (PID: 5324) | conhost.exe (PID: 5320) '\r\n '|\r\n 't2': '',\r\n 't3': 'Bruno',\r\n 't4': 'DESKTOP-ET51AJO',\r\n 't5': '',\r\n 't6': '',\r\n 't7': 'WORKGROUP',\r\n 't8': '1',\r\n 't9': 'dll'\r\n}\r\nThe complete JSON is available on this gist.\r\nCTI Cybercrime Malware\r\nShare this post:\r\nSource: https://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nhttps://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/\r\nPage 10 of 10\n\n{ 't1': '', 't10': 'Windows 10 Pro | 19044.1288',   \n't11': '7-Zip 23.01 (x64) | Mozilla Firefox (x64 en-US) | Mozilla ' \n'Maintenance Service | Node.js | Microsoft .NET Host-8.0.3 (x64) '\n'| Microsoft ASP.NET Core 8.0.3 Targeting Pack (x64) | Microsoft '\n'.NET Targeting Pack-8.0.3 (x64) | '  \n'Microsoft.NET.Sdk.Maui.Manifest-8.0.100  (x64) | Microsoft .NET '\n'Toolset 8.0.203 (x64) | Microsoft Visual C++ 2022 X64 Additional '\n\u003cREDACTED\u003e    \n'.NET Host FX Resolver-9.0.1 (x64) | Microsoft .NET Host FX '\n'Resolver -8.0.3 (x64) | Microsoft.NET.Sdk.Aspire.Manifest-8.0.100   '\n'(x64) | ',   \n't12': '[System Process] (PID: 0) | System (PID: 4) | Registry (PID: 100) '\n'| smss.exe (PID: 344) | csrss.exe (PID: 436) | wininit.exe (PID: '\n\u003cREDACTED\u003e    \n'(PID: 4744) | SppExtComObj.Exe (PID: 3160) | svchost.exe (PID: '\n'3696) | svchost.exe (PID: 1560) | svchost.exe (PID: 5548) | ' \n'sysmon.exe (PID: 6956) | unsecapp.exe (PID: 6992) | svchost.exe '\n  Page 9 of 10",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://blog.sekoia.io/oysterloader-unmasked-the-multi-stage-evasion-loader/"
	],
	"report_names": [
		"oysterloader-unmasked-the-multi-stage-evasion-loader"
	],
	"threat_actors": [
		{
			"id": "f6f91e1c-9202-4497-bf22-9cd5ef477600",
			"created_at": "2023-01-06T13:46:38.86765Z",
			"updated_at": "2026-04-10T02:00:03.12735Z",
			"deleted_at": null,
			"main_name": "WIZARD SPIDER",
			"aliases": [
				"TEMP.MixMaster",
				"GOLD BLACKBURN",
				"DEV-0193",
				"UNC2053",
				"Pistachio Tempest",
				"DEV-0237",
				"Storm-0230",
				"FIN12",
				"Periwinkle Tempest",
				"Storm-0193",
				"Trickbot LLC"
			],
			"source_name": "MISPGALAXY:WIZARD SPIDER",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "bc119938-a79c-4e5f-9d4d-dc96835dfe2e",
			"created_at": "2024-06-04T02:03:07.799286Z",
			"updated_at": "2026-04-10T02:00:03.606456Z",
			"deleted_at": null,
			"main_name": "GOLD BLACKBURN",
			"aliases": [
				"ITG23 ",
				"Periwinkle Tempest ",
				"Wizard Spider "
			],
			"source_name": "Secureworks:GOLD BLACKBURN",
			"tools": [
				"BazarLoader",
				"Buer Loader",
				"Bumblebee",
				"Dyre",
				"Team9",
				"TrickBot"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "63061658-5810-4f01-9620-7eada7e9ae2e",
			"created_at": "2022-10-25T15:50:23.752974Z",
			"updated_at": "2026-04-10T02:00:05.244531Z",
			"deleted_at": null,
			"main_name": "Wizard Spider",
			"aliases": [
				"Wizard Spider",
				"UNC1878",
				"TEMP.MixMaster",
				"Grim Spider",
				"FIN12",
				"GOLD BLACKBURN",
				"ITG23",
				"Periwinkle Tempest",
				"DEV-0193"
			],
			"source_name": "MITRE:Wizard Spider",
			"tools": [
				"TrickBot",
				"AdFind",
				"BITSAdmin",
				"Bazar",
				"LaZagne",
				"Nltest",
				"GrimAgent",
				"Dyre",
				"Ryuk",
				"Conti",
				"Emotet",
				"Rubeus",
				"Mimikatz",
				"Diavol",
				"PsExec",
				"Cobalt Strike"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "e6a21528-2999-4e2e-aaf4-8b6af14e17f3",
			"created_at": "2022-10-25T16:07:24.422115Z",
			"updated_at": "2026-04-10T02:00:04.983298Z",
			"deleted_at": null,
			"main_name": "Wizard Spider",
			"aliases": [
				"DEV-0193",
				"G0102",
				"Gold Blackburn",
				"Gold Ulrick",
				"Grim Spider",
				"ITG23",
				"Operation BazaFlix",
				"Periwinkle Tempest",
				"Storm-0230",
				"TEMP.MixMaster",
				"Wizard Spider"
			],
			"source_name": "ETDA:Wizard Spider",
			"tools": [
				"AdFind",
				"Agentemis",
				"Anchor_DNS",
				"BEERBOT",
				"BazarBackdoor",
				"BazarCall",
				"BazarLoader",
				"Cobalt Strike",
				"CobaltStrike",
				"Conti",
				"Diavol",
				"Dyranges",
				"Dyre",
				"Dyreza",
				"Dyzap",
				"Gophe",
				"Invoke-SMBAutoBrute",
				"KEGTAP",
				"LaZagne",
				"LightBot",
				"PowerSploit",
				"PowerTrick",
				"PsExec",
				"Ryuk",
				"SessionGopher",
				"TSPY_TRICKLOAD",
				"Team9Backdoor",
				"The Trick",
				"TheTrick",
				"Totbrick",
				"TrickBot",
				"TrickLoader",
				"TrickMo",
				"Upatre",
				"bazaloader",
				"cobeacon"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434370,
	"ts_updated_at": 1775792171,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/14522467f54a5de81dc1265ad3f19cfb20ba7a3d.pdf",
		"text": "https://archive.orkl.eu/14522467f54a5de81dc1265ad3f19cfb20ba7a3d.txt",
		"img": "https://archive.orkl.eu/14522467f54a5de81dc1265ad3f19cfb20ba7a3d.jpg"
	}
}