In this post I’ll finish up the discussion series by explaining some of the more subtle aspects of implementing process isolation. I started by covering file isolation system topics and finished last time by covering the registry isolation. These are the two largest subsystems, but there are other pathways through which a process, or process group, can access the underlying OS.
The first to be covered will be handle inheritance and sharing. Under Windows platforms, process 1 can open a handle to process 2 and use this handle to perform operations on the virtual address space. For example the handle can be used to write into the memory of the other process, corrupting the process causing it to crash or worse, manipulating the code to have it perform operations not intended for that process. In order to control this access and ensure that an isolated process is not able to perform such operations on a non-isolated process, we can leverage the Windows Object Manager interface. Using this interface, a callback can be registered for certain object types when specific actions are taken on those objects. Note that a handle within a given process is simply an index into that process’s handle table and the table entry in turn points to the actual object.
By registering this callback with the Object Manager the filter driver can be notified, for example, when a thread or process creates a new handle. When the callback is invoked, we have the ability to control the access rights assigned to the handle and therefore can remove access rights such as PROCESS_VM_WRITE which allows the handle to be used for modifying the process’s virtual address space. There are other access rights that can be removed from the newly created or duplicated handle such as PROCESS_TERMINATE which can be used to end another process.
This mechanism for manipulating access rights on handles associated to objects is a very strong tool in our arsenal to design a robust isolation system. Windows only supports this interface for process and thread handles but any object can, in theory, be controlled in this way. It takes a little effort to analyze but using WinDbg one can easily figure out what bits need to be set in other objects for this mechanism to work.
The next subsystem I will discuss is Named Pipes. Named Pipes are a mechanism used by processes to perform communication between separate processes or threads. As the name implies, they can be named, though they don’t have to be. But provided they are named, then knowing the name of the pipe a different process can open the pipe and listen for messages or send messages to the other end. This is a convenient mechanism used by processes to easily communicate information back and forth. In order to obfuscate this mechanism within our model, the approach is to mangle the name of the pipe in a deterministic manner before the pipe gets created. For example a pipe name, call it \Pipe\Process1CommPipe, can be mangled into \Pipe\Process1CommPipe_Isolated. Thus as long as the process which is opening the pipe is running in an isolated context, the name will always be mangled in the same manner. But a process not in an isolated context would attempt to open the original pipe name which would fail. Using this mechanism we have blocked communication via named pipes between an isolated and non-isolated process group while still allowing inter-process communication between isolated processes. One note, using the mangling scheme described above, this would allow for inter-process communication between different isolated process groups. If the isolation design required eliminating inter-group communication as well, you could simply use a unique identifier in the name mangling scheme which would be unique to that isolated process group.
Another mechanism for communication which needs to be covered are sockets. In general, depending on the type of process in which the design is isolating, sockets can be 1) completely blocked from access in an isolated process, 2) always allowed, or 3) selectively allowed to be connected. In the later versions of Windows, since Windows Vista, Microsoft has offered a robust platform for managing socket connections called the Windows Filtering Platform, or WFP. This subsystem allows for filtering of socket connections at the time of initial connection, during communication, and nearly any stage during the life cycle of the socket. Because WFP allows the design to determine the originating processes context, the design can easily determine whether the caller belongs to an isolated process group. Provided this information, the most robust approach is to have a user mode service make a determination at the time of socket connection if the connection should be allowed or denied. This way the design can control connections made to addresses which are unknown or suspicious and notify the user about the connections. This method can also be used to control connections made to the local host to prevent inter-process communication via sockets from a non-isolated process to an isolated process.
Some of the final items which should be touched upon are subsystems such as LPC/ALPC. These subsystems are used for inter-process communication, similar to how named pipes are used. This can be seen most frequently in requests issued to system services such as svchost or lsass. For example, when you download an installation file for, let’s say Adobe Reader, the browser will issue an ALPC call to the system service to execute the installer. In order to correctly handle these requests, a mechanism must be put in place to allow for the filtering of ALPC requests so location information can be updated or the request can be denied altogether. Unfortunately Windows does not currently offer a mechanism to cleanly filter these requests so as an alternative user mode patching can be employed. This requires that the import table for the isolated process be patched so calls to the ALPC API set can be filtered. While this method is not the cleanest approach and does require a DLL to be injected into the isolated process to perform the patching, it does accomplish the goal of filtering user mode calls to the ALPC and LPC subsystems. Of course any kernel mode communication which would leverage this interface cannot be filtered using this approach so until Microsoft offers a filtering mechanism for this interface, this is best design. Kernel mode patching is not an option on x64 systems due to PatchGuard.
One final note on the ALPC interface and specifically for downloading and running installers as described above. If the file system isolation mechanism is designed similarly to that which has been described in previous posts, the location of the downloaded installer would be within the shadow store. For example, if a file was downloaded and the browser stored downloaded files in the C:\Downloads folder, the virtualized location for the downloaded file would be C:\Downloads\Installer.exe but the actual location the file would be stored at would be C:\ShadowStore\CVolume\Downloads\Installer.exe. When the isolated browser would request a system service to then execute the file called C:\Downloads\Installer.exe, the service, which is not isolated would attempt to access the file but it would not find it at the location indicated since it is actually stored in the shadow location. So by default handling, downloaded files cannot be executed from outside of the isolated process context so it is implicit that protection has been implemented to prevent this type of attack. The only situation where the design would be required to filter the ALPC calls would be in the case where it does want to allow for the installation of the file and add that process to the isolated context. This would achieve a system where applications could be temporarily installed in an isolated context but once the process group terminated, the installed application would ‘go away’, sort of a like a ‘try and buy’ scenario.
That’s it for now on Security Through Process Isolation. In the next post on this topic, I will provide an example of the design described which isolated web browsers preventing zero-day attacks on the underlying system as well as removing the need for any type of anti-malware or anti-virus applications for protecting your browser sessions.