Dynamic malware analysis - or sandboxing - has become a central piece of every major security solution... and so has the presence of evasive code in malicious software. Practically all variants of current threats include some sort of sandbox-detection logic.
One very simple form of evasive code is to delay execution of any suspicious functionality for a certain amount of time - the basic idea is to leverage the fact that dynamic analysis systems monitor execution for a limited amount of time, and in the absence of malicious behavior classify a program as benign. On a victim machine, on the other hand, delaying behavior for a few minutes does not have a real impact, allowing the attacker to easily achieve different behavior in the analysis environment and on a real target machine.
The easiest, and definitely most prevalent method of stalling behavior is to make a program “sleep” for a certain amount of time. Since this is such a common behavior, most analysis sandboxes are able to detect this kind of evasion, and in most cases, simply “skip” the sleep. While this sounds like a simple solution, it can have a wide range of unintended effects as we will see in this blog post.
The Power of Procrastination
In our whitepaper Automated Detection and Mitigation of Execution-Stalling Malicious Code we describe the basic principle behind stalling code used against sandboxes:
Stalling code is typically executed before any malicious behavior. The attacker’s aim is to delay the execution of the malicious activity long enough so that an automated dynamic analysis system fails to extract the interesting malicious behavior.
Code stalling can be achieved in a number of ways: Waiting for a specific action of the user, wasting CPU cycles computing useless data, or simply delaying execution using a call to the Sleep() function.
According to MSDN
VOID WINAPI Sleep( _In_ DWORD dwMilliseconds);
Suspends the execution of the current thread until the time-out interval elapses.
a call to Sleep() will delay the execution of the current thread by the time passed as argument. Most sandboxes monitor the system- or API-calls of a program under analysis and will therefore see this evasion attempt. Therefore, the sandbox is able to detect, and in most cases even react to this, either by patching the delay argument passed to the operating system, by replacing the called function with a custom implementation, or simply by returning immediately to the calling code (skipping the sleep altogether).
Detecting Sleep Patching
Recently, we have come across an interesting malware family that uses this anti-evasion trick used by sandboxes to detect the presence of the analysis environment (one could call it an anti-evasion-evasion trick…)
This malware detects sleep-patching using the rdtsc instruction in combination with Sleep() to check acceleration of execution, as one can see in the following code extract:
In summary, this code:
- executes rdtsc, which reads the CPU’s timestamp counter, and stores the timestamp in a temporary value,
- invokes Sleep() to delay execution,
- re-executes rdtsc, and
- compares the two timestamps.
Sleep Patching Using High-Resolution Dynamic Analysis
Different from traditional sandboxes, Lastline’s high-resolution analysis engine monitors more than just the interaction of programs with the operating system (or API functions). Our engine sees - and can thus influence - every instruction that is executed by the malicious program, not just API function invocations. Thus, since we can also manipulate the values returned by the rdtsc instruction, we can maintain a consistent execution state even when patching a sleep, for example by fast-forwarding the timestamps returned by the CPU to the program each time a sleep is skipped or accelerated.
As a result, the program can no-longer distinguish if a sleep was truly executed in full, or if the analysis system simply forwarded the time inside the sandbox.
Side-Effects of Sleep Patching: User Emulation
We found other interesting side-effects introduced by sleep patching that might not be directly related to deliberate sandbox detection, as can be seen in the following piece of code:
Here, the malware sample checks for user-activity by repeatedly checking the cursor position (in 30 second intervals).
Most sandboxes have some mechanism to trigger (or simulate) user activity. Typically this means repeatedly changing cursor position, opening new windows, click on dialog-boxes, etc, just to name a few.
In the code above, the malware sample uses the Sleep() method not for delaying malicious activity, but merely to have a simple way for checking that some user-activity --mouse movement, in this case-- was observed within a certain time period. Clearly, if a sandbox naively accelerates this code by patching the sleeps, the behavior that was expected to happen while the malware sample is dormant will not happen, and as a consequence, the presence of the analysis environment will be detected, evading analysis.
Therefore, again, a naive approach to execution-stalling will allow an attacker identify the presence of the sandbox, or, as in this case, the absence of a real user, evading analysis.
Side-Effects of Sleep Patching: Race Conditions
Another interesting problem related to sleep-patching are race conditions: Race conditions are a non-trivial programming error, where multi-threaded code needs to be executed in a specific order to work correctly.
One (ugly, as many programmers would agree) way of avoiding race conditions is to delay code depending on completion of another task by the amount of time this task typically needs.
In the presence of sleep-patching, however, this approach is bound to fail, as the sandbox influences the amount of time that is slept. One such example can be seen in the code below, extracted from another malware family:
In this code, the malware decrypts and executes code from a dropped file, cleaning up after the program has executed (by deleting the file). Between invoking and deleting the program, the malware sample uses a - one already guessed - sleep to make sure the program is started before it is deleted. Once again, by patching the sleep incorrectly, the sandbox breaks this logic, causing the malware to delete the payload before it is ever executed.
A more complex example can be seen below:
Here, malware reads encrypted code from a file on disc and executes it in the context of the current process using a separate thread. Once the payload has been started, the main thread goes into an infinite sleep (but this could equally be a long sleep), before executing ExitProcess (which terminates the execution of all threads in the process).
If this sleep is patched to be shorter than the execution of the malicious payload, the process is terminated before completing its activity, unintentionally stopping the process before it can completely reveal its malicious behavior.
Timing attacks are common to most malware families today. While some of these timing attacks are easy to detect, naive approaches to overcoming these evasion attempts often cause more harm than they do good, opening gates to evasion attacks based on anti-evasion systems.
Using high-resolution dynamic analysis and leveraging its insight into each instruction that is executed by the malicious program, the Lastline sandbox is able to foil these attacks and reveal the malicious behavior.