
Подробный анализ и proof-of-concept эксплойт для CVE-2017-9822 — уязвимости XXE/небезопасной десериализации в DotNetNuke CMS, приводящей к удалённому выполнению кода через манипуляцию cookie.
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, с порядком установки можно ознакомиться здесь. Результат по завершении:


Согласно отчётам, которые я читал, эта уязвимость находится в месте обработки cookie в DotNetNuke
DNN использует небезопасную десериализацию (unsafe deserialization) для cookie DNNPersonalization

.dll или .exe, написанных на .NET. Установить можно здесь. Для отладки нам нужно скачать две версии.
DotNetNuke.dll в 32-битной версии и выберите Edit Assembly Attributes (C#)
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default |
DebuggableAttribute.DebuggingModes.DisableOptimizations |
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints |
DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

После этого сохраните изменения.
Attach to Process
w3wp.exe
Причина выбора 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

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

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

DotNetNuke.dll -> PersonalizationController#LoadProfile(int, int)
Эта функция используется для загрузки данных персонализации (профиля) пользователя на портале DNN.
Если пользователь авторизован → профиль берётся из базы данных + кэша.
Если пользователь анонимный (не авторизован) → профиль берётся из cookie DNNPersonalization.
Здесь стоит сосредоточиться на DNNPersonalization
Если userId недействителен (анонимный пользователь).
Проверяется, есть ли в запросе cookie DNNPersonalization.
Если есть → из этого cookie извлекается XML-значение.
Отправим запрос 404 на сайт и используем произвольный DNNPersonalization; поставим в dnSpy точку останова (Breakpoint) в DotNetNuke.dll –> PersonalizationController#LoadProfile(int, int) — тогда можно будет отладить.


PortalSettings
Обратите внимание: здесь с помощью условия if проверяется, аутентифицирован ли уже текущий запрос (IsAuthenticated)
И в то время как отправленный нами запрос — 404 -> unauthenticated
Далее в стеке вызовов сосредоточимся на Handle404OrException

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

Мы видим, что в Handle404OrException переменная IsAuthenticated теперь имеет значение true, а пользователь — это пользователь IIS-сервера, поэтому запрос выполняется как аутентифицированный пользователь.
Причина проблемы кроется в этом фрагменте кода
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 любое содержимое, оно выполняется как обычный пользователь.
Всё ещё в DotNetNuke.dll –> PersonalizationController#LoadProfile(int, int)
Видно, что переменная text получает значение из cookie, после чего оно передаётся как входные данные в Globals.DeserializeHashTableXml()
