Ha cruzado la línea: interrupción del REST de un host
Resumen ejecutivo
- El investigador de Akamai Ben Barnea descubrió dos vulnerabilidades importantes en un servicio RPC de Microsoft Windows a las que se asignaron la CVE-2022-37998 y la CVE-2022-37973 con una puntuación base de 7,7.
- Las vulnerabilidades aprovechan varios errores de la interfaz RPC del Administrador de sesiones local.
- Las vulnerabilidades provocan ataques de denegación de servicio que impiden el funcionamiento de los servicios de contenedor y sesión (como Microsoft Defender Application Guard, Sandbox, Docker y Windows Terminal Server).
- La vulnerabilidad existe en equipos con Windows 10, Windows 11 y Windows Server 2022 sin actualizar.
- Las vulnerabilidades se divulgaron de forma responsable a Microsoft y se solucionaron en el Patch Tuesday de octubre de 2022.
- Proporcionamos una prueba de concepto de las vulnerabilidades en nuestro repositorio de investigación.
Introducción
El grupo de inteligencia sobre seguridad de Akamai profundizó en el estudio de MS-RPC este año pasado. Para un protocolo que hace tanto, MS-RPC está en gran medida poco investigado, y puede tener efectos en el mundo real. Uno de esos efectos es que las vulnerabilidades en una interfaz RPC quedan expuestas. Esto es en lo que nos centramos en esta publicación de blog: en las vulnerabilidades dentro de la interfaz RPC del Administrador de sesiones local (LSM).
El LSM es un servicio que forma parte del subsistema del administrador de sesiones. Es responsable de la administración de las sesiones locales relacionadas con las sesiones de servidores de terminales en un equipo Windows. Se comunica con otros componentes de Windows relacionados, como Winlogon y Csrss.
El LSM se implementa en lsm.dll y contiene lógica de cliente y de servidor. El LSM expone varias interfaces RPC, con una interfaz interesante relacionada con la administración de sesiones de contenedores que se ejecutan dentro de una máquina virtual Hyper-V. Las vulnerabilidades se encuentran dentro de esta interfaz.
¿Qué es esta interfaz?
A la nueva interfaz RPC se le asigna el UUID c938b419-5092-4385-8360-7cdc9625976a. Esta interfaz expone exactamente dos funciones: ContainerCom_AskForSession y ContainerCom_SessionLoggedOff. La interfaz también está registrada con una devolución de llamada de seguridad que siempre devuelve RPC_S_OK, permitiendo así el acceso a todos. El servidor LSM registra un terminal de socket de Hyper-V (hvsocket) al que solo pueden acceder los contenedores de Hyper-V.
Fig. 1: Cliente configurando conexión RPC a través de hvsocket frente a servidor configurando un terminal de hvsocket
Cuando se crea una sesión dentro del contenedor (por ejemplo, debido a una conexión RDP), el cliente LSM llama primero a RpcGetRequestForWinlogon dentro del LSM del contenedor. Esta función arbitra la creación de la sesión y, cuando se ejecuta dentro de un contenedor, primero pide permiso al host. Esto se hace invocando la llamada RPC ContainerCom_AskForSession al host mediante un hvsocket al principal. La interfaz RPC limita el número de sesiones a los contenedores. Para ello, realiza un seguimiento de las sesiones creadas recientemente.
¿Cómo realiza LSM el seguimiento de las sesiones?
La respuesta es simple. Hay un objeto global denominado ContainerSessionServer que contiene dos variables para realizar un seguimiento de las sesiones:
- Contador del total de sesiones creadas. Esto está limitado a uno, lo que significa que solo se permite una sesión en un momento dado.
- Un mapa entre el GUID de un contenedor y el recuento de sesiones de su contenedor. Esto está limitado a dos para cada contenedor.
Cada vez que un contenedor solicita una sesión, ContainerSessionServer::AskForSession primero comprueba si el contador total de sesiones es inferior a uno. Si es así, aumenta el recuento total de sesiones y también el contador de sesiones del contenedor en el mapa.
Cuando se llama a ContainerSessionServer::OnSessionLoggedOff (al salir del contenedor o directamente como una llamada RPC), la función reduce en uno tanto el recuento total de sesiones como el recuento de sesiones del contenedor.
Auditoría de funciones RPC para detectar vulnerabilidades.
Aunque esta interfaz parece sencilla y fácil de implementar, hemos encontrado cuatro errores que hemos conseguido encadenar en dos vulnerabilidades.
Cadena 1: DoS mediante una sección crítica: CVE-2022-37998
Error 1: error al salir de una sección crítica
ContainerSessionServer::AskForSession utiliza una sección crítica para sincronizar el acceso al objeto global ContainerSessionServer.
Fig. 2: Código descompilado de la vulnerabilidad. La función sale sin liberar la sección crítica.
Como se ha visto anteriormente, la sección crítica se introduce en la línea 112. Más adelante, en las líneas 114–116, hay una comprobación para ver si el contador de sesiones del contenedor está en su límite (dos). Si es así, el LSM no realizará el seguimiento de esta sesión y saldrá inmediatamente de la función (línea 125). Lamentablemente, el código no sale de la sección crítica que se introdujo. Por lo tanto, se bloquearán más llamadas a esta interfaz esperando a que se libere esta sección crítica.
Pero, si recuerda, hay un límite de uno en el contador total de sesiones, así que ¿cómo podemos llegar a un punto en el que el contador de sesiones de un contenedor sea dos? Bueno, lógicamente no es posible. Pero, ¡entonces llega el segundo error!
Error 2: seguimiento incorrecto del contador
Cuando se cierra una sesión en el contenedor, se realiza una llamada RPC a ContainerSessionServer::OnSessionLoggedOff en el host. Esta función primero reduce el contador total de sesiones mediante la llamada a DecreaseTotalSessionCount. Lo hace independientemente de si se está realizando un seguimiento del contenedor. Si descubre que no se está realizando un seguimiento del contenedor, se cierra sin incrementar el contador total de sesiones.
Esto puede llevar a un contador total de sesiones al valor de un número negativo (ya que es un número entero con signo). Podemos simplemente enviar muchas solicitudes OnSessionLoggedOff antes de enviar cualquier solicitud AskForSession y, por lo tanto, mantener la disminución del contador total de sesiones a un número negativo arbitrario.
Encadenando el primer y el segundo error.
Podemos usar el error 2 para disminuir el contador total de sesiones unas cuantas veces hasta que sea un número negativo. Entonces, podemos explotar el error 1 enviando dos solicitudes a AskForSession. La segunda vez que se llama a esta función, comprobará que el contador total de sesiones es inferior a 1 y, debido al segundo error, lo es. A continuación, verá que el contador de sesiones del contenedor es 2 y volverá sin salir de la sección crítica.
Fig. 3: descripción general del proceso de explotación.
El DoS depende de si las nuevas llamadas RPC entrantes se distribuyen al mismo subproceso que creó el interbloqueo de sección crítica. Si el tiempo de ejecución de RPC distribuyó la nueva llamada al mismo subproceso, el DoS no se produciría dado que EnterCriticalSection permite la propiedad anidada; es decir, el mismo subproceso puede llamar a EnterCriticalSection dos veces. Si la llamada RPC se distribuyera a cualquier subproceso que no fuera el que contiene la sección crítica, esperaría para siempre.
Cadena 2: DoS mediante fuga de memoria: CVE-2022-37973
Error 3: fuga de memoria
ContainerSessionServer::AskForSession también realiza un seguimiento de los eventos de los contenedores, como la salida, pausa o reanudación del contenedor. Esto se hace llamando a HcsOpenComputeSystem con el GUID del contenedor y, a continuación, registrando una devolución de llamada con HcsRegisterComputeSystemCallback.
La devolución de llamada registrada recibe un objeto de contexto. El contexto se asigna en el interior de ContainerSessionServer::AskForSession. Desafortunadamente, en muchos casos en los que se produce un error, la función sale sin liberar la memoria asignada para el contexto. Esto provoca una fuga de memoria que un atacante puede activar varias veces. Después de suficientes llamadas, se agota la memoria para el proceso LSM y el proceso se bloquea.
En nuestras pruebas, el envío de solicitudes RPC en un bucle sin fin produjo aproximadamente 3 MB de asignación por segundo. En nuestro caso, después de asignar 24 GB de memoria, el servicio LSM se bloqueó. El tiempo que se tardó en agotar 24 GB fue aproximadamente de dos horas. El servicio no se vuelve a generar automáticamente.
Error 4: acceso remoto
Los terminales de MS-RPC se multiplexan. Si un servidor registra varias interfaces y varios terminales, se puede acceder a cada interfaz a través de cada terminal. Los terminales e interfaces no están vinculados entre sí.
Se supone que esta interfaz solo es accesible a través del hvsocket para los contenedores. En nuestro caso, LSM registra un terminal de canal con nombre “\pipe\LSM_API_service”, al que se puede acceder de forma remota. Debido a la multiplexación de terminal, un atacante remoto puede conectarse al terminal de canal con nombre y enviar una solicitud a la interfaz del contenedor. La solución es sencilla: la devolución de llamada de seguridad debe comprobar qué terminal ha utilizado el cliente y, si no es hvsocket, denegar el acceso.
Encadenando el tercer y el cuarto error
La funcionalidad de seguimiento de contenedores se basa en la propiedad del identificador de cliente. Esto significa que para un hvsocket, el identificador de cliente será el GUID del contenedor. Para un canal con nombre, será el nombre del equipo del cliente.
Para desencadenar la primera vulnerabilidad, el cliente debe tener un identificador de cliente que sea un GUID real de un contenedor en ejecución. Como tal, un cliente remoto no podría desencadenar esos errores a menos que determine con éxito el GUID de un contenedor en ejecución y cambie el nombre de su equipo, un escenario improbable.
Desafortunadamente, el tercer error (fuga de memoria) asigna los objetos antes de que se realicen comprobaciones en el contenedor solicitado. Esto significa que un atacante remoto (haciendo uso del error 4) puede desencadenar de forma remota la fuga de memoria. Al realizar varias llamadas, un atacante puede provocar el agotamiento de la memoria y bloquear el proceso.
Impacto
Aunque estas vulnerabilidades se clasifican como vulnerabilidades de denegación de servicio (DoS), tienen un impacto en la seguridad, ya que permiten que un atacante omita las funciones de seguridad.
Con la primera explotación (sección crítica), hay un DoS para la nueva interfaz específica. Este problema impediría la creación de nuevas instancias de Sandbox.
Con la segunda explotación, que se puede desencadenar tanto de forma remota como desde un contenedor, se bloquea todo el proceso. Por lo tanto, no funcionarían todas las dependencias del LSM. Las funciones de seguridad como Microsoft Defender Application Guard y Sandbox tampoco funcionarían. Además, RDP y Docker no funcionarían.
Fig. 4: Error de MDAG como se muestra en Microsoft Edge
Resumen
Estas vulnerabilidades son un gran ejemplo de cómo algo que parece simple o insignificante puede tener impactos realmente negativos. Esta interfaz puede parecer secundaria: nos ha presentado algunos errores que son fáciles de desencadenar y tienen un impacto interesante.
La realidad de la situación es que los agentes maliciosos están utilizando estas cadenas de ataque libremente. Cuanto más insignificante parece algo, más fácil se ignora, y eso es algo primordial para su explotación.
Junto con el trabajo continuo que estamos realizando en RPC, animamos a otros investigadores a buscar errores similares en otras interfaces RPC.
Si está interesado en temas de investigación de RPC como este, consulte nuestro kit de herramientas de RPC para conocer más informes y herramientas. También puede seguirnos en Twitter para obtener actualizaciones en tiempo real sobre esta y otras investigaciones que estamos llevando a cabo en Akamai.