
Analyse technique étape par étape et développement d'exploit pour CVE-2017-9822, une RCE critique dans DotNetNuke via une désérialisation XML non sécurisée dans le cookie DNNPersonalization. Comprend la configuration de l'environnement, la configuration de débogage avec dnSpy, la construction de chaîne de gadgets (ObjectDataProvider, ResourceDictionary) et le téléversement de webshell.
DNN (également appelé DotNetNuke) avant la version 9.1.1 permet l'exécution de code à distance via un cookie, également connu sous le nom de "2017-08 (Important) Exécution de code à distance possible sur les sites DNN".

Qu'est-ce que DotNetNuke ? DotNetNuke est un CMS (système de gestion de contenu) web gratuit et open source écrit en C# et basé sur la plateforme .NET. DotNetNuke est très populaire et largement utilisé sur Internet car vous pouvez déployer une version web DNN en quelques minutes sans avoir besoin de connaissances techniques approfondies. Une autre fonctionnalité importante de DotNetNuke est la capacité de créer ou d'importer des modules personnalisés de tiers construits en VB.NET ou C#. Vous pouvez installer DNN sur une pile comprenant Windows Server, IIS, ASP.NET et SQL Server pour Windows. DNN prend également en charge l'enregistrement avec vérification par email pour les nouveaux utilisateurs, mais vous devez configurer un serveur SMTP valide pour que cette fonctionnalité de sécurité fonctionne. Principales fonctionnalités de DNN • Architecture modulaire : DNN permet une extension facile en installant des modules (modules fonctionnels) développés par la communauté. Les administrateurs peuvent télécharger de nouveaux modules via l'interface d'administration (télécharger des packages .zip) ou les extraire directement dans des dossiers sur le serveur. • Gestion des utilisateurs : Le système fournit des fonctionnalités de sécurité et d'autorisation détaillées (rôles/autorisations) pour les portails et les modules. Les comptes utilisateurs, les rôles et les permissions sont gérés de manière centralisée dans DNN. • Gestion de contenu : Prise en charge de l'édition WYSIWYG, gestion des articles, images, documents… Il existe un système de workflow/publication (publication avec processus de validation) et un versionnage du contenu. Le contenu est stocké sur une base de données (SQL Server) commune. • API et intégration étendue : DNN fournit des API .NET pour les développeurs afin de créer des modules personnalisés (WebForms, MVC, Razor) et d'intégrer des services externes. De nombreuses bibliothèques tierces sont disponibles (thèmes, modules e-commerce, forums, etc.) pour étendre les fonctionnalités. • Interface et thèmes : Le système de skins (thèmes) sépare le contenu de l'interface, permettant une conception web flexible. Les sites web créés avec DNN peuvent changer d'apparence en changeant de skins. • Mécanisme d'installation des modules : Les modules DNN sont empaquetés dans des fichiers ZIP, pouvant être installés via l'interface d'administration ou en les décompressant manuellement. DNN prend en charge à la fois les modules compilés (.NET DLL) et les modules Razor dynamiques ; chaque module peut être autorisé ou révoqué via les paramètres de permission sur chaque page.
Operating Systems: Windows 10 .NET Framework: 4.5.1+ Web Server: Microsoft IIS 10 Database Server: Microsoft® SQL Server® 2019 Express, SQL Server Management Studio Dotnetnuke version 9.1.0 Vous pouvez utiliser les Google dorks suivants pour trouver des versions de Dotnetnuke déployées sur Internet et les vérifier par rapport au site 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 " Vous pouvez suivre cet article pour construire l'environnement
Modifiez les propriétés de l'assembly pour les rendre « débogables » ; cela est essentiel car, lors de l'exécution, certaines optimisations sont appliquées et peuvent entraver le débogage : certains points d'arrêt peuvent ne pas être atteints ou certaines variables peuvent ne pas exister. Chargez DotNetNuke.dll dans dnSpy (32 bits) puis sélectionnez Edit Assembly Attributes (C#)

Changez la ligne de
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
à
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

Ensuite, sélectionnez Compile et enregistrez ce module à son emplacement d'origine. Ensuite, lancez dnSpy en tant qu'administrateur et sélectionnez Debug -> Attach to Process

Sélectionnez le processus w3wp.exe

La raison pour laquelle nous devons attacher ce processus pour déboguer est que les applications web sur IIS utilisent généralement des workers processes. Ils sont chargés de traiter les requêtes web envoyées au serveur web IIS pour chaque pool d'applications. Plusieurs workers processes peuvent exister sur une même machine et ils portent tous le même nom : w3wp.exe. Une petite remarque : il peut arriver qu'aucun processus w3wp ne soit en cours d'exécution ; IIS ne démarre les workers processes qu'à la réception de la première requête web.

Revenons au débogage : après avoir attaché le processus, sélectionnez : Debug -> Windows -> Modules

Cliquez sur un module et sélectionnez Open All Modules

À ce stade, dans la fenêtre Assembly, nous pouvons voir tous les modules concernés

La désérialisation est le processus d'interprétation des flux d'octets et de leur conversion en données pouvant être exécutées par l'application. Le principal problème avec la désérialisation est que la plupart du temps, elle peut utiliser des données d'entrée utilisateur. Cela signifie que vous pouvez injecter des charges utiles malveillantes dans le format attendu par l'application et ainsi manipuler la logique, divulguer des données ou même exécuter du code à distance. DotNetNuke utilise le cookie DNNPersonalization pour stocker les préférences de personnalisation des utilisateurs anonymes (les préférences des utilisateurs authentifiés sont stockées via leur page de profil). Selon le rapport, la vulnérabilité se produit dans le traitement du cookie DNNPersonalization, qui est utilisé pour charger le profil utilisateur mais peut également être déclenché sans authentification en accédant à une page inexistante (erreur 404). Le point d'entrée de ce bogue se trouve dans la fonction LoadProfile du module DotNetNuke.dll. Nous allons décompiler ce module avec dnSpy et l'analyser plus en détail : Tại PersonalizationController#LoadProfile(int, int)

Nếu userId khác null thì biến text sẽ được gán giá trị của DNNPersonalization cookie từ request sau đó gọi đến Globals#DeserializeHashTableXml

Globals#DeserializeHashTableXml gọi đến XmlUtils#DeSerializeHashtable

Le processus de traitement est le suivant :
Utilisez Burp pour envoyer une requête déclenchant un statut 404 + cookie DNNPersonalization

On peut voir ici que la fonction appelée pour gérer l'erreur 404 est Handler404OrException, qui a déclenché une chaîne d'appels et appelé Personalization.LoadProfile(int,int).

Ce qui est remarquable dans le code ci-dessus, c'est la condition if qui vérifie si la requête actuelle est authentifiée (IsAuthenticated), alors que la requête que nous venons d'effectuer est non authentifiée et pointe vers un point d'entrée inexistant. Pourquoi la requête actuelle est-elle traitée comme un utilisateur authentifié ? En continuant le débogage, en remontant vers la fin de la pile, on voit dans AdvancedUrlRewriter#Handle404OrException une boucle else if comme suit :

Ici, il vérifie si le context.User actuel de la requête est null ; si c'est le cas, il assigne context.User avec l'utilisateur du thread actuel. En plaçant un point d'arrêt, on peut voir le résultat suivant :

La variable IsAuthenticated a maintenant la valeur true et l'utilisateur assigné est l'utilisateur exécutant le thread actuel, appartenant au groupe IIS APPPOOL du serveur IIS, donc la requête est traitée comme un utilisateur authentifié. La raison de cette logique est que le gestionnaire 404 est invoqué avant que HttpContext.User ne soit défini, et le flux de traitement suivant dépend de User.IsAuthenticated. Pour éviter les erreurs de référence null, les développeurs assignent l'objet User avec l'objet WindowsPrincipal du thread actuel.
XmlSerializer est la classe de sérialisation propre à Microsoft, utilisée pour convertir entre des chaînes et des objets XML. Son espace de noms est : System.Xml.Serialization. Exemple d'utilisation de XmlSerializer :


La condition pour une attaque RCE via XmlSerializer est de pouvoir contrôler le type de données passé au constructeur de XmlSerializer. C'est-à-dire que les types de données menant à un gadget doivent être passés à la propriété XmlSerializer.mapping.
Le gadget le plus courant pour attaquer la désérialisation XML est ObjectDataProvider. Ce gadget peut être créé avec l'outil ysoserial .net
Fondamentalement, en utilisant cette classe, on peut appeler n'importe quelle méthode de n'importe quelle classe.

Par exemple, on peut appeler Process.Start avec les paramètres suivants :
ObjectDataProvider o = new ObjectDataProvider(); o.MethodParameters.Add("cmd.exe"); o.MethodParameters.Add("/c calc"); o.MethodName = "Start"; o.ObjectInstance = new Process(); Console.ReadKey();
Construire une charge utile XML de désérialisation avec le code ci-dessus :


ResourceDictionary est utilisé pour le développement WPF ; comme c'est WPF, il doit utiliser le langage XAML. Commençons par voir une charge utile utilisant ResourceDictionary pour exécuter des commandes.
<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>
Explication de ce XAML :

L'exécution du code ci-dessus équivaut à ObjectDataProvider -> Person.Evil(). Si l'on effectue une attaque RCE via XmlSerializer, le flux ressemblera à : ObjectDataProvider -> XamlReader.Parse() -> ObjectDataProvider -> System.Diagnostics.Process.Start("cmd.exe","/c calc")
L'objectif est maintenant de trouver un objet capable d'exécuter du code lors de la désérialisation. Dans le POC, la fonction PullFile de DotNetNuke.Common.Utilities.FileSystemUtils est utilisée pour exploiter le « téléchargement de fichier arbitraire ».


Nous obtenons obj.xml :

Envoyer la charge utile


Après que DNN a désérialisé les cookies, le serveur HTTP a reçu une requête vers /cmd.aspx, ce qui signifie que la désérialisation a réussi et que le webshell a été téléchargé sur DNN.

De même, on peut exploiter la fonction WriteFile de DotNetNuke.Common.Utilities.FileSystemUtils pour lire un fichier

