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.
Summary
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.