
Exploit remoto del sistema de archivos de red de Windows para CVE-2022-30136
autor: Ricardo Narvaja
Solo con fines de demostración. El exploit completo funciona en sistemas Windows Server vulnerables.
Consulta el artículo Análisis de CVE-2022-30136 “Vulnerabilidad de Windows Network File System”.
Quería escribir este artículo para demostrar el análisis que realicé mientras desarrollaba el exploit de Core Impact “Windows Network File System Remote” que abusa de la vulnerabilidad CVE-2022-30136.
La vulnerabilidad de ejecución remota de código de Windows Network File System es un error de cálculo de tamaño que ocurre al crear la respuesta del servidor en una COMPOUND REQUEST usando la versión 4.1 de NFS.
El servidor calcula un tamaño menor del necesario para asignar el pool, y luego, al copiar los datos para generar la respuesta, desborda el búfer.
La función Nfs4SvrXdrpGetEncodeOperationResultByteCount en nfssvr.sys se llama para cada operación y devuelve un tamaño menor del necesario (4 bytes menos por cada operación).
Se realizó un parche para Nfs4SvrXdrpGetEncodeOperationResultByteCount.
Esta función se llama durante cada OPERACIÓN de una COMPOSE REQUEST para que devuelva los bytes necesarios para cada una de ellas según el OPCODE. Luego se suma al encabezado y otras partes de la respuesta. A continuación, calcula el tamaño final de toda la respuesta para asignarlo y luego copia en él para responder.
En cada caso, podemos ver que el valor del tamaño devuelto para cada operación es cuatro bytes menor en la versión vulnerable que en la versión parcheada.
Construyo el POC para Windows Server 2019.
A continuación se muestra la versión vulnerable de nfssvr.sys utilizada para este POC, seguida de la versión parcheada para Windows Server 2019:

La siguiente imagen muestra el CASE 26 en la diferencia:

En el ejemplo de CASE 26, podemos ver que la constante añadida al valor calculado es 0x2c en la versión vulnerable y 0x30 en la versión parcheada.
Lo mismo se puede observar en cada caso correspondiente a cada OPCODE. El vulnerable siempre devuelve un tamaño cuatro bytes menor que el parcheado.
No vamos a mostrar todos los casos porque el parche es similar para todos los OPCODES.
El padre de Nfs4SvrXdrpGetEncodeOperationResultByteCount es Nfs4SvrXdrEncodeCompoundResults. Lee el número de operaciones enviadas en la COMPOUND REQUEST.
En este POC el valor es 0x34 (52d). Cuando mi POC se conecta al servidor en el puerto 2049 (el puerto predeterminado para NFS), necesito colocar un punto de interrupción condicional para una parada.


En este caso, se detiene cuando number_of_operations=0x34.

El pool con la etiqueta ARGS se asigna aquí.

Luego crearé una estructura llamada TAG_ARGS_0x10e0 para invertir los campos.

Copia el number_of_operations en r13 y recorre la función vulnerable una vez por operación, hasta que el contador alcanza el valor de r13.

Muestra que el primer package_OPCODE= 0x35, que corresponde a SEQUENCE en la primera operación obligatoria en una COMPOUND REQUEST. En la imagen de abajo, la flecha apunta a este OPCODE en mi paquete.

Aquí podemos ver los argumentos de la función vulnerable.


Dentro de la función vulnerable lee el OPCODE y va al CASE correspondiente.

Se resta tres del valor original del OPCODE (53).

Y salta a CASE 50, devolviendo 0x28 al tamaño necesario para esta operación.


Podemos ver en la diferencia cómo la versión parcheada devuelve 0x2c.

Este valor devuelto se suma al valor anterior de otros campos en la respuesta para calcular el tamaño de las operaciones. En este caso, este valor es 0X40c.

A continuación podemos ver los valores que se están sumando:

Cuando sale del bucle, se calcula el tamaño total. En este caso, el tamaño total es 0x1310.

Podemos adivinar la diferencia entre la versión vulnerable y la parcheada calculando el tamaño, usando la fórmula: number_of_operations * 4.
En este caso, la asignación en la versión parcheada será 0x34 * 4 = 0x68 mayor que en la versión vulnerable.

Después de eso, suma 0x24. Este valor se calcula de manera similar tanto en la versión vulnerable como en la parcheada.

Luego suma la constante 0xf en ambos casos.

Hasta este punto, el tamaño en este ejemplo ha sido 0x1340.

Luego llega a rpcxdr_OncRpcBufMgrpAllocate.

Luego se mueve a r15.