
Análisis técnico paso a paso y desarrollo de exploit para CVE-2017-9822, una RCE crítica en DotNetNuke a través de deserialización XML insegura en la cookie DNNPersonalization. Incluye configuración del entorno, configuración de depuración con dnSpy, construcción de la cadena de gadgets (ObjectDataProvider, ResourceDictionary) y carga de webshell.
DNN (también conocido como DotNetNuke) anterior a la versión 9.1.1 tiene capacidad de ejecución remota de código a través de cookies, también conocido como "2017-08 (Importante) Posible ejecución remota de código en sitios web DNN".

¿Qué es DotNetNuke?
DotNetNuke es un CMS (sistema de gestión de contenidos) web gratuito y de código abierto escrito en C# y basado en la plataforma .NET. DotNetNuke es muy popular y se usa ampliamente en Internet porque puede implementar una versión web de DNN en minutos sin necesidad de muchos conocimientos técnicos. Otra función importante de DotNetNuke es la capacidad de crear o importar módulos personalizados de terceros creados en VB.NET o C#.
Se puede instalar DNN en un stack que incluya Windows Server, IIS, ASP.NET y SQL Server para Windows. DNN también admite el registro de verificación de nuevos usuarios por correo electrónico, pero necesita configurar un servidor SMTP válido para que esta función de seguridad funcione.
Características principales de DNN:
• Arquitectura modular: DNN permite una fácil expansión instalando módulos adicionales (módulos funcionales) desarrollados por la comunidad. Los administradores pueden cargar nuevos módulos a través de la interfaz de administración (cargar un paquete .zip) o descomprimirlos directamente en el directorio del servidor.
• Administración de usuarios: El sistema proporciona funciones de seguridad y permisos detallados (roles/permisos) para portales y módulos. Las cuentas de usuario, roles y permisos se gestionan de forma centralizada en DNN.
• Gestión de contenido: Soporta edición WYSIWYG, gestión de artículos, imágenes, documentos, etc. Tiene un sistema de flujo de trabajo/publicación (publicación con proceso de revisión) y versionado de contenido. El contenido se almacena en una base de datos (SQL Server) común.
• API e integración extensible: DNN proporciona una API .NET para que los desarrolladores creen módulos personalizados (WebForms, MVC, Razor) e integren servicios externos. Hay disponibles muchas bibliotecas de terceros (temas de interfaz, módulos de comercio electrónico, foros, etc.) para ampliar la funcionalidad.
• Interfaz y temas: El sistema de skins (temas) separa el contenido de la interfaz, lo que permite un diseño web flexible. Los sitios web creados con DNN pueden cambiar su apariencia cambiando de skin.
• Mecanismo de instalación de módulos: Los módulos de DNN se empaquetan en archivos ZIP y se pueden instalar a través de la interfaz de administración o descomprimiéndolos manualmente. DNN admite tanto módulos compilados (DLL .NET) como módulos Razor dinámicos; todos los módulos pueden otorgar o revocar acceso mediante la configuración de permisos en cada página.
Sistemas operativos: Windows 10
.NET Framework: 4.5.1+
Servidor web: Microsoft IIS 10
Servidor de base de datos: Microsoft® SQL Server® 2019 Express, SQL Server Management Studio
Versión de DotNetNuke: 9.1.0
Se pueden usar los siguientes Google dorks para encontrar versiones de DotNetNuke implementadas disponibles en Internet y verificarlas según el sitio web:
inurl:dnn.js
inurl:dnn.modalpopup.js
inurl:dnn.servicesframework.js
inurl:dnn.xml.js
inurl:dnncore.js
inurl:/Portals/0/
inurl:/DesktopModules/
inurl:/DNNCorp/
inurl:/DotNetNuke
inurl:/tabid//Default.aspx
inurl:/tabid//language/*/Default.aspx
intext:"by DNN Corp "
Se puede seguir este artículo para construir el entorno:
Cambiar las propiedades del ensamblado a atributos "depurables", esto es muy necesario porque en tiempo de ejecución se aplican algunas optimizaciones que pueden obstaculizar la depuración; algunos puntos de interrupción podrían no alcanzarse o algunas variables podrían no existir.
Cargar DotNetNuke.dll en dnSpy (32 bits) y luego seleccionar Edit Assembly Attributes (C#).

Cambiar la línea de:
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
a:
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

Luego seleccionar Compile y guardar este módulo en la ubicación original.
A continuación, iniciar dnSpy con permisos de administrador y seleccionar Debug -> Attach to Process.

Seleccionar el proceso w3wp.exe.

La razón por la que debemos adjuntar este proceso es que las aplicaciones web en IIS suelen usar procesos de trabajo (worker processes). Estos se encargan de procesar las solicitudes web que llegan al servidor web IIS para cada grupo de aplicaciones. Puede haber varios procesos de trabajo en una máquina y todos comparten el mismo nombre: w3wp.exe. Una nota: a veces no hay ningún proceso w3wp en ejecución; IIS no iniciará los procesos de trabajo hasta que reciba la primera solicitud web.

Volviendo a la depuración, después de adjuntar el proceso, seleccionar: Debug -> Windows -> Modules.

Hacer clic en un módulo y seleccionar Open All Modules.

En la ventana Assembly ahora podemos ver todos los módulos relacionados.

La deserialización es el proceso de interpretar flujos de bytes y convertirlos en datos que la aplicación pueda ejecutar.
El problema principal con la deserialización es que la mayoría de las veces puede usar datos de entrada del usuario. Esto significa que se pueden insertar payloads maliciosos en el formato requerido por la aplicación y manipular la lógica, revelar datos o incluso ejecutar código de forma remota.
DotNetNuke utiliza la cookie DNNPersonalization para almacenar las preferencias de personalización de usuarios anónimos (las preferencias de usuarios autenticados se almacenan a través de su página de perfil). Según el informe, la vulnerabilidad ocurre en el manejo de la cookie DNNPersonalization, que se usa para cargar el perfil de usuario, pero aún se puede activar sin autenticación al acceder a una página inexistente (error 404). El punto de entrada de este bug se encuentra en la función LoadProfile del módulo DotNetNuke.dll. Descompilaremos este módulo con dnSpy y analizaremos más a fondo:
En PersonalizationController#LoadProfile(int, int)

Si userId no es nulo, la variable text se asignará con el valor de la cookie DNNPersonalization de la solicitud y luego se llamará a Globals#DeserializeHashTableXml.

Globals#DeserializeHashTableXml llama a XmlUtils#DeSerializeHashtable.

El proceso de manejo es el siguiente:
Usar Burp para enviar una solicitud que active el estado 404 + cookie DNNPersonalization.

Aquí se puede ver que la función llamada para manejar el 404 es Handler404OrException, que desencadena una cadena de llamadas y llama a Personalization.LoadProfile(int,int).

Lo notable en el código anterior es la condición if que verifica si la solicitud actual ya está autenticada (IsAuthenticated), y claramente la solicitud que acabamos de hacer no está autenticada y va a un punto de entrada inexistente. Entonces, ¿por qué la solicitud actual se ejecuta como un usuario autenticado?
Continuamos depurando. Retrocediendo casi al final de la pila, vemos en AdvancedUrlRewriter#Handle404OrException un else if como el siguiente:

Aquí verifica si request context.User actual es nulo; si es así, asigna context.User como el usuario del hilo actual. Al establecer un punto de interrupción, se puede ver el resultado:

La variable IsAuthenticated ahora tiene el valor true y el usuario asignado es el usuario que ejecuta el hilo actual, perteneciente al grupo IIS APPPOOL del servidor IIS, por lo que la solicitud se ejecuta como un usuario autenticado. La razón de que exista esta lógica es que el manejador 404 se invoca antes de que se establezca HttpContext.User, y el flujo de procesamiento posterior se basa en User.IsAuthenticated; para evitar errores de referencia nula, los desarrolladores asignan el objeto User con el objeto WindowsPrincipal del hilo actual.
XmlSerializer es la clase de serialización propia de Microsoft, utilizada para convertir entre cadenas y objetos xml. Su espacio de nombres es: System.Xml.Serialization.
Ejemplo de uso de XmlSerializer:


La condición para un ataque RCE a través de XmlSerializer es que es obligatorio controlar el tipo de datos pasado al constructor de XmlSerializer. Es decir, los tipos de datos que llevan al gadget deben pasarse al atributo XmlSerializer.mapping.
El gadget más común para atacar la deserialización xml es ObjectDataProvider. Este gadget se puede crear con la herramienta ysoserial .net.
Básicamente, al usar esta clase, podemos llamar a cualquier método de cualquier clase.

Por ejemplo, podemos llamar a Process.Start con los parámetros que se pasan a continuación:
ObjectDataProvider o = new ObjectDataProvider(); o.MethodParameters.Add("cmd.exe"); o.MethodParameters.Add("/c calc"); o.MethodName = "Start"; o.ObjectInstance = new Process(); Console.ReadKey();
Construir un payload de deserialización xml con el código anterior:


ResourceDictionary se usa para desarrollar WPF; como es WPF, debe usar el lenguaje XAML. Primero veamos un payload que usa ResourceDictionary para ejecutar comandos.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:b="clr-namespace:System;assembly=mscorlib" xmlns:c="clr-namespace:System.Diagnostics;assembly=system"> <ObjectDataProvider d:Key="" ObjectType="{d:Type c:Process}" MethodName="Start"> <ObjectDataProvider.MethodParameters> <b:String>cmd</b:String> <b:String>/c calc</b:String> </ObjectDataProvider.MethodParameters> </ObjectDataProvider> </ResourceDictionary>
Explicación de este XAML:

La ejecución del código anterior equivale a ObjectDataProvider -> Person.Evil(). Si se realiza un ataque RCE a través de XmlSerializer, el flujo se vería así: ObjectDataProvider -> XamlReader.Parse() -> ObjectDataProvider -> System.Diagnostics.Process.Start("cmd.exe","/c calc").
El objetivo ahora es encontrar un objeto que pueda ejecutar código al deserializar. En el PoC, se utiliza la función PullFile de DotNetNuke.Common.Utilities.FileSystemUtils para explotar la "subida arbitraria de archivos".


Obtenemos obj.xml:

Enviar el payload.


Después de que DNN deserializa las cookies, en el servidor http hay una solicitud a /cmd.aspx, lo que significa que la deserialización fue exitosa y la webshell se ha subido a DNN.

De manera similar, aprovechando la función WriteFile de DotNetNuke.Common.Utilities.FileSystemUtils para leer archivos.

