Administración de identificadores
Una fuente significativa de problemas de seguridad dentro de los controladores es el uso de identificadores pasados entre los componentes del modo de usuario y el modo kernel. Hay varios problemas conocidos para controlar el uso en el entorno del kernel, incluidos los siguientes:
Una aplicación que pasa el tipo incorrecto de identificador a un controlador de kernel. Es posible que el controlador de kernel se bloquee al intentar usar un objeto de evento en el que se necesita un objeto de archivo.
Aplicación que pasa un identificador a un objeto para el que no tiene el acceso necesario. El controlador de kernel puede realizar una operación que funcione porque la llamada procede del modo kernel, aunque el usuario no tenga los permisos adecuados para hacerlo.
Una aplicación que pasa un valor que no es un identificador válido en su espacio de direcciones, pero se marca como identificador del sistema para realizar una operación malintencionada en el sistema.
Aplicación que pasa un valor que no es un identificador adecuado para el objeto de dispositivo (un identificador que este controlador no creó).
Para protegerse contra estos problemas, un controlador de kernel debe tener especial cuidado para asegurarse de que los identificadores pasados a él son válidos. La directiva más segura consiste en crear los identificadores necesarios en el contexto del controlador. Estos identificadores, creados por controladores de kernel, deben especificar la opción OBJ_KERNEL_HANDLE, que creará un identificador válido en contexto de proceso arbitrario y uno al que solo se puede acceder desde un llamador en modo kernel.
En el caso de los controladores que usan identificadores creados por un programa de aplicación, el uso de estos identificadores debe realizarse con un cuidado extremo:
El procedimiento recomendado consiste en convertir el identificador en un puntero de objeto mediante una llamada a ObReferenceObjectByHandle, especificando los parámetros AccessMode correctos (normalmente desde Irp-RequestorMode>), DesiredAccess y ObjectType, como IoFileObjectType o ExEventObjectType.
Si se debe usar un identificador directamente dentro de una llamada, es mejor usar las variantes Nt de funciones en lugar de las variantes Zw de las funciones. Esto aplicará la comprobación de parámetros y controlará la validación por parte del sistema operativo, ya que el modo anterior será UserMode y, por tanto, no es de confianza. Tenga en cuenta que los parámetros pasados a las funciones Nt que son punteros pueden producir un error de validación si el modo anterior es UserMode. Las rutinas Nt y Zw devuelven un parámetro IoStatusBlock con información de error que debe comprobar si hay errores.
Los errores deben quedar atrapados adecuadamente, así como usar __try y __except según sea necesario. Muchas de las rutinas de biblioteca en tiempo de ejecución del sistema de archivos (FsRtl) y administrador de memoria (Cc), administrador de memoria (Mm) generan una excepción cuando se produce un error.
Ningún controlador nunca debe basarse en identificadores o parámetros pasados desde una aplicación en modo de usuario sin tomar las precauciones adecuadas.
Tenga en cuenta que si se usa la variante Nt para abrir un archivo, la variante Nt también debe usarse para cerrar el archivo.