
Analyse détaillée et preuve de concept d'exploitation pour CVE-2017-9822, une vulnérabilité XXE/désérialisation non sécurisée dans le CMS DotNetNuke menant à une exécution de code à distance via la manipulation de cookies.
DotNetNuke (souvent abrégé DNN) est une plateforme CMS (Content Management System) et un framework d'application web basé sur la technologie ASP.NET de Microsoft.
Produit concerné : DotNetNuke (DNN Platform) – un CMS/portail .NET populaire.
Date de publication : Juillet 2017.
Sévérité : Critique (CVSS ~9.8).
Type de vulnérabilité : XML External Entity (XXE) / Désérialisation non sécurisée → Exécution de code à distance (RCE).
Impact : avant la version 9.1.1, exécution de code à distance possible via le cookie
Ici, j'utilise Windows 10 pour installer et déboguer le programme. La version que j'installe est 9.1.0. Vous pouvez consulter la méthode d'installation ici. Et voici le résultat une fois terminé :


D'après les rapports que j'ai lus, cette vulnérabilité se situe au niveau du traitement des cookies de DotNetNuke.
DNN utilise une désérialisation non sécurisée (unsafe deserialization) pour le cookie DNNPersonalization.

.dll ou .exe écrits en .NET. Vous pouvez l'installer ici. Nous devons télécharger 2 versions pour le débogage.
DotNetNuke.dll avec la version 32 bits, puis choisissez Edit Assembly Attributes (C#).
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
par
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default |
DebuggableAttribute.DebuggingModes.DisableOptimizations |
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints |
DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

Ensuite, enregistrez.
Attach to Process.
w3wp.exe.
La raison de choisir w3wp.exe est :
w3wp.exe = processus de travail IIS (IIS Worker Process).
C'est le processus d'exécution de l'Application Pool dans IIS.
Lorsqu'une requête HTTP est envoyée au site web, IIS crée ou réutilise un processus w3wp.exe pour traiter cette requête (exécution du code ASP.NET, gestion des modules, middleware, connexions à la base de données...).
Chaque Application Pool peut avoir un ou plusieurs processus w3wp.exe selon la configuration (web garden, recycling).
Ensuite, sélectionnez Debug -> Window -> Modules.

Une fois terminé, les modules apparaissent. Faites un clic droit sur n'importe quel module et sélectionnez Open All Modules.

Et enfin, tous les assemblys liés à DNN apparaîtront.

DotNetNuke.dll -> PersonalizationController#LoadProfile(int, int).
Cette fonction sert à charger les données de personnalisation (profil) de l'utilisateur dans le portail DNN.
Si l'utilisateur est connecté → le profil est récupéré depuis la base de données + le cache.
Si l'utilisateur est anonyme (non connecté) → le profil est récupéré depuis le cookie DNNPersonalization.
Ici, nous devrions nous concentrer sur DNNPersonalization.
Si userId n'est pas valide (utilisateur anonyme).
Vérifie si la requête contient le cookie DNNPersonalization.
Si c'est le cas → récupère la valeur XML de ce cookie.
Nous allons envoyer une requête 404 au site web avec un cookie DNNPersonalization quelconque, puis utiliser dnSpy pour placer un point d'arrêt (breakpoint) dans DotNetNuke.dll –> PersonalizationController#LoadProfile(int, int) ; le débogage fonctionnera alors.


PortalSettings.
Ce qui est intéressant, c'est qu'une condition if est utilisée ici pour vérifier si la requête actuelle est IsAuthenticated ou non.
Et comme la requête que nous envoyons est une 404 -> unauthenticated.
En continuant dans la pile d'appels, concentrons-nous sur Handle404OrException.

context.User de la requête actuelle est null ; si c'est le cas, elle affecte à context.User l'utilisateur du thread actuel.

On voit que dans Handle404OrException, la variable IsAuthenticated a maintenant la valeur true et l'utilisateur est celui du serveur IIS ; la requête est donc traitée comme un utilisateur authentifié.
La cause du problème se trouve dans ce morceau de code :
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 n'existe pas → affecte Thread.CurrentPrincipal (c'est-à-dire l'identité actuelle du thread).
Cela donne à la requête les informations sur l'utilisateur/rôle lors du traitement suivant.
=> Ainsi, lorsque nous transmettons n'importe quel contenu dans le cookie DNNPersonalization, il est traité comme un utilisateur normal.