Sleep masking
Because sometimes set sleep_mask "true"; isnt enough
Last updated
Because sometimes set sleep_mask "true"; isnt enough
Last updated
Why do we need sleep masking?
What about the Sleep Mask Kit?
Normal beacon: Callback -> sleep 10s -> callback
To hide the memory indicators during the sleep phase, it adds a step to the beacon callback cycle:
Sleep protected beacon: Callback -> encrypt memory -> sleep 10s -> decrypt memory -> callback
The way the encryption and decryption of memory is performed, is by applying a userland hook to the kernel32 WinAPI Sleep(), which Beacon uses to go to sleep. This userland hook is used to trigger the encryption decryption routine.
This prevents memory scanners like BeaconEye from finding your Beacon IOCs in memory. Links to existing implementations you can reference from are at the bottom of this blogpost, under the Other implementations list.
Demo time!
Materials list:
BeaconEye (https://github.com/CCob/BeaconEye)
LockdExeDemo (https://github.com/waldo-irc/LockdExeDemo)
ShellcodeFluctuation (https://github.com/mgeeky/ShellcodeFluctuation)
Cobalt Strike 4.x (the 3.x trial works fine if you don't have a licensed copy of 4.x)
First, disable the sleep mask if you are running Cobalt Strike 4.4 and above. This can be set in the Malleable C2 profile.
Lets run our teamserver and a beacon.
Now, on the victim, lets scan the beacon process using the following yara rule:
We can see that the YARA rule for Cobalt Strike in memory found a match in the process, at the address 0xc0d10. We can confirm this in x64dbg.
Lets also run BeaconEye.
As you can see, Cobalt Strike's beacon is easily caught in memory. Now, lets test it with different types of sleep protection.
Before we use the different sleep protection PoCs, we should understand how they work.
The first PoC we will be using is named ShellcodeFluctuation, by mgeeky. I chose this PoC as it is one of the simpler to understand implementations of the sleep masking concept.
The purpose of this PoC is to encrypt the injected Beacon shellcode while it sleeps. It is performed as such. based on the README:
The main concept in the code can be understood as follows:
The hookSleep() function redirects function calls to kernel32!Sleep to our a custom function, MySleep(). This is done by replacing bytes at the start of the Sleep() function with instructions to jump to our custom function, redirection execution.
2. The custom sleep function MySleep encrypts the shellcode, sleeps then decrypts the shellcode.
This is the core concept of this PoC. It redirects Beacon's calls to Sleep() with a custom function that encrypts and decrypts Beacon in memory before and after sleeping. This way, beacon shellcode is encrypted in memory while it sleeps. It also sets the memory permissions to RW from RX, to appear as non executable memory, which is less suspicious to memory scanners.
Compile and use https://github.com/mgeeky/ShellcodeFluctuation, replacing the payload with your beacon.
Lets run the YARA rule and BeaconEye again.
First, we can observe that the YARA rule does not match.
It does shows 1 implanted PE in PE-Sieve, another popular memory scanning tool. The explanation for which is in the README of the repo:
My current assumption is that PE-Sieve is picking up on the same traits that Moneta does (described below in Modified code in kernel32.dll) - the fact that PE mapped module has a non-empty Working set, being an evident fact of code injection of some sort. That is labeled as Implanted PE / Implanted. If that's the case, conclusion is similar to the Moneta's observation. I don't think we should care that much about that IOC detection-wise.
While it is true that no bytes in kernel32 differ from its file on disk due to the unhooking of Sleep() before sleeping, it should be noted that the due to the hook, private bytes are created within kernel32.dll, which is an indication of modification that remains even after the bytes have been reverted to their original values. I am not sure how often this IOC would be flagged, but it is something to take note of when using inline hooks for sleep masking.
BeaconEye still catches us like before. This is because BeaconEye locates Beacon via its heap indicators rather than shellcode:
Now that we know BeaconEye checks the heap for Beacon signatures, why don't we encrypt the heap and see if it still catches us?
The second PoC we will be using is LockdExeDemo, by Waldo-IRC. I chose this PoC mostly because it already comes with a blog post explaining itself, as well as heap encryption.
Refer to the blog post for LockdExeDemo, Hook Heaps and Live Free and understand:
What is heap encryption
How the PoC works
Download the https://github.com/waldo-irc/LockdExeDemo and replace the shellcode etc.
As you can see, with heap encryption, heap based signature detections are easily evaded.
While these PoCs are good for understanding sleep masking concepts, they are not yet operationally usable. To incorporate these techniques into an operationally usable loader, you may want to consider some of the following:
What memory stays RX or in plaintext during sleep? e.g. the sleep masking function
Do we create abnormal memory artifacts? e.g. abnormal private bytes
What operational implications does the implementation have? e.g. suspending all threads during sleep
Why do these IOCs/implications exist? Can they be removed?
Other implementations
SleepyCrypt <-- whole PE image encryption
dotnet-gargoyle <-- .NET sleep masking of AppDomain
TitanLdr <-- Cobalt Strike UDRL implementation of heap encryption (What are the benefits of doing it from the UDRL?)
Sleep Mask Kit <-- Cobalt Strike builtin sleep masking
GPUSleep <-- Hide in GPU memory
Implementations have pros and cons, e.g. Cobalt Strike specific, inline hooks etc. It is up to the operator to understand them and decide which implementation is best suited for your operational needs.
Hopefully this explains how sleep protection can help evade host based detection techniques during an engagement :)