{
	"id": "374b782d-c917-4c4d-900e-3aa72e8ef0f5",
	"created_at": "2026-04-06T00:22:12.21613Z",
	"updated_at": "2026-04-10T03:35:21.371425Z",
	"deleted_at": null,
	"sha1_hash": "54792d7f0b0347067fb3369c02a2e2906c2ddcd6",
	"title": "Raspberry Robin’s Roshtyak: A Little Lesson in Trickery",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 3581050,
	"plain_text": "Raspberry Robin’s Roshtyak: A Little Lesson in Trickery\r\nBy Jan VojtěšekAuthor at Avast Threat Labs\r\nArchived: 2026-04-05 13:22:57 UTC\r\nThere are various tricks malware authors use to make malware analysts’ jobs more difficult. These tricks include\r\nobfuscation techniques to complicate reverse engineering, anti-sandbox techniques to evade sandboxes, packing to\r\nbypass static detection, and more. Countless deceptive tricks used by various malware strains in-the-wild have\r\nbeen documented over the years. However, few of these tricks are implemented in a typical piece of malware,\r\ndespite the many available tricks. \r\nThe subject of this blog post, a backdoor we dubbed Roshtyak, is not your typical piece of malware. Roshtyak is\r\nfull of tricks. Some are well-known, and some we have never seen before. From a technical perspective, the\r\nlengths Roshtyak takes to protect itself are extremely interesting. Roshtyak belongs to one of the best-protected\r\nmalware strains we have ever seen. We hope by publishing our research and analysis of the malware and its\r\nprotection tricks we will help fellow researchers recognize and respond to similar tricks, and harden their analysis\r\nenvironments, making them more resistant to the evasion techniques described.\r\nRoshtyak is the DLL backdoor used by Raspberry Robin, a worm spreading through infected removable drives.\r\nRaspberry Robin is extremely prevalent. We protected over 550K of our users from the worm this year. Due to its\r\nhigh prevalence, it should be no surprise that we aren’t the only ones taking note of Raspberry Robin. \r\nRed Canary’s researchers published the first analysis of Raspberry Robin in May 2022. In June, Symantec\r\npublished a report describing a mining/clipboard hijacking operation, which reportedly made the cybercriminals at\r\nleast $1.7 million. Symantec did not link the malicious operation to Raspberry Robin. Nevertheless, we assess\r\nwith high confidence that what they analyzed was Raspberry Robin. This assessment is based on C\u0026C overlaps,\r\nstrong malware similarity, and coinfections observed in our telemetry. Cybereason, Microsoft, and Cisco\r\npublished further reports in July/August 2022. Microsoft reported that Raspberry Robin infections led to DEV-0243 (a.k.a Evil Corp) pre-ransomware behavior. We could not confirm this connection using our telemetry. Still,\r\nwe find it reasonable to believe that the miner payload is not the only way Raspberry Robin infections are being\r\nmonetized. Other recent reports also hint at a possible connection between Raspberry Robin and Evil Corp.\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 1 of 22\n\nA map showing the number of users Avast protected from Raspberry Robin\r\nThere are many unknowns about Raspberry Robin, despite so many published reports. What are the ultimate\r\nobjectives behind the malware? Who is responsible for Raspberry Robin? How did it become so prevalent?\r\nUnfortunately, we do not have answers to all these questions. However, we can answer an important question we\r\nsaw asked multiple times: What functionality is hidden inside the heavily obfuscated DLL (or Roshtyak as we call\r\nit)? To answer this question, we fully reverse engineered a Roshtyak sample, and present our analysis results in\r\nthis blog post.\r\nOverview\r\nRoshtyak is packed in as many as 14 protective layers, each heavily obfuscated and serving a specific purpose.\r\nSome artifacts suggest the layers were originally PE files but were transformed into custom encrypted structures\r\nthat only the previous layers know how to decrypt and load. Numerous anti-debugger, anti-sandbox, anti-VM, and\r\nanti-emulator checks are sprinkled throughout the layers. If one of these checks successfully detects an analysis\r\nenvironment, one of four actions are taken. \r\n1. The malware calls TerminateProcess on itself to avoid exhibiting any further malicious behavior and to\r\nkeep the subsequent layers encrypted.\r\n2. Roshtyak crashes on purpose. This has the same effect as terminating itself, but it might not be\r\nimmediately clear if the crash was intentional or because of a bug thanks to Roshtyak’s obfuscated nature.\r\n3. The malware enters an infinite loop on purpose. Since the loop itself is located in obfuscated code and\r\nspans thousands of instructions, it might be hard to determine if the loop is doing something useful or not.\r\n4. The most interesting case is when the malware reacts to a successful check by unpacking and loading a\r\nfake payload. This happens in the eighth layer, which is loaded with dozens of anti-analysis checks. The\r\nresult of each of these checks is used to modify the value of a global variable. There are two payloads\r\nencrypted in the data section of the eighth layer: the real ninth layer and a fake payload. The real ninth\r\nlayer will get decrypted only if the global variable matches the expected value after all the checks have\r\nbeen performed. If at least one check succeeded in detecting an analysis environment, the global variable’s\r\nvalue will differ from the expected value, causing Roshtyak to unpack and execute the fake payload\r\ninstead. \r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 2 of 22\n\nRoshtyak’s obfuscation causes even relatively simple functions to grow into large proportions. This necessitates\r\nsome custom deobfuscation tooling if one wants to reverse engineer it within a reasonable timeframe.\r\nThe fake payload is a BroAssist (a.k.a BrowserAssistant) adware sample. We believe this fake payload was\r\nintended to mislead malware analysts into thinking the sample is less interesting than it really is. When a reverse\r\nengineer focuses on quickly unpacking a sample, it might look like the whole sample is “just” an obfuscated piece\r\nof adware (and a very old one at that), which could cause the analyst to lose interest in digging deeper. And\r\nindeed, it turns out that these fake payload shenanigans can be very effective. As can be seen on the screenshot\r\nbelow, it fooled at least one researcher, who misattributed the Raspberry Robin worm, because of the fake\r\nBrowserAssistant payload.\r\nA security researcher misattributing Raspberry Robin because of the fake payload. This is not to pick on anyone,\r\nwe just want to show how easy it is to make a mistake like this given Roshtyak’s trickery and complexity.\r\nThe Bag of Tricks\r\nFor the sake of keeping this blog post (sort of) short and to the point, let’s get straight into detailing some of the\r\nmore interesting evasion techniques employed by Roshtyak.\r\nSegment registers\r\nEarly in the execution, Roshtyak prefers to use checks that do not require calling any imported functions. If one of\r\nthese checks is successful, the sample can quietly exit without generating any suspicious API calls. Below is an\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 3 of 22\n\nexample where Roshtyak checks the behavior of the gs segment register. The check is designed to be stealthy\r\nand the surrounding garbage instructions make it easy to overlook.\r\nA stealthy detection of single-stepping. Only the underscored instructions are useful.\r\nThe first idea behind this check is to detect single-stepping. Before the above snippet, the value of cx was\r\ninitialized to 2 . After the pop ecx instruction, Roshtyak checks if cx is still equal to 2 . This would be the\r\nexpected behavior because this value should propagate through the stack and the gs register under normal\r\ncircumstances. However, a single step event would reset the value of the gs selector, which would result in a\r\ndifferent value getting popped into ecx at the end.\r\nBut there is more to this check. As a side effect of the two push/pop pairs above, the value of gs is temporarily\r\nchanged to 2 . After this check, Roshtyak enters a loop, counting the number of iterations until the value of gs\r\nis no longer 2 . The gs selector is also reset after a thread context switch, so the loop essentially counts the\r\nnumber of iterations until a context switch happens. Roshtyak repeats this procedure multiple times, averages out\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 4 of 22\n\nthe result, and checks that it belongs to a sensible range for a bare metal execution environment. If the sample runs\r\nunder a hypervisor or in an emulator, the average number of iterations might fall outside of this range, which\r\nallows Roshtyak to detect undesirable execution environments.\r\nRoshtyak also checks that the value of the cs segment register is either 0x1b or 0x23 . Here, 0x1b is the\r\nexpected value when running on native x86 Windows, while 0x23 is the expected value under WoW64.\r\nAPC injection through a random ntdll gadget\r\nRoshtyak performs some of its functionality from separate processes. For example, when it communicates with its\r\nC\u0026C server, it spawns a new innocent-looking process like regsvr32.exe . Using shared sections, it injects its\r\ncomms module into the address space of the new process. The injected module is executed via APC injection,\r\nusing NtQueueApcThreadEx .\r\nInterestingly, the ApcRoutine argument (which marks the target routine to be scheduled for execution) does not\r\npoint to the entry point of the injected module. Instead, it points to a seemingly random address inside ntdll .\r\nTaking a closer look, we see this address was not chosen randomly but that Roshtyak scanned the code section of\r\nntdll for pop r32; ret gadgets (excluding pop esp , because pivoting the stack would be undesirable) and\r\npicked one at random to use as the ApcRoutine . \r\nA random pop r32; ret gadget used as the entry point for APC injection\r\nLooking at the calling convention for the ApcRoutine reveals what’s going on. The pop instruction makes the\r\nstack pointer point to the SystemArgument1 parameter of NtQueueApcThreadEx and so the ret instruction\r\neffectively jumps to wherever SystemArgument1 is pointing. This means that by abusing this gadget, Roshtyak\r\ncan treat SystemArgument1 as the entry point for the purpose of APC injection. This obfuscates the control flow\r\nand makes the NtQueueApcThreadEx call look more legitimate. If someone hooks this function and inspects the\r\nApcRoutine argument, the fact that it is pointing into the ntdll code section might be enough to convince them\r\nthat the call is not malicious.\r\nChecking read/write performance on write-combined memory\r\nIn this next check, Roshtyak allocates a large memory buffer with the PAGE_WRITECOMBINE flag. This flag is\r\nsupposed to modify cache behavior to optimize sequential write performance (at the expense of read performance\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 5 of 22\n\nand possibly memory ordering). Roshtyak uses this to detect if it’s running on a physical machine. It conducts an\r\nexperiment where it first writes to the allocated buffer and then reads from the allocated buffer, all while\r\nmeasuring the read/write performance using a separate thread as a counter. This experiment is repeated 32 times\r\nand the check is passed only if write performance was at least six times higher than read performance most of the\r\ntimes. If the check fails, Roshtyak intentionally selects a wrong RC4 key, which results in failing to properly\r\ndecrypt the next layer.\r\nHiding shellcode from plain sight\r\nThe injected shellcode is interestingly hidden, too. When Roshtyak prepares for code injection, it first creates a\r\nlarge section and maps it into the current process as PAGE_READWRITE . Then, it fills the section with random data\r\nand places the shellcode at a random offset within the random data. Since the shellcode is just a relatively small\r\nloader followed by random-looking packed data, the whole section looks like random data. \r\nA histogram of the bytes inside the shared section. Note that it looks almost random, the most suspicious sign is\r\nthe slight overrepresentation of null bytes.\r\nThe section is then unmapped from the current process and mapped into the target process, where it is executed\r\nusing the above-described APC injection technique. The random data was added in an attempt to conceal the\r\nexistence of the shellcode. Judging only from the memory dump of the target process, it might look like the\r\nsection is full of random data and does not contain any valid executable code. Even if one suspects actual valid\r\ncode somewhere in the middle of the section, it will not be easy to find its exact location. \r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 6 of 22\n\nThe start of the shellcode within the shared section. It might be hard to pinpoint the exact start address because it\r\nunconventionally starts on an odd bt instruction.\r\nRet2Kernel32\r\nRoshtyak makes a point of cleaning up after itself. Whenever a certain string or piece of memory is no longer\r\nneeded, Roshtyak wipes and/or frees it in an attempt to destroy as much evidence as possible. The same holds for\r\nRoshtyak’s layers. Whenever one layer finishes its job, it frees itself before passing execution onto the next layer.\r\nHowever, the layer cannot just simply free itself directly. The whole process would crash if it called VirtualFree\r\non the region of memory it’s currently executing from.\r\nRoshtyak, therefore, frees the layer through a ROP chain executed during layer transitions to avoid this problem.\r\nWhen a layer is about to exit, it constructs a ROP chain on the stack and returns into it. An example of such a ROP\r\nchain can be seen below. This chain starts by returning into VirtualFree and UnmapViewOfFile to release the\r\nprevious layer’s memory. Then, it returns into the next layer. The return address from the next layer is set to\r\nRtlExitUserThread , to safeguard execution.\r\nA simple ROP chain consisting of VirtualFree -\u003e UnmapViewOfFile -\u003e next layer -\u003e RtlExitUserThread\r\nMulDiv bug\r\nMulDiv is a function exported by kernel32.dll , which takes three signed 32-bit integers as arguments. It\r\nmultiplies the first two arguments, divides the multiplication result by the third argument, and returns the final\r\nresult rounded to the nearest integer. While this might seem like a simple enough function, there’s an ancient sign\r\nextension bug in Microsoft’s implementation. This bug is sort of considered a feature now and might never get\r\nfixed.\r\nRoshtyak is aware of the bug and tests for its presence by calling MulDiv(1, 0x80000000, 0x80000000) . On real\r\nWindows machines, this triggers the bug and MulDiv erroneously returns 2 , even though the correct return\r\nvalue should be 1 , because (1 * -2147483648) / -2147483648 = 1 . This allows Roshtyak to detect emulators\r\nthat do not replicate the bug. For example, this successfully detects Wine, which, funnily enough, contains a\r\ndifferent bug, which makes the above call return 0 .\r\nTampering with return addresses stored on the stack\r\nThere are also tricks designed to obfuscate function calls. As shown in the previous section, Roshtyak likes to call\r\nfunctions using the ret instruction. This next trick is similar in that it also manipulates the stack so a ret\r\ninstruction can be used to jump to the desired address. \r\nTo achieve this, Roshtyak scans the current thread’s stack for pointers into the code section of one of the previous\r\nlayers (unlike the other layers, this one was not freed using the ROP chain technique). It replaces all these pointers\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 7 of 22\n\nwith the address it wants to call. Then it lets the code return multiple times until a ret instruction encounters one\r\nof the hijacked pointers, redirecting the execution to the desired address.\r\nException-based checks\r\nAdditionally, Roshtyak contains checks that set up a custom vectored exception handler and intentionally trigger\r\nvarious exceptions to ensure they all get handled as expected.\r\nRoshtyak sets up a vectored exception handler using RtlAddVectoredExceptionHandler . This handler contains\r\ncustom handlers for selected exception codes. A top-level exception handler is also registered using\r\nSetUnhandledExceptionFilter . This handler should not be called in the targeted execution environments (none\r\nof the intentionally triggered exceptions should fall through the vectored exception handler). So this top-level\r\nhandler just contains a single call to TerminateProcess . Interestingly, Roshtyak also uses\r\nZwSetInformationProcess to set SEM_FAILCRITICALERRORS using the ProcessDefaultHardErrorMode class.\r\nThis ensures that even if the exception somehow is passed all the way to the default exception handler, Windows\r\nwould not show the standard error message box, which could alert the victim that something suspicious is going\r\non.\r\nWhen everything is set up, Roshtyak begins generating exceptions. The first exception is generated by a popf\r\ninstruction, directly followed by a cpuid instruction (shown below). The value popped by the popf instruction\r\nwas crafted to set the trap flag, which should, in turn, raise a single-step exception. On a physical machine, the\r\nexception would trigger right after the cpuid instruction. Then, the custom vectored exception handler would\r\ntake over and move the instruction pointer away from the C7 B2 opcodes, which mark an invalid instruction.\r\nHowever, under many hypervisors, the single-step exception would not be raised. This is because the cpuid\r\ninstruction forces a VM exit, which might delay the effect of the trap flag. If that is the case, the processor will\r\nraise an illegal instruction exception when trying to execute the invalid opcodes. If the vectored exception handler\r\nencounters such an exception, it knows that it is running under a hypervisor. A variation of this technique is\r\ndescribed thoroughly in a blog post by Palo Alto Networks. Please refer to it for more details. \r\nThe exception-based check using popf and cpuid to detect hypervisors\r\nAnother exception is generated using the two-byte int 3 instruction ( CD 03 ). This instruction is followed by\r\ngarbage opcodes. The int 3 here raises a breakpoint exception, which is handled by the vectored exception\r\nhandler. The vectored exception handler doesn’t really do anything to handle the exception, which is interesting.\r\nThis is because by default, when Windows handles the two-byte int 3 instruction, it will leave the instruction\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 8 of 22\n\npointer in between the two instruction bytes, pointing to the 03 byte. When disassembled from this 03 byte, the\r\ngarbage opcodes suddenly start making sense. We believe this is a check against some overeager debuggers,\r\nwhich could “fix” the instruction pointer to point after the 03 byte.\r\nMoreover, the vectored exception handler checks the thread’s CONTEXT and makes sure that registers Dr0\r\nthrough Dr3 are empty. If they are not, the process is being debugged using hardware breakpoints. While this\r\ncheck is relatively common in malware, the CONTEXT is usually obtained using a call to a function like\r\nGetThreadContext . Here, the malware authors took advantage of CONTEXT being passed as an argument to the\r\nexception handler, so they did not need to call any additional API functions.\r\nLarge executable mappings\r\nThis next check is interesting mostly because we are not sure what it’s really supposed to check (in other words,\r\nwe’d be happy to hear your theories!). It starts with Roshtyak creating a large PAGE_EXECUTE_READWRITE mapping\r\nof size 0x386F000 . Then it maps this mapping nine times into its own address space. After this, it memsets the\r\nmapping to 0x42 (opcode for inc edx ), except for the last six bytes, which are filled with four inc ecx\r\ninstructions and jmp dword ptr [ecx] (see below). Next, it puts the nine base addresses of the mapped views\r\ninto an array, followed by an address of a single ret instruction. Finally, it points ecx into this array and calls\r\nthe first mapped view, which results in all the mapped views being called sequentially until the final ret\r\ninstruction. After the return, Roshtyak validates that edx got incremented exactly 0x1FBE6FCA times ( 9 *\r\n(0x386F000 - 6) ).\r\nThe end of the large mapped section. The jmp dword ptr [ecx] instruction is supposed to jump to the start of the\r\nnext mapped view.\r\nOur best guess is that this is yet another anti-emulator check. For example, in some emulators, mapped sections\r\nmight not be fully implemented, so the instructions written into one instance of the mapped view might not\r\npropagate to the other eight instances. Another theory is the check could be done to request large amounts of\r\nmemory that emulators might fail to provide. After all, the combined size of all the views is almost half of the\r\nstandard 32-bit user mode address space.\r\nDetecting process suspension\r\nThis trick abuses an undocumented thread creation flag in NtCreateThreadEx to detect when Roshtyak’s main\r\nprocess gets externally suspended (which could mean that a debugger got attached). This flag essentially allows a\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 9 of 22\n\nthread to keep running even when PsSuspendProcess gets called. This is coupled with another trick abusing the\r\nfact that the thread suspend counter is a signed 8-bit value, which means that it maxes out at 127. Roshtyak\r\nspawns two threads, one of which keeps suspending the other one until the suspend counter limit is reached. After\r\nthis, the first thread keeps periodically suspending the other one and checking if the call to NtSuspendThread\r\nkeeps failing with STATUS_SUSPEND_COUNT_EXCEEDED . If it does not, the thread must have been externally\r\nsuspended and resumed (which would leave the suspend counter at 126, so the next call to NtSuspendThread\r\nwould succeed). Not getting this error code would be suspicious enough for Roshtyak to quit using\r\nTerminateProcess . This entire technique is described in more detail in a blog post by Secret Club. We believe\r\nthat’s where the authors of Roshtyak got this trick from. It’s also worth mentioning Roshtyak uses this technique\r\nonly on Windows builds 18323 (19H1) and later because the undocumented thread creation flag was not\r\nimplemented on prior builds.\r\nIndirect registry writes\r\nRoshtyak performs many suspicious registry operations, for example, setting up the RunOnce key for persistence.\r\nSince modifications to such keys are likely to be monitored, Roshtyak attempts to circumvent the monitoring. It\r\nfirst generates a random registry key name and temporarily renames the RunOnce key to the random name using\r\nZwRenameKey . Once renamed, Roshtyak adds a new persistence entry to the temporary key before finally\r\nrenaming it back to RunOnce . This method of writing to the registry can be easily detected, but it might bypass\r\nsome simple hooking-based monitoring methods.\r\nSimilarly, there are multiple methods Roshtyak uses to delete files. Aside from the apparent call to\r\nNtDeleteFile , Roshtyak is able to effectively delete a file by setting FileDispositionInformation or\r\nFileRenameInformation in a call to ZwSetInformationFile . However, unlike the registry modification method,\r\nthis doesn’t seem to be implemented in order to evade detection. Instead, Roshtyak will try these alternative\r\nmethods if the initial call to NtDelete file fails. \r\nChecking VBAWarnings\r\nThe VBAWarnings registry value controls how Microsoft Office behaves when a user opens a document\r\ncontaining embedded VBA macros. If this value is 1 (meaning “Enable all macros”), macros are executed by\r\ndefault, even without the need for any user interaction. This is a common setting for sandboxes, which are\r\ndesigned to detonate maldocs automatically. On the other hand, this setting is uncommon for regular users, who\r\ngenerally don’t go around changing random settings to make themselves more vulnerable (at least most of them\r\ndon’t). Roshtyak therefore uses this check to differentiate between sandboxes and regular users and refuses to run\r\nfurther if the value of VBAWarnings is 1 . Interestingly, this means that users, who for whatever reason have\r\nlowered their security this way, are immune to Roshtyak.\r\nCommand line wiping\r\nRoshtyak’s core is executed with very suspicious command lines, such as RUNDLL32.EXE\r\nSHELL32.DLL,ShellExec_RunDLL REGSVR32.EXE -U /s \"C:\\Users\\\u003cREDACTED\u003e\\AppData\\Local\\Temp\\dpcw.etl.\" .\r\nThese command lines don’t look particularly legitimate, so Roshtyak attempts to hide them during execution. It\r\ndoes this by wiping command line information collected from various sources. It starts by calling\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 10 of 22\n\nGetCommandLineA and GetCommandLineW and wiping both of the returned strings. Then it attempts to wipe the\r\nstring pointed to by PEB-\u003eProcessParameters-\u003eCommandLine (even if this points to a string that has already been\r\nwiped). Since Roshtyak is often running under WoW64, it also calls NtWow64QueryInformationProcess64 to\r\nobtain a pointer to PEB64 to wipe ProcessParameters-\u003eCommandLine obtained by traversing this “second” PEB.\r\nWhile the wiping of the command lines was probably meant to make Roshtyak look more legitimate, the complete\r\nabsence of any command line is also highly unusual. This was noticed by the Red Canary researchers in their blog\r\npost, where they proposed a detection method based on these suspiciously empty command lines.\r\nRoshtyak’s core process, as shown by Process Explorer. Note the suspiciously empty command line.\r\nAdditional tricks\r\nAside from the techniques described so far, Roshtyak uses many less sophisticated tricks that are commonly found\r\nin other malware as well. These include:\r\nHiding threads using ThreadHideFromDebugger (and verifying that the threads really got hidden using\r\nNtQueryInformationThread )\r\nPatching DbgBreakPoint in ntdll\r\nDetecting user inactivity using GetLastInputInfo\r\nChecking fields from PEB ( BeingDebugged , NtGlobalFlag )\r\nChecking fields from KUSER_SHARED_DATA ( KdDebuggerEnabled , ActiveProcessorCount ,\r\nNumberOfPhysicalPages )\r\nChecking the names of all running processes (some are compared by hash, some by patterns, and some by\r\ncharacter distribution)\r\nHashing the names of all loaded modules and checking them against a hardcoded blacklist\r\nVerifying the main process name is not too long and doesn’t match known names used in sandboxes\r\nUsing the cpuid instruction to check hypervisor information and the processor brand\r\nUsing poorly documented COM interfaces\r\nChecking the username and computername against a hardcoded blacklist\r\nChecking for the presence of known sandbox decoy files\r\nChecking MAC addresses of own adapters against a hardcoded blacklist\r\nChecking MAC addresses from the ARP table (using GetBestRoute to populate it and GetIpNetTable to\r\ninspect it)\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 11 of 22\n\nCalling ZwQueryInformationProcess with ProcessDebugObjectHandle , ProcessDebugFlags , and\r\nProcessDebugPort\r\nChecking DeviceId of display devices (using EnumDisplayDevices )\r\nChecking ProductId of \\\\.\\PhysicalDrive0 (using IOCTL_STORAGE_QUERY_PROPERTY )\r\nChecking for virtual hard disks (using NtQuerySystemInformation with SystemVhdBootInformation )\r\nChecking the raw SMBIOS firmware table (using NtQuerySystemInformation with\r\nSystemFirmwareTableInformation )\r\nSetting up Defender exclusions (both for paths and processes)\r\nRemoving IFEO registry keys related to process names used by the malware\r\nObfuscation\r\nWe’ve shown many anti-analysis tricks that are designed to prevent Roshtyak from detonating in undesirable\r\nexecution environments. These tricks alone would be easy to patch or bypass. What makes analyzing Roshtyak\r\nespecially lethal is the combination of all these tricks with heavy obfuscation and multiple layers of packing. This\r\nmakes it very difficult to study the anti-analysis tricks statically and figure out how to pass all the checks in order\r\nto get Roshtyak to unpack itself. Furthermore, even the main payload received the same obfuscation, which means\r\nthat statically analyzing Roshtyak’s core functionality also requires a great deal of deobfuscation. \r\nIn the rest of this section, we’ll go through the main obfuscation techniques used by Roshtyak.\r\nA random code snippet from Roshtyak. As can be seen, the obfuscation makes the raw output of the Hex-Rays\r\ndecompiler practically incomprehensible.\r\nControl flow flattening\r\nControl flow flattening is one of the most noticeable obfuscation techniques employed by Roshtyak. It is\r\nimplemented in an unusual way, giving the control flow graphs of Roshtyak’s functions a unique look (see below).\r\nThe goal of control flow flattening is to obscure control flow relations between individual code blocks. \r\nControl flow is directed by a 32-bit control variable, which tracks the execution state, identifying the code block\r\nto be executed. This control variable is initialized at the start of each function to refer to the starting code block\r\n(which is frequently a nop block). The control variable is then modified at the end of each code block to identify\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 12 of 22\n\nthe next code block that should be executed. The modification is performed using some arithmetic instructions,\r\nsuch as add , sub , or xor .\r\nThere is a dispatcher using the control variable to route execution into the correct code block. This dispatcher is\r\nmade up of if/else blocks that are circularly linked into a loop. Each dispatcher block takes the control variable\r\nand masks it using arithmetic instructions to check if it should route execution into the code block that it is\r\nguarding. What’s interesting here is there are multiple points of entry from the code blocks into the dispatcher\r\nloop, giving the control flow graphs the jagged “sawblade” look in IDA. \r\nBranching is performed using a special code block containing an imul instruction. It relies on the previous block\r\nto compute a branch flag. This branch flag is multiplied using the imul instruction with a random constant, and\r\nthe result is added, subbed, or xored to the new control variable. This means that after the branch block, the\r\ncontrol variable will identify one of the two possible succeeding code blocks, depending on the value that was\r\ncomputed for the branch flag.\r\nControl flow graph of a function obfuscated using control flow flattening\r\nFunction activation keys\r\nRoshtyak’s obfuscated functions expect an extra argument, which we call an activation key. This activation key is\r\nused to decrypt all local constants, strings, variables, etc. If a function is called with a wrong activation key, the\r\ndecryption results in garbage plaintext, which will most likely cause Roshtyak to get stuck in an infinite loop\r\ninside the control flow dispatcher. This is because all constants used by the dispatcher (the initial value of the\r\ncontrol variable, the masks used by the dispatcher guards, and the constants used to jump to the next code block)\r\nare encrypted with the activation key. Without the correct activation key, the dispatcher simply does not know how\r\nto dispatch.\r\nReverse engineering a function is practically impossible without knowing the correct activation key. All strings,\r\nbuffers, and local variables/constants remain encrypted, all cross-references are lost, and worse, there is no control\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 13 of 22\n\nflow information. Only individual code blocks remain, with no way to know how they relate to each other.\r\nEach obfuscated function has to be called from somewhere, which means the code calling the function has to\r\nsupply the correct activation key. However, obtaining the activation key is not that easy. First, call targets are also\r\nencrypted with activation keys, so it’s impossible to find where a function is called from without knowing the\r\nright activation keys. Second, even the supplied activation key is encrypted with the activation key of the calling\r\nfunction. And that activation key got encrypted with the activation key of the next calling function. And so on,\r\nrecursively, all the way until the entry point function.\r\nThis brings us to how to deobfuscate the mess. The activation key of the entry point function must be there in\r\nplaintext. Using this activation key, it is possible to decrypt the call targets and activation keys of functions that\r\nare called directly from this entry point function. Applying this method recursively allows us to reconstruct the\r\nfull call graph along with the activation keys of all the functions. The only exceptions would be functions that\r\nwere never called and were left in by the compiler. These functions will probably remain a mystery, but since the\r\nsample does not use them, they are not that important from a malware analyst’s point of view.\r\nVariable masking\r\nSome variables are not stored in plaintext form but are masked using one or more arithmetic instructions. This\r\nmeans that if Roshtyak is not actively using a variable, it keeps the variable’s value in an obfuscated form.\r\nWhenever Roshtyak needs to use the variable, it has to first unmask it before it can use it. Conversely, after\r\nRoshtyak uses the variable, it converts it back into the masked form. This masking-based obfuscation method\r\nslightly complicates tracking variables during debugging and makes it harder to search memory for a known\r\nvariable value.\r\nLoop transformations\r\nRoshtyak is creative with some loop conditions. Instead of writing a loop like for (int i = 0; i \u003c 1690; i++) ,\r\nit transforms the loop into e.g. for (int32_t i = 0x06AB91EE; i != 0x70826068; i = i * -0x509FFFF +\r\n0xEC891BB1) . While both loops will execute exactly 1690 times, the second one is much harder to read. At first\r\nglance, it is not clear how many iterations the second loop executes (and if it even terminates). Tracking the\r\nnumber of loop iterations during debugging is also much harder in the second case.\r\nPacking\r\nAs mentioned, Roshtyak’s core is hidden behind multiple layers of packing. While all the layers look like they\r\nwere originally compiled into PE files, all but the strictly necessary data (entry point, sections, imports, and\r\nrelocations) were stripped away. Furthermore, Roshtyak supports two custom formats for storing the stripped PE\r\nfile information, and the layers take turns on what format they use. Additionally, parts of the custom formats are\r\nencrypted, sometimes using keys generated based on the results of various anti-analysis checks.\r\nThis makes it difficult to unpack Roshtyak’s layers statically into a standalone PE file. First, one would have to\r\nreverse engineer the custom formats and figure out how to decrypt the encrypted parts. Then, one would have to\r\nreconstruct the PE header, the sections, the section headers, and the import table (the relocation table doesn’t need\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 14 of 22\n\nto be reconstructed since relocations can just be turned off). While this is all perfectly doable (and can be\r\nsimplified using libraries like LIEF), it might take a significant amount of time. Adding to this that the layers are\r\nsometimes interdependent, it might be easier to just analyze Roshtyak dynamically in memory.\r\nA section header in one of the custom PE-like file formats: raw_size corresponds to SizeOfRawData, raw_size +\r\nvirtual_padding_size is effectively VirtualSize. There is no VirtualAddress or PointerToRawData equivalent\r\nbecause the sections are loaded sequen\r\nOther obfuscation techniques\r\nIn addition to the above-described techniques, Roshtyak also uses other obfuscation techniques, including:\r\nJunk instruction insertion\r\nImport hashing\r\nFrequent memory wiping\r\nMixed boolean-arithmetic obfuscation\r\nRedundant threading\r\nHeavy polymorphism\r\nCore Functionality\r\nNow that we’ve described how Roshtyak protects itself, it might be interesting to also go over what it actually\r\ndoes. Roshtyak’s DLL is relatively large, over a megabyte, but its functionality is surprisingly simple once you\r\neliminate all the obfuscation. Its main purpose is to download further payloads to execute. In addition, it does the\r\nusual evil malware stuff, namely establishing persistence, escalating privileges, lateral movement, and exfiltrating\r\ninformation about the victim.\r\nPersistence\r\nRoshtyak first generates a random file name in %SystemRoot%\\Temp and moves its DLL image there. The\r\ngenerated file name consists of two to eight random lowercase characters concatenated with a random extension\r\nchosen from a hardcoded list. The PRNG used to generate this file name is seeded with the volume serial number\r\nof C:\\ . The sample we analyzed hardcoded seven extensions ( .log , .tmp , .loc , .dmp , .out , .ttf ,\r\nand .etl ). We observed other extensions being used in other samples, suggesting this list is somewhat dynamic.\r\nWith a small probability, Roshtyak will also use a randomly generated extension. Once fully constructed, the full\r\npath to the Roshtyak DLL might look like e.g. C:\\Windows\\Temp\\wcdp.etl .\r\nAfter the DLL image is moved to the new filesystem path, Roshtyak stomps its Modified timestamp to the\r\ncurrent system time. It then proceeds to set up a RunOnce(Ex) registry key to actually establish persistence. The\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 15 of 22\n\nregistry entry is created using the previously described indirect registry write technique. The command inserted\r\ninto the key might look as follows:\r\nRUNDLL32.EXE SHELL32.DLL,ShellExec_RunDLL REGSVR32.EXE -U /s \"C:\\Windows\\Temp\\wcdp.etl.\"\r\nThere are a couple of things to note here. First, regsvr32 doesn’t care about the extensions of the DLLs it loads,\r\nallowing Roshtyak to hide under an innocent-looking extension such as .log . Second, the /s parameter puts\r\nregsvr32 into silent mode. Without it, regsvr32 would complain that it did not find an export named\r\nDllUnregisterServer . Finally, notice the trailing period character at the end of the path. This period is removed\r\nduring path normalization, so it practically has no effect on the command. We are not exactly sure what the\r\nauthor’s original intention behind including this period character is. It looks like it could have been designed to\r\ntrick some anti-malware software into not being able to connect the persistence entry with the payload on the\r\nfilesystem.\r\nBy default, Roshtyak uses the HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce key for persistence.\r\nHowever, under some circumstances (such as when it detects that Kaspersky is running by checking for a process\r\nnamed avp.exe ) the key HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx will be used instead.\r\nThe RunOnceEx key is capable of loading a DLL, so when using this key, Roshtyak specifies shell32.dll\r\ndirectly, omitting the use rundll32 .\r\nA RunOnceEx persistence entry established by Roshtyak\r\nPrivilege escalation\r\nRoshtyak uses both UAC bypasses and regular EoP exploits in an attempt to elevate its privileges. Unlike many\r\nother pieces of malware, which just blindly execute whatever UAC bypasses/exploits the authors could find,\r\nRoshtyak makes efforts to figure out if the privilege escalation method is even likely to be successful. This was\r\nprobably implemented to lower the chances of detection due to the unnecessary usage of incompatible\r\nbypasses/exploits. For UAC bypasses, this involves checking the ConsentPromptBehaviorAdmin and\r\nConsentPromptBehaviorUser registry keys. For EoP exploits, this is about checking the Windows build number\r\nand patch level.\r\nBesides checking the ConsentPromptBehavior(Admin|User) keys, Roshtyak performs other sanity checks to\r\nensure that it should proceed with the UAC bypass. Namely, it checks for admin privileges using\r\nCheckTokenMembership with the SID S-1-5-32-544 ( DOMAIN_ALIAS_RID_ADMINS ). It also inspects the value of\r\nthe DbgElevationEnabled flag in KUSER_SHARED_DATA.SharedDataFlags . This is an undocumented flag that is\r\nset if UAC is enabled. Finally, there are AV checks for BitDefender (detected by the module atcuf32.dll ),\r\nKaspersky (process avp.exe ), and our own Avast/AVG (module aswhook.dll ). If one of these AVs is detected,\r\nRoshtyak avoids selected UAC bypass techniques, presumably the ones that might result in detection.\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 16 of 22\n\nAs for the actual UAC bypasses, there are two main methods implemented. The first is an implementation of the\r\naptly named ucmDccwCOM method from UACMe. Interestingly when this method is executed, Roshtyak\r\ntemporarily masquerades its process as explorer.exe by overwriting FullDllName and BaseDllName in the\r\n_LDR_MODULE structure corresponding to the main executable module. The payload launched by this method\r\nis a randomly named LNK file, dropped into %TEMP% using the IShellLink COM interface. This LNK file is\r\ndesigned to relaunch the Roshtyak DLL, through LOLBins such as advpack or register-cimprovider .\r\nThe second method is more of a UAC bypass framework than a specific bypass method, because multiple UAC\r\nbypass methods follow the same simple pattern: first registering some specific shell open command and then\r\nexecuting an autoelevating Windows binary (which internally triggers the shell open command). For instance, a\r\nUAC bypass might be accomplished by writing a payload command to HKCU\\Software\\Classes\\ms-settings\\shell\\open\\command and then executing fodhelper.exe from %windir%\\system32 . Basically, the\r\nsame bypass can be achieved by substituting the pair ms-settings / fodhelper.exe with other pairs, such as\r\nmscfile / eventvwr.exe . Roshtyak uses the following six pairs to bypass UAC:\r\nLet’s now look at the kernel exploits (CVE-2020-1054 and CVE-2021-1732) Roshtyak uses to escalate privileges.\r\nAs is often the case in Roshtyak, these exploits are stored encrypted and are only decrypted on demand.\r\nInterestingly, once decrypted, the exploits turn out to be regular PE files with completely valid headers (unlike the\r\nother layers in Roshtyak, which are either in shellcode form or stored in a custom stripped PE format). Moreover,\r\nthe exploits lack the obfuscation given to the rest of Roshtyak, so their code is immediately decompilable, and\r\nonly some basic string encryption is used. We don’t know why the attackers left these exploits so exposed, but it\r\nmight be due to the difference in bitness. While Roshtyak itself is x86 code (most of the time running under\r\nWoW64), the exploits are x64 (which makes sense considering they exploit vulnerabilities in 64-bit code). It could\r\nbe that the obfuscation tools used by Roshtyak’s authors were designed to work on x86 and are not portable to\r\nx64.\r\nSnippet from Roshtyak’s exploit for CVE-2020-1054, scanning through IsMenu to find the offset to\r\nHMValidateHandle.\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 17 of 22\n\nTo execute the exploits, Roshtyak spawns (the AMD64 version of) winver.exe and gets the exploit code to run\r\nthere using the KernelCallbackTable injection method. Roshtyak’s implementation of this injection method\r\nessentially matches a public PoC, with the biggest difference being the usage of slightly different API functions\r\ndue to the need for cross-subsystem injection (e.g. NtWow64QueryInformationProcess64 instead of\r\nNtQueryInformationProcess or NtWow64ReadVirtualMemory64 instead of ReadProcessMemory ). The code\r\ninjected into winver.exe is not the exploit PE itself but rather a slightly obfuscated shellcode, designed to load\r\nthe exploit PE into memory.\r\nThe kernel exploits target certain unpatched versions of Windows. Specifically, CVE-2020-1054 is only used on\r\nWindows 7 systems where the revision number is not higher than 24552 . On the other hand, the exploit for CVE-2021-1732 runs on Windows 10, with the targeted build number range being from 16353 to 19042 . Before\r\nexploiting CVE-2021-1732, Roshtyak also scans through installed update packages to see if a patch for the\r\nvulnerability is installed. It does this by enumerating the registry keys under\r\nHKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\Packages and checking if the\r\npackage for KB4601319 (or higher) is present.\r\nLateral movement\r\nWhen it comes to lateral movement, Roshtyak simply uses the tried and tested PsExec tool. Before executing\r\nPsExec, Roshtyak ensures it makes sense to run it by checking for a SID matching the “well-known”\r\nWinAccountDomainAdminsSid group. If domain admin rights are not detected, Roshtyak skips its lateral\r\nmovement phase entirely.\r\nRoshtyak attempts to get around detection by setting Defender exclusions, as PsExec is often flagged as a\r\nhacktool (for good reasons). It sets a path exclusion for %TEMP% (where it will drop PsExec and other files used\r\nfor lateral movement). Later, it sets up a process exclusion for the exact path from which PsExec will be\r\nexecuted. \r\nWhile we would expect PsExec to be bundled inside Roshtyak, it turns out Roshtyak downloads it on demand\r\nfrom https://download.sysinternals[.]com/files/PSTools.zip . The downloaded zip archive is dropped into\r\n%TEMP% under a random name with the .zip extension. PsExec is then unzipped from this archive using the\r\nWindows Shell COM interface ( IShellDispatch ) into a randomly named .exe file in %TEMP% .\r\nThe payload to be executed by PsExec is a self-extracting package created by a tool called IExpress. This is an\r\narchaic installer that’s part of Windows, which is probably why it’s used, since Roshtyak can rely on it already\r\nbeing on the victim machine. The installer generation is configured by a text file using the Self Extraction\r\nDirective (SED) syntax. \r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 18 of 22\n\nRoshtyak’s IExpress configuration template\r\nRoshtyak uses a SED configuration template with three placeholders ( %1 , %2 , and %3 ) that it substitutes with\r\nreal values at runtime. As seen above, the configuration template was written in mixed-case, which is frequently\r\nused in Raspberry Robin in general. Once the SED configuration is prepared, it is written into a randomly named\r\n.txt file in %TEMP% . Then, iexpress is invoked to generate the payload using a command such as\r\nC:\\Windows\\iexpress.exe /n /q \u003cpath_to_sed_config\u003e . The generated payload is dumped into a randomly\r\nnamed .exe file in %TEMP% , as configured by the TargetName directive (placeholder %1 ).\r\nOnce the payload is generated, Roshtyak proceeds to actually run PsExec. There are two ways Roshtyak can\r\nexecute PsExec. The first one uses the command \u003cpath_to_psexec\u003e \\\\* -accepteula -c -d -s\r\n\u003cpath_to_payload\u003e . Here, the \\\\* wildcard instructs PsExec to run the payload on all computers in the current\r\ndomain. Alternatively, Roshtyak might run the command \u003cpath_to_psexec\u003e @\u003cpath_to_target_file\u003e -\r\naccepteula -c -d -s \u003cpath_to_payload\u003e . Here, the target_file is a text file containing a specific list of\r\ncomputers to run the payload on. Roshtyak builds this list by enumerating Active Directory objects using API\r\nfunctions exported from activeds.dll .\r\nProfiling the victim\r\nUSB worms tend to have a life of their own. Since their worming behavior is usually completely automated, the\r\nthreat actor who initially deployed the worm doesn’t necessarily have full control over where it spreads. This is\r\nwhy it’s important for threat actors to have the worm beacon back to their C\u0026C servers. With a beaconing\r\nmechanism in place, the threat actor can be informed about all the machines under their control and can use this\r\nknowledge to manage the worm as a whole.\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 19 of 22\n\nThe outgoing beaconing messages typically contain some information about the infected machine. This helps\r\nfinancially-motivated cybercriminals decide on how to best monetize the infection. Roshtyak is no exception to\r\nthis, and it collects a lot of information about each infected victim. Roshtyak concatenates all the collected\r\ninformation into a large string, using semicolons as delimiters. This large string is then exfiltrated to one of\r\nRoshtyak’s C\u0026C servers. The exfiltrated pieces of information are listed below, in order of concatenation.\r\nExternal IP address (obtained during a Tor connectivity check)\r\nA string hardcoded into Roshtyak’s code, e.g. AFF123 (we can’t be sure what’s the meaning behind this,\r\nbut it looks like an affiliate ID)\r\nA 16-bit hash of the DLL’s PE header (with some fields zeroed out) xored with the lower 16 bits of its\r\nTimeDateStamp . The TimeDateStamp appears to be specially crafted so that the xor results in a known\r\nvalue. This could function as a tamper check or a watermark.\r\nCreation timestamp of the System Volume Information folder on the system drive\r\nThe volume serial number of the system drive\r\nProcessor count ( GetActiveProcessorCount )\r\nIsWow64Process ( _PROCESS_EXTENDED_BASIC_INFORMATION.Flags \u0026 2 )\r\nWindows version ( KUSER_SHARED_DATA.Nt(Major|Minor)Version )\r\nWindows product type ( KUSER_SHARED_DATA.NtProductType )\r\nWindows build number ( PEB.OSBuildNumber )\r\nLocal administrative privileges ( ZwQueryInformationToken(TokenGroups) / CheckTokenMembership , check\r\nfor DOMAIN_ALIAS_RID_ADMINS )\r\nDomain administrative privileges (check for WinAccountDomainAdminsSid / WinAccountDomainUsersSid )\r\nSystem time ( KUSER_SHARED_DATA.SystemTime )\r\nTime zone ( KUSER_SHARED_DATA.TimeZoneBias )\r\nSystem locale ( NtQueryDefaultLocale(0) )\r\nUser locale ( NtQueryDefaultLocale(1) )\r\nEnvironment variables ( username , computername , userdomain , userdnsdomain , and logonserver )\r\nJava version ( GetFileVersionInfo(\"javaw.exe\") -\u003e VerQueryValue )\r\nProcessor information ( cpuid to obtain the Processor Brand String )\r\nPath to the image of the main executable module ( NtQueryVirtualMemory(MemorySectionName) )\r\nProduct ID and serial number of the main physical drive\r\n( DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY, StorageDeviceProperty) )\r\nMAC address of the default gateway ( GetBestRoute -\u003e GetIpNetTable )\r\nMAC addresses of all network adapters ( GetAdaptersInfo )\r\nInstalled antivirus software ( root\\securitycenter2 -\u003e SELECT * FROM AntiVirusProduct )\r\nDisplay device information ( DeviceId , DeviceString , dmPelsWidth , dmPelsHeight ,\r\ndmDisplayFrequency ) ( EnumDisplayDevices -\u003e EnumDisplaySettings )\r\nActive processes ( NtQuerySystemInformation(SystemProcessInformation) )\r\nScreenshot encoded in base64 ( gdi32 method)\r\nBeaconing\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 20 of 22\n\nOnce collected, Roshtyak sends the victim profile to one of its C\u0026C servers. The profile is sent over the Tor\r\nnetwork, using a custom comms module Roshtyak injects into a newly spawned process. The C\u0026C server\r\nprocesses the exfiltrated profile and might respond with a shellcode payload for the core module to execute.\r\nLet’s now take a closer look at this whole process. It’s worth mentioning that before generating any malicious\r\ntraffic, Roshtyak first performs a Tor connectivity check. This is done by contacting 28 legitimate and well-known\r\n.onion addresses in random order and checking if at least one of them responds. If none of them respond,\r\nRoshtyak doesn’t even attempt to contact its C\u0026C, as it would most likely not get through to it anyway.\r\nAs for the actual C\u0026C communication, Roshtyak contains 35 hardcoded V2 onion addresses (e.g.\r\nip2djbz3xidmkmkw:53148 , see our IoC repository for the full list). Like during the connectivity check, Roshtyak\r\niterates through them in random order and attempts to contact each of them until one responds. Note that while V2\r\nonion addresses are officially deprecated in favor of V3 addresses (and the Tor Browser no longer supports them\r\nin its latest version) they still appear to be functional enough for Roshtyak’s nefarious purposes.\r\nRoshtyak’s hardcoded C\u0026C addresses\r\nThe victim profile is sent in the URL path, appended to the V2 onion address, along with the / character. As the\r\nraw profile might contain characters forbidden for use in URLs, the profile is wrapped in a custom structure and\r\nencoded using Base64. The very first 0x10 bytes of the custom structure serve as an encryption key, with the rest\r\nof the structure being encrypted. The custom structure also contains a 64-bit hash of the victim profile, which\r\npresumably serves as an integrity check. Interestingly, the custom structure might get its end padded with random\r\nbytes. Note that the full path could be pretty large, as it contains a doubly Base64-encoded screenshot. The authors\r\nof Roshtyak were probably aware that the URL path is not suitable for sending large amounts of data and decided\r\nto cap the length of the victim profile at 0x20000 bytes. If the screenshot makes the exfiltrated profile larger than\r\nthis limit, it isn’t included.\r\nWhen the full onion URL is constructed, Roshtyak goes ahead to launch its Tor comms module. It first spawns a\r\ndummy process to host the comms module. This dummy process is randomly chosen and can be one of\r\ndllhost.exe , regsvr32.exe , or rundll32.exe . The comms module is injected into the newly spawned\r\nprocess using a shared section, obfuscated through the previously described shellcode hiding technique. The\r\ncomms module is then executed via NtQueueApcThreadEx , using the already discussed ntdll gadget trick. The\r\ninjected comms module is a custom build of an open-source Tor library packed in three additional protective\r\nshellcode layers.\r\nThe core module communicates with the comms module using shared sections as an IPC mechanism. Both\r\nmodules synchronously use the same PRNG with the same seed ( KUSER_SHARED_DATA.Cookie ) to generate the\r\nsame section name. Both then map this named section into their respective address spaces and communicate with\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 21 of 22\n\neach other by reading/writing to it. The data read/written into the section is encrypted with RC4 (the key also\r\ngenerated using the synchronized PRNGs).\r\nThe communication between the core module and the comms module follows a simple request/response pattern.\r\nThe core module writes an encrypted onion URL (including the URL path to exfiltrate) into the shared section.\r\nThe comms module then decrypts the URL and makes an HTTP request over Tor to it. The core module waits for\r\nthe comms module to write the encrypted HTTP response back to the shared section. Once it’s there, the core\r\nmodule decrypts it and unwraps it from a custom format (which includes decrypting it yet again and computing a\r\nhash to check the payload’s integrity). The decrypted payload might include a shellcode for the core module to\r\nexecute. If the shellcode is present, the core module allocates a huge chunk of memory, hides the shellcode there\r\nusing the shellcode hiding technique, and executes it in a new thread. This new thread is hidden using the\r\nNtSetInformationThread -\u003e ThreadHideFromDebugger technique (including a follow-up anti-hooking check\r\nusing NtGetInformationThread to confirm that the NtSetInformationThread call did indeed succeed).\r\nConclusion\r\nIn this blog post, we took a technical deep dive into Roshtyak, the backdoor payload associated with Raspberry\r\nRobin. The main focus was to describe how to deal with Roshtyak’s protection mechanisms. We showed some\r\nnever-before-seen anti-debugger/anti-sandbox/anti-VM tricks and discussed Roshtyak’s heavy obfuscation. We\r\nalso described Roshtyak’s core functionality. Specifically, we detailed how it establishes persistence, escalates\r\nprivileges, moves laterally, and uses Tor to download further payloads.\r\nWe have to admit that reverse engineering Roshtyak was certainly no easy task. The combination of heavy\r\nobfuscation and numerous advanced anti-analysis tricks made it a considerable challenge. Nick Harbour, if you’re\r\nlooking for something to repurpose for next year’s final Flare-On challenge, this might be it.\r\nIndicators of Compromise (IoCs)\r\nIoCs are available at https://github.com/avast/ioc/tree/master/RaspberryRobin.\r\nJan Vojtěšek\r\nAuthor at Avast Threat Labs\r\nSource: https://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nhttps://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/\r\nPage 22 of 22",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia",
		"MITRE"
	],
	"references": [
		"https://decoded.avast.io/janvojtesek/raspberry-robins-roshtyak-a-little-lesson-in-trickery/"
	],
	"report_names": [
		"raspberry-robins-roshtyak-a-little-lesson-in-trickery"
	],
	"threat_actors": [
		{
			"id": "2864e40a-f233-4618-ac61-b03760a41cbb",
			"created_at": "2023-12-01T02:02:34.272108Z",
			"updated_at": "2026-04-10T02:00:04.97558Z",
			"deleted_at": null,
			"main_name": "WildCard",
			"aliases": [],
			"source_name": "ETDA:WildCard",
			"tools": [
				"RustDown",
				"SysJoker"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "50068c14-343c-4491-b568-df41dd59551c",
			"created_at": "2022-10-25T15:50:23.253218Z",
			"updated_at": "2026-04-10T02:00:05.234464Z",
			"deleted_at": null,
			"main_name": "Indrik Spider",
			"aliases": [
				"Indrik Spider",
				"Evil Corp",
				"Manatee Tempest",
				"DEV-0243",
				"UNC2165"
			],
			"source_name": "MITRE:Indrik Spider",
			"tools": [
				"Mimikatz",
				"PsExec",
				"Dridex",
				"WastedLocker",
				"BitPaymer",
				"Cobalt Strike"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "b296f34c-c424-41da-98bf-90312a5df8ef",
			"created_at": "2024-06-19T02:03:08.027585Z",
			"updated_at": "2026-04-10T02:00:03.621193Z",
			"deleted_at": null,
			"main_name": "GOLD DRAKE",
			"aliases": [
				"Evil Corp",
				"Indrik Spider ",
				"Manatee Tempest "
			],
			"source_name": "Secureworks:GOLD DRAKE",
			"tools": [
				"BitPaymer",
				"Cobalt Strike",
				"Covenant",
				"Donut",
				"Dridex",
				"Hades",
				"Koadic",
				"LockBit",
				"Macaw Locker",
				"Mimikatz",
				"Payload.Bin",
				"Phoenix CryptoLocker",
				"PowerShell Empire",
				"PowerSploit",
				"SocGholish",
				"WastedLocker"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "9806f226-935f-48eb-b138-6616c9bb9d69",
			"created_at": "2022-10-25T16:07:23.73153Z",
			"updated_at": "2026-04-10T02:00:04.729977Z",
			"deleted_at": null,
			"main_name": "Indrik Spider",
			"aliases": [
				"Blue Lelantos",
				"DEV-0243",
				"Evil Corp",
				"G0119",
				"Gold Drake",
				"Gold Winter",
				"Manatee Tempest",
				"Mustard Tempest",
				"UNC2165"
			],
			"source_name": "ETDA:Indrik Spider",
			"tools": [
				"Advanced Port Scanner",
				"Agentemis",
				"Babuk",
				"Babuk Locker",
				"Babyk",
				"BitPaymer",
				"Bugat",
				"Bugat v5",
				"Cobalt Strike",
				"CobaltStrike",
				"Cridex",
				"Dridex",
				"EmPyre",
				"EmpireProject",
				"FAKEUPDATES",
				"FakeUpdate",
				"Feodo",
				"FriedEx",
				"Hades",
				"IEncrypt",
				"LINK_MSIEXEC",
				"MEGAsync",
				"Macaw Locker",
				"Metasploit",
				"Mimikatz",
				"PayloadBIN",
				"Phoenix Locker",
				"PowerShell Empire",
				"PowerSploit",
				"PsExec",
				"QNAP-Worm",
				"Raspberry Robin",
				"RaspberryRobin",
				"SocGholish",
				"Vasa Locker",
				"WastedLoader",
				"WastedLocker",
				"cobeacon",
				"wp_encrypt"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "6c4f98b3-fe14-42d6-beaa-866395455e52",
			"created_at": "2023-01-06T13:46:39.169554Z",
			"updated_at": "2026-04-10T02:00:03.23458Z",
			"deleted_at": null,
			"main_name": "Evil Corp",
			"aliases": [
				"GOLD DRAKE"
			],
			"source_name": "MISPGALAXY:Evil Corp",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "256a6a2d-e8a2-4497-b399-628a7fad4b3e",
			"created_at": "2023-11-30T02:00:07.299845Z",
			"updated_at": "2026-04-10T02:00:03.484788Z",
			"deleted_at": null,
			"main_name": "WildCard",
			"aliases": [],
			"source_name": "MISPGALAXY:WildCard",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		}
	],
	"ts_created_at": 1775434932,
	"ts_updated_at": 1775792121,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/54792d7f0b0347067fb3369c02a2e2906c2ddcd6.pdf",
		"text": "https://archive.orkl.eu/54792d7f0b0347067fb3369c02a2e2906c2ddcd6.txt",
		"img": "https://archive.orkl.eu/54792d7f0b0347067fb3369c02a2e2906c2ddcd6.jpg"
	}
}