In-depth technical analysis and proof-of-concept for CVE-2017-9822, an insecure deserialization vulnerability in DotNetNuke leading to remote code execution via crafted cookies.
DNN (also known as DotNetNuke) before version 9.1.1 has the ability to execute remote code via cookies, also known as "2017-08 (Critical) Possible Remote Code Execution on DNN websites".

What is DotNetNuke?
DotNetNuke is a free and open-source web CMS (Content Management System) written in C# and based on the .NET platform. DotNetNuke is very popular and widely used on the Internet because you can deploy a DNN web instance in minutes without much technical knowledge. Another important feature of DotNetNuke is the ability to create or import custom third-party modules built with VB.NET or C#.
DNN can be installed on a stack consisting of Windows Server, IIS, ASP.NET, and SQL Server for Windows. DNN also supports new user registration verification via email, but you need to configure a valid SMTP server for this security feature to work.
Main features of DNN
• DNN allows easy extension by installing additional modules (functional modules) developed by the community. Administrators can load new modules through the admin interface (upload .zip package) or extract them directly into a directory on the server.
• The system provides detailed security and permission features (roles/permissions) for portals and modules. User accounts, roles, and permissions are centrally managed within DNN.
• Supports WYSIWYG editing, management of articles, images, documents, etc. It has a workflow/publishing system (post approval process) and content versioning. Content is stored in a common SQL Server database.
• DNN provides .NET APIs for developers to build custom modules (WebForms, MVC, Razor) and integrate with external services. A wide range of third-party libraries are available (themes, e-commerce modules, forums, etc.) to extend functionality.
• A skin system separates content from the interface, allowing flexible web design. Websites created with DNN can change their appearance by switching skins.
• DNN modules are packaged in ZIP files and can be installed via the admin interface or by manual extraction. DNN supports both compiled modules (.NET DLL) and dynamic Razor modules; every module can be granted or revoked access through permission settings on each 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
You can use the following Google dorks to find available DotNetNuke deployments on the Internet and check them against the website:
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 "
You can follow this article to build the environment:
Modify the assembly attributes to "debuggable" properties; this is essential because at runtime, some optimizations are applied that may inadvertently hinder debugging; some breakpoints may not be hit or some variables may not exist.
Load DotNetNuke.dll into dnSpy (32-bit) then select Edit Assembly Attributes (C#)

Change the line from
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
To
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

Then select Compile and save this module back to its original location.
Next, launch dnSpy with admin privileges and select Debug -> Attach to Process

Select the process w3wp.exe

The reason we must attach to this process for debugging is that web applications on IIS typically use worker processes. They handle web requests sent to the IIS web server for each application pool. There can be multiple worker processes on a machine, all named w3wp.exe. A small note: sometimes there is no w3wp process running; IIS will not start worker processes until the first web request is received.

Returning to debugging, after attaching the process, next select: Debug -> Windows -> Modules

Click on a module and select Open All Modules

At this point, in the Assembly window, you can see all related modules

Deserialization is the process of interpreting byte streams and converting them into data that the application can execute.
The main issue with Deserialization is that most of the time it can use user input data. This means you can inject malicious payloads into the required format of the application and possibly manipulate logic, leak data, or even execute remote code.
DotNetNuke uses the DNNPersonalization cookie to store anonymous user personalization preferences (authenticated user preferences are stored via their profile page). According to the report, the vulnerability occurs in the handling of the DNNPersonalization cookie, which is used to load user profiles but can still be triggered unauthenticated when accessing a non-existent page (404 error). The entry point of this bug is in the LoadProfile function belonging to the DotNetNuke.dll module. We will decompile this module using dnSpy and analyze it further:
At PersonalizationController#LoadProfile(int, int)

If userId is not null, then the variable text is assigned the value of the DNNPersonalization cookie from the request, then calls Globals#DeserializeHashTableXml

Globals#DeserializeHashTableXml calls XmlUtils#DeSerializeHashtable

Processing flow as follows:
Use Burp to send a request that triggers a 404 status + DNNPersonalization cookie

Here we can see that the function called to handle 404 is Handler404OrException which triggered a call chain and invoked Personalization.LoadProfile(int,int).

Notable in the above code is the if condition checking whether the current request is IsAuthenticated, and clearly our request was unauthenticated to a non-existent entrypoint. So why was the current request executed as an authenticated user?
Continuing debugging, going back near the bottom of the stack, at AdvancedUrlRewriter#Handle404OrException there is an else-if block as follows:

Here it checks if the request context.User is null; if true, it assigns context.User as the current thread user. Setting a breakpoint shows the following result:

The IsAuthenticated variable is now true, and the user is assigned the user running the current thread, which belongs to the IIS APPPOOL group of the IIS server, so the request is executed as an authenticated user. The reason this logic exists is that the 404 handler is invoked before HttpContext.User is set, and the subsequent processing flow relies on User.IsAuthenticated, so to avoid Null reference errors, developers assign the User object as the WindowsPrincipal object of the current thread.
XmlSerializer is Microsoft's own serializer class, used to convert between strings and XML objects. Its namespace is: System.Xml.Serialization.
Example of using XmlSerializer:


The condition for an RCE attack via XmlSerializer is that you must control the data type passed to the XmlSerializer constructor. That is, the data types leading to the gadget must be passed into the XmlSerializer.mapping property.
The most common gadget for attacking XML deserialization is ObjectDataProvider. This gadget can be generated by the ysoserial .net tool.
Basically, when using this class, you can call any method of any class.

For example, we can call Process.Start with parameters as shown below:
ObjectDataProvider o = new ObjectDataProvider(); o.MethodParameters.Add("cmd.exe"); o.MethodParameters.Add("/c calc"); o.MethodName = "Start"; o.ObjectInstance = new Process(); Console.ReadKey();
Construct an XML deserialization payload with the above code:


ResourceDictionary is used for WPF development; because it is WPF, it must use the XAML language. First, let’s look at a payload using ResourceDictionary to execute commands.
<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>
Explanation of this XAML:

Executing the above code is equivalent to ObjectDataProvider -> Person.Evil(). If performing an RCE attack via XmlSerializer, the flow would look like this: ObjectDataProvider -> XamlReader.Parse() -> ObjectDataProvider -> System.Diagnostics.Process.Start("cmd.exe","/c calc")
The goal now is to find an object that can execute code during deserialization. In the POC, the PullFile function of DotNetNuke.Common.Utilities.FileSystemUtils is used to exploit "arbitrary file upload".


We get obj.xml:

Send payload


After DNN deserializes the cookies, the HTTP server receives a request to /cmd.aspx, meaning the deserialization succeeded, and the webshell has been uploaded to DNN.

Similarly, exploiting the WriteFile function of DotNetNuke.Common.Utilities.FileSystemUtils to read files.

