How can we isolate suspicious processes in Windows and not destroy the OS? How can we create a reliable and Windows-compatible sandbox without hardware virtualization and kernel function hooking, but with the use of documented default OS security mechanisms? In this article we will be discussing the most common problems faced by sandbox developers (and, as a result, consumers). And of course we will also offer our own solutions.
Introduction, or how bad life is without a sandbox
Professionals all know a few axioms that they don't like to talk about. And what is there to say about axioms, anyway? They simply exist, just as clear as day. For example, here's one of them: a signature-based antivirus doesn't offer any protection. Okay, so it just doesn't protect anything, and that's that. People have gone over it a thousand times, complete with examples, nice presentations, and so on and so forth. And wide-spread nasties such as Ransomware are proof of the ineffectiveness of signature-based and heuristic technologies. All kinds of cryptors and obfuscators successfully solve the problem of defending long-known malware from being detected, so that for some time this malware is not detected by antivirus software. This is ample time for someone to come to harm and for someone else to profit.
It's not even about 0day. You can take old well-known malware, morph it, remove behavioral signatures (a couple days of work for a lazy person), and use it again and again until you get bored or get caught. At the same time, the people who sold the cure, so people would never come to harm again, act like they've got nothing to do with things anymore. They publish some newsletter and talk about Internet hygiene in all seriousness, forgetting to say that if the hygienic procedures would be fully complied with, there would be almost no need in antivirus software, especially commercial.
Sandboxes and aspects of their implementation
So, antivirus software doesn't save and sometimes even destroys what's already there. "Let's approach security from the opposite angle and isolate processes from one another," someone really smart once said. And indeed, it's nice when suspicious processes are executed in a somewhat isolated environment called a sandbox. When running inside a sandbox, malware can't escape and damage the system. This could be the ultimate solution, but using existing sandboxes is somewhat complicated…
Later on we are going to discuss all the details of building a sandbox, and they certainly will come in handy when you need to choose a means to isolate processes or HIPS (Host-based Intrusion Prevention System.)
Nuance 1, or one sandbox for all
Most sandboxes actually don't allow users to isolate processes. The truth is that in most uses the protected system is divided into two parts: the trusted and the untrusted. Typical processes are executed in the trusted part, and the untrusted part is reserved for isolated processes. This means that all isolated processes are executed in the same sandbox, have access to each other and each other's resources, and use the same registry and the same file system.
Malware can therefore gain a foothold in the sandbox and get its start with just one of the isolated applications (or several isolated applications, or any of them). Given that, sandboxes often don't log isolated process actions. Actions that are alarming to HIPS pass freely through sandboxes with just an isolation mark, and that is not very good.
How can we check that isolation indeed works this way? It's actually quite simple! Start two applications in a sandbox. For example, notepad.exe and wordpad.exe. Using notepad.exe, create a text file 1.txt.
Of course, the file will not be saved on the desktop, but in the "virtual" directory. Try to open it with Wordpad (fig. 3).
So, a file created with one isolated application can be opened with another isolated application. Put bluntly, it's not very isolating at all. But there is some protection from recording, isn't there? Let's change the contents (fig. 4).
Then save it. Now let's try to open 1.txt with notepad.exe. Of course, we open up notepad.exe in the sandbox (fig. 5).
Now here's what we were talking about. Two isolated applications are not actually isolated from one another. It's not really very clear what this isolation is for. Even an encryptor that does not have access to local folders on your computer can encrypt everything in a virtualized directory and, if its lucky, on network resources as well, because sandbox settings are the same for all isolated applications.
Nuance 2, or poor isolation
No, the isolated processes can't reach a trusted part of the system… but in most implementations only recording is restricted. Meaning they can read almost anything without limits and often have network access. This was probably done for better compatibility, but it cannot be called isolation.
Try and conduct this simple experiment with your sandbox. Create a directory on your hard drive. Like this:
E:\Photos. Put, let's say, a photo in it (fig. 6).
Start Internet Explorer in the sandbox and try to send this picture, for example, to rghost.
So? Did it work? It's not a good sign if the experiment was successful. It's even worse if you can't specify the directory an isolated application cannot access. And it's not good at all if isolated applications have the read permission for the data in directories of the current user.
The virtualization of the file system and registry is built on the principle of "copy on demand" in most implementations. So if a user tries to read a file, it is read from a source directory if there's no analogue in the virtual directory. And if there is such a file in the virtual directory, then the isolated application will work with it. The same can be said about virtual registries. And it's clear that when you try to record a file in a real directory, it is also recorded in the virtual file system. Almost always. If the malware is therefore "isolated" in a sandbox, it has full access to all the other "isolated" processes, to almost all data in the system for reading, and to virtualized (created by isolated applications) data (which is often open to all isolated applications) for recording.
Nuance 3, or "let's reinvent the wheel, it's fun"
We have always been amazed at the striking ability of some antivirus companies to tackle problems very straightforwardly, without thinking whether it is necessary to solve some individual problem at all, whether it is necessary to solve it this particular way or whether it is necessary to destroy something to build something else. It creates the feeling that companies are mindlessly snatching solutions from each other. However, as a wise man once said, who needs brains when you've got money. If you run the Wincheck utility by Red Plait, you can see exactly how isolation mechanisms are used and, at the same time, answer the question why the quality of isolation in 64-bit Windows is much lower than in 32-bit Windows. Wincheck is a fast and very reliable free utility that searches for rootkits in the system. Let's download Wincheck and start it. We will see something like this:
SDT entry C (ZwAdjustPrivilegesToken) hooked 872DB50E \SystemRoot\system32\DRIVERS\dirtyguard.sys!
SDT entry 16 (ZwAlpcConnectPort) hooked 872DB91A \SystemRoot\system32\DRIVERS\dirtyguard.sys!
SDT entry 17 (ZwAlpcCreatePort) hooked 872DB8C8 \SystemRoot\system32\DRIVERS\dirtyguard.sys!
SDT entry 170 (ZwSystemDebugControl) hooked 872DABCC \SystemRoot\system32\DRIVERS\dirtyguard.sys!
SDT entry 172 (ZwTerminateProcess) hooked 872DA534 \SystemRoot\system32\DRIVERS\dirtyguard.sys!
SDT entry 173 (ZwTerminateThread) hooked 872DA302 \SystemRoot\system32\DRIVERS\dirtyguard.sys!
Most sandbox implementations use rootkit technology hooking a substantial part of kernel functions. This listing shows the trivial hooks of system service descriptor table (SSDT) records, which is protected by PatchGuard (but more on that later). And many do exactly this.
By the way, the fact that sandboxes are now missing from the flagship products of some antivirus software companies can be explained by PatchGuard starting to control Shadow SSDT, which is responsible for working with windows and is located in win32k.sys. After all, it's possible to escape from the sandbox, for example, using
SetWindowsHookEx. To cut off this method you need to hook
NtUserSetWindowsHookEx in Shadow SSDT.
Let's have a look at the Wincheck results:
Shadow SDT: 90C16000, limit 339
win32k_sdt(NtGdiBitBlt) hooked, addr 872DF818 \SystemRoot\system32\DRIVERS\dirtyguard.sys
win32k_sdt(NtGdiDeleteObjectApp) hooked, addr 872E0160 \SystemRoot\system32\DRIVERS\dirtyguard.sys
win32k_sdt(NtUserSetWinEventHook) hooked, addr 872E046A \SystemRoot\system32\DRIVERS\dirtyguard.sys
win32k_sdt(NtUserSystemParametersInfo) hooked, addr 872DE56A \SystemRoot\system32\DRIVERS\dirtyguard.sys
This is rootkit technology and it's not safe; there are plenty of articles on the subject, but when has this ever stopped anyone? Passing through the gauntlet and hard kernel hooking, sandbox developers are trying to implement Windows access control mechanisms anew! Now they are somewhere near the level of Windows 2000, but the bug problems are much worse. And this makes sense because even a handful of very talented developers can't take into account all the details of something that took years to create and has been tested by millions of users.
But still, almost no one uses default debugged OS security mechanisms. Instead, they try and get around them and put forth a strange substitute that can hardly help against a serious threat, and sometimes might cause harm because the attack area increases.
In mid-2013, we tested a sandbox escape method on the basis of an injection in explorer.exe. It was PowerLoader. The vast majority of sandboxes did not resist this attempt. In some, it took something simple like removing user mode hooks or calling the kernel directly, and then the issue was resolved. Only two sandboxes had good resistance, one of which is not being developed at the moment, and the other was removed from the flagship product (antivirus software). This suggests that the hooking method only protects against sandbox escape methods that are already known to developers!
But we shouldn't be so critical. There are also solutions that use the OS security mechanisms without destroying them. So, if you don't see kernel hooks, it is likely that the developers approached the isolation issue more wisely.
Let's try and check this. To start, let's download the free utility Process Hacker. Then we run isolated Internet Explorer and then Process Hacker (fig. 9).
In the screenshot we can see the processes are darkly highlighted. These processes are launched by an anonymous user (NT AUTHORITY\ANONYMOUS LOGON) and have the Integrity Level of Untrusted, and their Logon SID is S-1-5-7. Yes, we're talking about Sandboxie. Nobody else does it like this. We strongly recommend you read more about integrity levels and Windows security mechanisms in the book "Windows Internals" by Mark Russinovich and David Solomon. Now we'll just say that the process is running in a way that gives it access rights to almost nothing, and if it wasn't for just one thing, the process would have been unloaded from the OS memory and (simply put) would have crashed. Nevertheless, it doesn't crash. It runs. And it's isolated, without hooking the kernel and without hardware virtualization (which is hooking, too, but more on that later). So how come? Let's take a closer look.
Since we are, among other things, doing a small review of hacking utilities, we cannot forget to mention GMER. Let's download, start and have a look at it. There are no kernel hooks. That's good. So what is there? How does a process run even though it was launched in a way it wasn't supposed to run? It's those same hooks, only this time in user mode, that come to the rescue here. Like these:
.text C:\Program Files\Internet Explorer\iexplore.exe ntdll.dll!NtAdjustPrivilegesToken 7565268 10 Bytes JMP 73AAB7E0 C:\Program Files\Sandboxie\SbieDll.dll
That is to say, each application running in a sandbox has a huge number of hooking (splicing) at the user level that proxy requests to the decision-making center through inter-process communication mechanisms. Apparently in this case it's SbieSvc (however, there is a lot of logic in the driver too). And the center, checking with the base, makes permissive or restrictive decisions. At the same time, if you get rid of the hooks, the application will stop working because it is prohibited from doing almost anything. In other words, this implementation is the purest form of the "what is not permitted is prohibited" principle. However, this approach has a number of drawbacks that firstly affect speed (every interception has to be processed), secondly, compatibility with antivirus software (they don't like hooks, even in user mode), and thirdly, compatibility with programs guarded by protectors (which are rather peculiar in that they also dislike hooks, sometimes getting rid of them as sabotage). And the list of permissive rules is very long, which leads to compatibility and performance problems.
Nuance 4, or why it's bad to cut off the branch you're sitting on
In most implementations an isolated application runs under the same user account as an unisolated one. Implementing isolation alone is not enough to filter the file system and registry. It's also necessary to monitor the opening of handles, work with COM objects, ALPC as a whole, windows, and so many more built-in Windows functions, particularly SRM (Security Reference Monitor). As per tradition, security technology developers solve these problems with hooks. We have already described this process in detail above. At the same time, PatchGuard, another protective technology, fights hooks. However, sandbox developers have already found a solution in their own unique style that involves virtualization mechanisms. The link can also show why this decision is not very beneficial (to put things mildly). For the same reason it's often difficult for developers to implement isolation for each individual process.
Antivirus company marketing experts (and others) often present hardware virtualization as a feature that dramatically increases computer security. And now we, system programmers, have come to promote the trend ourselves. We love large, complex, knowledge-intensive, and high-budget projects! But in fact, in most cases, switching to hypervisor is just a ploy to escape PatchGuard and nothing more. It's a chance to set hooks freely. At least for now :). And yes, it's expensive in every way, all the way from hardware requirements to costly developments. It's not cheap to write a hypervisor by any means. But consumers of course will pay for anything.
A few words about Windows default security mechanisms and using them for process isolation
As we noted above, there are very few solutions on the market that use Windows default security mechanisms. The one we talked about uses them, but in a rather original way: only to prohibit. This way brings about a number of problems.
But how else can these mechanisms be used to isolate processes? In fact, we can run applications under different user accounts, and most of the work will be done by Windows default access separation mechanisms. Although some filters are certainly still needed for the file system and registry.
Using default mechanisms for discretionary access control (manipulating object DACLs), we can regulate the access rights of subjects (isolated programs running under new user accounts) to objects (the file system, registry). In addition, we can regulate access rights to the station, desktop, system privileges, and trust level in a completely safe and documented way.
However, there are a number of nuances that must be taken into consideration, otherwise the solution's stability might decline or the solution's safety might be reduced to zero.
How to properly run processes under a different user account
Here's a fact: starting from Windows Vista, the "Run As" mechanism doesn't work. And even though it's not really news, not everyone knows about it. Let's conduct a simple experiment. Let's start Process Explorer. Then start notepad.exe. Double-click notepad.exe in the Process Explorer window. Now head over to the Security tab and select the Permissions tab (fig. 10).
Here we see some unknown record that has an SID of S-1-5-5-0-80701. This is the Logon SID. The Logon SID process permissions are: query limited information, query information, read memory, terminate, synchronize, read. The Logon SID token permissions are: assign as primary token, duplicate, impersonate, query, query source, and read. The Logon SID flow permissions are: query limited information, query information, get context, synchronize, and read.
This all means that if a process starts under a different user account via "Run As" (which starts the process with the current Logon SID), in most cases the process flow will be able to receive the token of another process (the target one started under the initial user account) in order to impersonate the security context of the target process and get all the privileges the target process has, which will mean a way out of isolation.
When analyzing the means of process isolation it is therefore necessary to pay attention to the Logon SID of the isolated process. Even if a process was started under another user account, escape from a sandbox might be possible if Logon SIDs are the same.
There are only three documented ways to start a process under a different user account:
- CreateProcessWithLogonW. Requires a username and password to enter, creates a process with the same Logon SID. We can't use this one.
- CreateProcessAsUser. Creates a process with the same Logon SID. We can't use this one.
- CreateProcessWithTokenW. This is the one we need.
We should be using CreateProcessWithTokenW to start applications under a different user account. It is the only correct solution.
The conniving desktop and isolation
Starting from Vista there is a security mechanism in Windows called UIPI (User Interface Privilege Isolation). This mechanism regulates the exchange of messages between applications with different integrity levels. Now let's remember that old friend of ours Windows XP.
Fig. 11 shows that SetWindowsHookEx will not work for an application running, for example, under the current user account if the application has been running under a different account. The isolation works! Just not in Windows XP :(. Let's have a look at how everything works in newer Windows operating systems (fig. 12).
The situation here is much more interesting. If UIPI is on (the gbEnforceUIPI variable is TRUE), then equivalence checking to find out what user account an application was started in is skipped!
In other words, if you run an application under a different user account on the same desktop along with applications from other users in Windows Vista or later and the application has 'DESKTOP_HOOKCONTROL' set in the desktop access rights, then worry about escape from a sandbox is a thing of the past. 'DESKTOP_HOOKCONTROL' permits the interception of any window messages and allows for window interceptions. If you disable 'DESKTOP_HOOKCONTROL', then a large number of applications will stop working. For instance, this access right is required by the MS Visual Studio runtime. Otherwise, not a single application compiled with MS Visual Studio will function.
There are hardly any solutions to this problem:
- Use hooking (problems with PatchGuard and all those other wonderful things we went over earlier).
- Start an application (which is nailed to the UAC — User Account Control) with UIPI turned off; this automatically disables UAC, one of the most important Windows security mechanisms (this option is by no means intended for novices). And yes, we've seen this in one far from popular process isolation implementation :).
- Run the isolated applications on individual desktops (an option for enlightened system programmers who understand the Truth).
So, if a sandbox doesn't use individual desktops in the isolation process, then it smacks of one of the first two options.
But individual desktops have their own pitfalls as well. It turns out that not every application "likes" to run on an individual desktop. Some particularly malicious ones specifically check for what desktop they run on. For example, Firefox can be updated improperly on an individual desktop and close immediately after starting. This happens because the update process clearly starts on winsta0/default, resulting in the inability to start due to a lack of access rights. If it's patched or run on the current desktop, it works. Fortunately for Firefox and its forks, 'DESKTOP_HOOKCONTROL' can be disabled, which rids applications of the necessity to run on an individual desktop.
Yandex Browser (version 13.12.1599.12987) contains a library C:\Users\xxx\AppData\Local\Yandex\YandexBrowser\Application\30.0.1599.12987\browser.dll
. Somewhere in it, nearChromeBrowserMainParts::PreMainMessageLoopRunImpl
, there is a procedure calledGetStartupInfo
, that haslpDesktop
(it usually hasWinStation\Desktop
); through a "\" search, the desktop name can be cut out and compared with the desktop name obtained throughOpenInputDesktop`, which is in fact the currently active desktop. If they don't match, the initialization is considered failed, and the browser is terminated.
As a side note, this comparison is inaccurate. It's not enough to only check the desktop name; we must consider the workstation as well. Moreover, the real desktop may not be the one that
GetStartupInfo shows. It's understandable that this approach won't let Yandex Browser run on an individual desktop. Yandex experts assert that it is some sort of clever click-through protection. And they've hardly ever changed anything about it beyond maybe a couple upgrades. Fortunately, Yandex Browser does not require permission for
DESKTOP_HOOKCONTROL, so it can run in isolation on the current desktop.
To be more universal, a sandbox therefore just needs a set of rules for the most popular applications so that user involvement in the magical world of product customization can be reduced. For example, Firefox and Thunderbird (after version 37, or somewhere around there) do not display windows correctly if desktop permissions don't include
DESKTOP_JOURNALPLAYBACK. When it comes to these minor details about sandboxes, it would be nice to know ahead of time.
A perfect world, or what a sandbox should be like
Based on our research, we realized that a reliable sandbox should:
- Maximize the efficiency of using the default security mechanisms of the platform it runs on.
- Be able to isolate each process in an individually configurable isolated environment.
- Provide the ability to regulate the access of each process to the file system and registry, removable media, and network; set privileges and the integrity level. In short, it should be maximally customized for a specific field of application.
- Be relatively easy to use.
- Log its work. Preferably in the OS journal.
- Not use kernel hooking under any circumstances.
- Not destroy existing OS security mechanisms and the OS itself.
- Have a constantly expanding base of initial settings to involve a user in the administration process as little as possible.
We have tried to tie all these requirements together in a tool called ReHIPS. It's completely free, so you can download and play around with it legally. Who knows, maybe you'll be lucky and be the one to escape from our sandbox. In any event, you can always send us a review with your impressions of the product and we'll be very grateful for it.
To wrap things up
The problem of process isolation only seems easy at first glance. In reality, it has a large number of potential pitfalls. Each vendor handles this problem differently… some more successfully than others. Now you know all the problematic aspects of modern sandboxes, so when you need to run a potentially dangerous application, you can choose the appropriate tool for its isolation from the main system.