Cabecera blog ciberseguridad

Abusando SeLoadDriverPrivilege para elevar privilegios

0x01 – Preámbulo

En el contexto de los sistemas operativos Windows, es bien conocido que la asignación de ciertos privilegios a cuentas de usuario sin permisos de administración puede dar lugar a la ejecución de ataques de elevación de privilegios a nivel local. Si bien la documentación de Microsoft es bastante clara al respecto, a lo largo de diversos pentests realizados en Tarlogic se han encontrado usuarios corrientes con ciertos privilegios asignados que han podido ser explotados para obtener el control total de un sistema.

En el artículo de hoy se analizara el impacto vinculado a la asignación de la directiva «Cargar y descargar controladores de dispositivo» la cual define a los usuarios que disponen de capacidades para cargar de forma dinámica un driver. La asignación de esta directiva en el contexto de usuarios no privilegiados supone un riesgo significativo ya que permitirá la carga y ejecución de drivers en el contexto del kernel. La posibilidad de ejecutar código en este contexto, permite la manipulación de estructuras internas del núcleo del sistema, dando lugar, entre otras cosas, a la ejecución de ataques de elevación de privilegios.

Esta técnica, si bien es conocida, no se encuentra incluida en herramientas de post-explotación y elevación de privilegios. Ante esta situación, se ha desarrollado una prueba de concepto que permite su explotación de forma automática.

0x02 – Análisis de SeLoadDriverPrivilege y Access Tokens

La directiva “Cargar y descargar controladores de dispositivo” es accesible desde el editor de directivas de grupo local (gpedit.msc) bajo la siguiente ruta: “Configuración de equipo->Configuración de Windows->Directivas locales->Asignación de derechos de usuario”.

Dadas sus implicaciones los valores por defecto de esta directiva incluyen únicamente al grupo de “administradores” y “operadores de impresión”. La siguiente tabla muestra los valores por defecto, según la documentación:

permisos de seloaddriverprivilege

Valores predeterminados de la directiva

NOTA: El grupo operadores de impresión puede parecer bastante inocuo a simple vista, sin embargo dispone de la capacidad de cargar drivers de dispositivos en los controladores de dominio así como administrar objetos de tipo impresora en el directorio activo. Adicionalmente este grupo dispone de capacidades para autenticarse en un controlador de dominio, por lo cual es de especial interés verificar la pertenencia de usuarios a este grupo.

La asignación de esta directiva a un usuario o grupo añadirá el privilegio SeLoadDriverPrivilege en los access tokens de los procesos del usuario posibilitando así la carga de drivers.

Un Access token es un tipo de objeto que describe el contexto de seguridad de un proceso de o hilo, y son asignados a todos los procesos creados por un usuario autenticado en el sistema. En él se especifican entre otras cosas el SID (Security identifier) que identifica la cuenta de usuario,  los SIDs vinculados a los distintos grupos de los que es miembro, así como la lista de privilegios asignados al propio usuario o a los grupos a los que pertenece.  Esta información es esencial en el modelo de control de acceso del sistema operativo ya que esta información se verifica cada vez que se intenta acceder a cualquier objeto securizable del sistema.

Para entender el procedimiento de explotación (que explicaremos posteriormente), es necesario tener en cuenta que desde Windows Vista, el sistema operativo integra el mecanismo de seguridad “Control de cuentas de usuario”, más conocido por sus siglas en inglés como UAC. Como resumen, esta medida de seguridad está basada en el “principio del mínimo privilegio”, el cual consiste en hacer uso de access tokens restringidos que omiten ciertos privilegios asignados al usuario. Estos privilegios omitidos pueden ser habilitados una vez que se acepte el cuadro de diálogo del UAC.

Teniendo en cuenta esta información, a continuación, se analiza el proceso de explotación de este privilegio para realizar la carga de un driver desde una cuenta de usuario sin permisos de administración.

0x03 – Análisis del procedimiento de explotación de seloaddriverprivilege

Obtención de una shell con el token sin restricciones.

En primer lugar, como hemos visto anteriormente, será necesario obtener un token sin restricciones. De cara a conseguirlo disponemos de las siguientes opciones:

  • Hacer uso de la funcionalidad “Ejecutar como administrador” para elevar cualquier proceso iniciado por el usuario. El uso de este mecanismo en el contexto de usuarios que no son administradores, permitirá obtener un token sin restricciones.
  • Hacer uso de la herramienta elevate. Esta herramienta permite iniciar el procedimiento de elevación en la ejecución de cualquier programa.

elevate.exe -c cmd.exe

  • Compilar una aplicación incluyendo un manifest que indique el uso de un token sin restricciones.
  • Hacer uso de alguna técnica de bypassing de UAC para obtener un token sin restricciones.

Activación del privilegio SeLoadDriverPrivilege

Una vez obtenido el token sin restricciones se puede observar que por defecto, el privilegio está presente pero se encuentra deshabilitado. Para disponer del privilegio será necesario habilitarlo. Para ello es posible hacer uso de la API LookupPrivilegeValue(), la cual permite obtener la referencia del sistema vinculada al privilegio, en conjunción con la API AdjustTokenPriviliges(), para hacer efectiva la habilitación del mismo.

TOKEN_PRIVILEGES tp;
LUID luid;

if (!LookupPrivilegeValue(
  NULL,            // lookup privilege on local system
  lpszPrivilege,   // privilege to lookup 
  &luid))        // receives LUID of privilege
{
  printf("LookupPrivilegeValue error: %u\n", GetLastError());
  return FALSE;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
  tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
  tp.Privileges[0].Attributes = 0;

// Enable the privilege or disable all privileges.

if (!AdjustTokenPrivileges(
  hToken,
  FALSE,
  &tp,
  sizeof(TOKEN_PRIVILEGES),
  (PTOKEN_PRIVILEGES)NULL,
  (PDWORD)NULL))
{
  printf("AdjustTokenPrivileges error: %u\n", GetLastError());
  return FALSE;
}

Carga del driver

La carga de drivers desde espacio de usuario puede realizarse haciendo uso de la API de Windows NTLoadDriver, cuyo formato se detalla a continuación:

NTSTATUS NTLoadDriver(
  _In_ PUNICODE_STRING DriverServiceName
);

Esta función toma como único parámetro de entrada DriverServiceName, un puntero a una cadena en formato UNICODE en la que se especifica una clave del registro en la que se definen los distintos valores de configuración del driver.

Estas claves del registro se localizan por defecto en la siguiente ruta.

\Registry\Machine\System\CurrentControlSet\Services\NombreDriver

Bajo la clave NombreDriver se definen distintos parámetros de configuración vinculados al driver. Para nuestro objetivo los más relevantes serán:

  • ImagePath: Valor de tipo REG_EXPAND_SZ en el que se especifica la ruta en la que se localiza el driver. Para nuestro propósito la imagen del driver deberá localizarse en una ruta/directorio con permisos de modificación para el usuario no privilegiado.
  •  Type: Valor de tipo REG_WORD en el que se indica el tipo de servicio. Para nuestro propósito el valor a definir será SERVICE_KERNEL_DRIVER (0x00000001) , valor que indica que se tratará de un driver de dispositivo ejecutando en el kernel.

La documentación de Microsoft indica que la información de configuración de drivers y servicios debe localizarse bajo la ruta HKLM\SYSTEM\CurrentControlSet\Services, la cual por defecto define permisos de modificación únicamente al grupo de Administradores, sin embargo la API NTLoadDriver no restringe el uso de claves bajo HKCU (HKEY_CURRENT_USER), en la cual un usuario no privilegiado sí que dispone de permisos de modificación.

Teniendo en cuenta esta situación, a la hora de invocar la API NTLoadDriver, será posible especificar a través del parámetro DriverServiceName una clave del registro bajo HKCU (HKEY_CURRENT_USER),  definiendo la ruta en el siguiente formato:

\Registry\User\{SID_USUARIO_NO_PRIVILEGIADO}\

El valor SID vinculado a la cuenta de usuario puede obtenerse programáticamente mediante el uso de la API GetTokenInformation, la cual permite obtener información vinculada al access token del proceso ejecutado en el contexto del usuario autenticado. Alternativamente el SID puede ser consultado haciendo uso del comando “whoami /all”, o mediante las siguientes instrucciones de PowerShell:

(New-Object System.Security.Principal.NTAccount("NOMBRE_CUENTA_USUARIO")).Translate([System.Security.Principal.SecurityIdentifier]).value

# En el contexto de un usuario del dominio.
Get-ADUser -Identity ' NOMBRE_CUENTA_USUARIO ' | select SID

0x04 – Prueba de concepto

Para facilitar la explotación del privilegio de carga de drivers hemos creado una aplicación que permite automatizar el procedimiento descrito anteriormente.

En primer lugar partimos de un usuario no privilegiado (test) al cual se ha asignado el privilegio “Cargar y descargar controladores de dispositivo”.

seloaddriverprivilege group policy

Directiva «Cargar y descargar controladores de dispositivo» asignada al usuario test

Como comentamos durante el proceso de análisis, inicialmente el usuario tendrá asignado un token restringido, en el que no figura el privilegio SeLoadDriverPrivilege.

privilegios seloaddriverprivilege

Verificación inicial de privilegios asignados al usuario test (restricted token)

Si se dispone de una sesión interactiva no habrá problemas para aceptar el cuadro de dialogo de UAC, en caso contrario (ej: se dispone de una sesión tras la ejecución de un exploit) se deberá hacer uso de alguna técnica de bypassing de UAC.

En este caso concreto asumiremos que se dispone de una sesión interactiva en el sistema. Haciendo uso de la herramienta elevate, se puede abrir una nueva terminal con el token sin restricciones tras aceptar el prompt de UAC.

Como se puede observar el privilegio “SeLoadDriverPrivilege” se encuentra presente en el access token del usuario, sin embargo se encuentra deshabilitado.

seloaddriverprivilege privilegio desactivado

Obtención de un token sin restricciones vinculado a la cuenta sin permisos de administración

A partir de este punto, mediante nuestra prueba de concepto EOPLOADRIVER.exe (https://github.com/TarlogicSecurity/EoPLoadDriver/), se podrá:

  • Habilitar el privilegio SeLoadDriverPrivilege
  • Crear los distintos parámetros vinculados a la carga del driver bajo la clave del registro HKEY_CURRENT_USER
  • Ejecutar la función NTLoadDriver, especificando la clave del registro creada previamente

La ejecución de la misma se detalla a continuación:

EOPLOADDRIVER.exe ClaveRegistro RutaDriver

El parámetro ClaveRegistro especifica el nombre de la clave del registro creada a partir de la ruta “\Registry\User\{SID_USUARIO_NO_PRIVILEGIADO}\”, mientras que el argumento RutaDriver define la localización del driver. Tal y como se visualiza en la imagen la localización es el escritorio del usuario.

seloaddriverprivilege escalada de privilegios

Ejecución de la herramienta EOPLOADDRIVER

Tras la ejecución se puede comprobar la carga correcta del driver haciendo uso de la herramienta DriverView o WinObj.

ver driver cargado

Verificación de carga del driver con la herramienta DriverView

0x05 – Exploit de seloaddriverprivilege

Una vez identificada la forma de cargar un driver desde una cuenta de usuario no privilegiada, el siguiente paso es identificar un driver firmado que cuente con alguna vulnerabilidad que nos permita elevar privilegios en el sistema.

Para este ejemplo, hemos seleccionado el driver de Capcom.sys (SHA1: c1d5cf8c43e7679b782630e93f5e6420ca1749a7) el cual cuenta con una ‘funcionalidad’ que permite ejecutar código en espacio de kernel a partir de una función definida en espacio de usuario.

La decisión de seleccionar este driver es que existen distintos exploit públicos entre los que destacan:

  • ExploitCapcom de Tandasat – https://github.com/tandasat/ExploitCapcom – Este exploit permite obtener una Shell como SYSTEM
  • PuppetStrings by zerosum0x0 – https://github.com/zerosum0x0/puppetstrings – Permite la ocultación de procesos en ejecución .

Siguiendo el procedimiento descrito anteriormente en primer lugar obtenemos una terminal elevada, con el objetivo de obtener un token sin restricciones, para a continuación ejecutar la herramienta EOPDRIVERLOAD.exe, la cual permitirá habilitar el privilegio SeLoadDriverPrivilege y finalmente cargar el driver seleccionado.

exploit seloaddriverprivilege

Elevación de la terminal y ejecución de EOPLOADDRIVER

Una vez cargado es posible ejecutar cualquier exploit descrito anteriormente. La siguiente imagen evidencia la utilización del exploit “ExploitCapcom” de Tandasat para obtener una terminal como SYSTEM.

exploit de driver capcom

Ejecución de ExploitCapcom de Tandasat para elevación de privilegios como SYSTEM

0x06 – Conclusiones

Se ha podido comprobar como la asignación del privilegio “Cargar y descargar controladores de dispositivo” posibilita la carga dinámica de drivers en el kernel, lo que puede dar lugar a la ejecución de ataques de elevación de privilegios. Si bien por defecto Windows no permite la carga de drivers no firmados, en múltiples ocasiones se han identificado debilidades en drivers que podrían ser aprovechados para comprometer completamente un sistema.

Todos los test han sido realizados en un entorno Windows 10 Version 1708

A partir de Windows 10 Version 1803, parece que NTLoadDriver prohíbe referenciar claves del registro bajo HKEY_CURRENT_USER.

Descubre nuestro trabajo y nuestros servicios de ciberseguridad en www.tarlogic.com/

En TarlogicTeo y en TarlogicMadrid.