Как простая обработка XML-файлов может стать дефектом безопасности? Каким образом блог, развёрнутый на вашей машине, может стать причиной утечки данных? Сегодня мы ответим на эти вопросы и разберём, что такое XXE и как эта уязвимость выглядит в теории и на практике.
Сразу хочется отметить, что возможных типов уязвимостей, связанных с обработкой XML, несколько. Наиболее популярными, пожалуй, являются XXE, XEE, XPath injection. Сегодня мы поговорим про XXE. Если вам интересно почитать, в чём суть XEE, предлагаю ознакомиться со статьёй "Как Visual Studio 2022 съела 100 Гб памяти и при чём здесь XML бомбы?". До XPath injection доберёмся как-нибудь в следующий раз. :)
XXE (акроним от XML eXternal Entities) – дефект безопасности приложения, который может образоваться в результате парсинга скомпрометированных данных небезопасно сконфигурированным XML-парсером. Последствием атаки может быть, например, раскрытие данных с целевой машины или SSRF (server-side request forgery).
Стандарт XML предусматривает возможность использования DTD (document type definition), описывающего структуру XML-документа. DTD даёт нам возможность определять и использовать XML-сущности.
Выглядеть это может, например, так:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE order [
<!ENTITY myEntity "lol">
]>
<order>&myEntity;</order>
В этом XML мы определили сущность myEntity, которую дальше используем – &myEntity;. В данном случае сущность является внутренней и определена как литерал. Если XML-парсер выполнит раскрытие этой сущности, то вместо &myEntity; будет подставлено фактическое значение – lol. Кроме того, внутренние сущности могут раскрываться через другие. Таким образом могут создаваться XML-бомбы и проводиться XEE-атаки.
Однако сущности могут быть и внешними. Они могут ссылаться на какие-то локальные файлы или обращаться к внешним ресурсам:
<!ENTITY myExternalEntity SYSTEM "https://test.com/target.txt">
Пример XML-файла, в котором внешняя сущность ссылается на локальный файл:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE order [
<!ENTITY myExternalEntity SYSTEM "file:///D:/HelloWorld.cs">
]>
<order>&myExternalEntity;</order>
В данном случае XML-парсер вместо сущности myExternalEntity подставит содержимое файла D:/HelloWorld.cs. При условии, что он сконфигурирован соответствующим образом, конечно.
Суть XXE-атаки заключается в том, чтобы эксплуатировать описанную выше особенность.
Разберём на примере. Предположим, что есть приложение, которое принимает запросы в виде XML-файлов и обрабатывает товары с соответствующим идентификатором.
Формат XML-файла, с которым работает приложение:
<?xml version="1.0" encoding="utf-8" ?>
<order>
<itemID>62</itemID>
</order>
Упрощённый C# код:
static void ProcessItemWithID(XmlReader reader, String pathToXmlFile)
{
....
while (reader.Read())
{
if (reader.Name == "itemID")
{
var itemIdStr = reader.ReadElementContentAsString();
if (long.TryParse(itemIdStr, out var itemIdValue))
{
// Process item with the 'itemIdValue' value
Console.WriteLine(
$"An item with the '{itemIdValue}' ID was processed.");
}
else
{
Console.WriteLine($"{itemIdStr} is not valid 'itemID' value.");
}
}
}
}
Логика тривиальна:
Соответственно, для приведённого ранее XML-файла приложение распечатает следующую строку:
An item with the '62' ID was processed.
Если вместо номера в ID будет записано что-то другое (например, строка "Hello world"), приложение сообщит об ошибке:
"Hello world" is not valid 'itemID' value.
Если XML-парсер (reader), который обрабатывает файлы, разбирает внешние сущности, возникает дефект безопасности. Ниже представлен XML-файл, с помощью которого можно скомпрометировать приложение:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE order [
<!ENTITY xxe SYSTEM "file:///D:/MySecrets.txt">
]>
<order>
<itemID>&xxe;</itemID>
</order>
В этом файле объявляется внешняя сущность xxe. При обработке вместо &xxe; будет подставлено содержимое файла D:/MySecrets.txt (например, такое: "This is an XXE attack target."). Соответственно, вывод приложения будет следующим:
"This is an XXE attack target." is not valid 'itemID' value.
Получается, что приложение уязвимо к XXE, если:
Если подставленное значение сущности как-то возвращается обратно злоумышленнику, он может получать содержимое файлов со скомпрометированного устройства. Понятно, что это уже опасно. Дополнительная же опасность состоит в том, что таким образом можно попробовать собрать больше данных о системе в целом и, как следствие, найти другие дефекты безопасности.
Кроме того, XXE может являться мостиком к SSRF-атаке. Суть в том, что у самого хакера может не быть доступа к каким-то ресурсам (ограничение доступа для внешних пользователей), но они могут быть у эксплуатируемого приложения. Так как XXE позволяет выполнять запросы и по сети, скомпрометированное приложение является брешью в защите ресурсов от внешнего мира.
Говоря про важность и опасность XXE следует вспомнить о том, что этот дефект безопасности часто фигурирует в различных стандартах, топах и перечислениях.
CWE
В Common Weakness Enumeration для XXE есть отдельная запись: CWE-611: Improper Restriction of XML External Entity Reference.
CWE Top 25
Каждый год из CWE отбираются 25 наиболее распространённых и опасных дефектов безопасности, из которых составляется CWE Top 25.
В 2021 году XXE потеряла 4 позиции по сравнению с 2020-ым, но всё ещё осталась в топе, обосновавшись на 23-ем месте.
OWASP ASVS
В OWASP ASVS (Application Security Verification Standard) приведён список требований к безопасной разработке. Тему XXE в нём также не обошли стороной: OWASP ASVS 4.0.3 (ID 5.5.2): Verify that the application correctly restricts XML parsers to only use the most restrictive configuration possible and to ensure that unsafe features such as resolving external entities are disabled to prevent XML eXternal Entity (XXE) attacks.
OWASP Top 10
В OWASP Top 10 2017 для XXE была выделена отдельная категория: A4:2017-XML External Entities (XXE). В OWASP Top 10 2021 отдельная категория для XXE была устранена, и теперь XXE входит в категорию A05:2021-Security Misconfiguration.
Как мы писали выше, для осуществления XXE нужно минимум 2 составляющих: опасно сконфигурированный парсер и данные от злоумышленника, которые он обрабатывает.
В принципе, здесь всё достаточно просто – часто в приложении есть несколько точек, в которых оно принимает внешние данные. Обрабатывать такие данные нужно со всей строгостью, так как не все пользователи используют ваше приложение по назначению и так, как вы задумывали.
Подобными точками входа являются аргументы консольного приложения, различные поля форм, данные запросов и тому подобное. Первое, самое простое и очевидное, что приходит в голову – консольный ввод.
var taintedVar = Console.ReadLine();
Мы не знаем, что хранится в taintedVar – лежат ли там данные в ожидаемом формате или же переменная содержит какую-нибудь строку для компрометации системы. Доверия к ней быть не должно.
Немного более подробно эта тема раскрыта в статье "OWASP, уязвимости и taint анализ в PVS-Studio C#. Смешать, но не взбалтывать" в разделе "Taint sources". Также с подозрением стоит относиться к параметрам публично-доступных методов. Сами по себе данные в них могут быть как безопасными, так и не очень. Про это писали здесь.
Для того чтобы парсер был уязвимым к XXE, он должен:
Если в XML-парсере не задано ограничение на максимальный размер сущностей (или этот размер велик), это может усилить негативные последствия атаки, так как позволит злоумышленнику извлекать данные большего объёма.
Интересующее нас поведение задаётся с помощью следующих свойств:
В одних XML-парсерах можно встретить все эти опции, в других – только некоторые. Их смысловое значение от типа к типу не изменяется.
ProhibitDtd
Свойство ProhibitDtd декорировано атрибутом Obsolete и на замену ему пришло свойство DtdProcessing. Тем не менее, оно всё так же может использоваться в старом коде. Значение true запрещает обработку DTD, false – разрешает.
DtdProcessing
Свойство DtdProcessing имеет тип System.Xml.DtdProcessing и может принимать значения Prohibit, Ignore и Parse:
Сразу отвечу на возможный вопрос. Свойства ProhibitDtd и DtdProcessing, если они встречаются вместе (например, в XmlReaderSettings) связаны друг с другом. Так что, даже если вы запретите обработку DTD в одном свойстве и разрешите в другом, актуальной будет только последняя выставленная опция.
XmlResolver
Свойство XmlResolver отвечает за то, какой объект используется для обработки внешних сущностей. Самый безопасный вариант – отсутствие резолвера вовсе (значение null). В таком случае, даже если включена обработка DTD, внешние сущности раскрываться не будут.
MaxCharactersFromEntities
Ещё одна интересующая нас опция – MaxCharactersFromEntities – отвечает за максимально допустимый размер сущностей. Чем больше значение, тем потенциально больший объём информации получится извлечь при проведении XXE-атаки.
Пожалуй, наиболее распространёнными стандартными типами, с которыми можно наткнуться на XXE, являются XmlReader, XmlTextReader, XmlDocument. Однако призываю помнить, что ими список не ограничивается.
Ещё раз подчеркну, что опасной считается конфигурация парсера, при которой он:
XmlReader
За поведение XmlReader отвечает объект XmlReaderSettings, создаваемый явно или неявно. Тип XmlReaderSettings содержит все настройки, перечисленные нами ранее.
Парсер с опасной конфигурацией может выглядеть так:
var settings = new XmlReaderSettings()
{
DtdProcessing = DtdProcessing.Parse,
XmlResolver = new XmlUrlResolver(),
MaxCharactersFromEntities = 0
};
using (var xmlReader = XmlReader.Create(xmlFileStringReader, settings))
....
Здесь разработчик явно разрешил обработку DTD, установил резолвер внешних сущностей, ещё и ограничение на их размер снял.
XmlTextReader
В случае с этим типом мы имеем дело всё с теми же знакомыми свойствами: ProhibitDtd, DtdProcessing, XmlResolver.
Пример парсера с опасной конфигурацией:
using (var xmlTextReader = new XmlTextReader(xmlFileStringReader))
{
xmlTextReader.XmlResolver = new XmlUrlResolver();
xmlTextReader.DtdProcessing = DtdProcessing.Parse;
....
}
XmlDocument
В случае с типом XmlDocument единственное интересующее нас свойство — XmlResolver. Парсер с опасной конфигурацией в данном случае может выглядеть так:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = new XmlUrlResolver();
xmlDoc в такой конфигурации будет раскрывать внешние сущности, а значит, может считаться опасным.
Выше мы рассматривали примеры, где настройки XML-парсеров задавались явно. Однако все перечисленные типы имеют какие-то настройки по умолчанию, и с ними есть пара интересных моментов.
Первый – в разных версиях .NET эти настройки могут отличаться.
Второй – настройки отличаются от типа к типу. Например, где-то обработка DTD по умолчанию будет выключена, где-то – включена.
То есть в определённых случаях XML-парсер может иметь опасную конфигурацию по умолчанию, даже если опасные настройки не прописаны явно.
Если совместить всё это — разные типы парсеров, отличие настроек по умолчанию в разных типах и версиях .NET — получаем неплохой такой объём информации, который может быть сложно удержать в голове (особенно поначалу).
Выходит, смотря только на код, порой нельзя сказать, сконфигурирован ли XML-парсер устойчивым к XXE или нет:
XmlDocument doc = new XmlDocument();
doc.Load(xmlReader);
Из этого кода непонятно, может ли doc обрабатывать внешние сущности или нет – нужно знать информацию о версии фреймворка.
Значения 'опасных' настроек поменялись между версиями .NET Framework 4.5.1 и .NET Framework 4.5.2. Ниже привожу таблицу, в которой указано, в каких версиях .NET парсеры с настройками по умолчанию устойчивы к XXE, в каких – нет.
Экземпляры типов |
.NET Framework 4.5.1 и ниже |
.NET Framework 4.5.2 и выше (включая .NET Core и .NET) |
---|---|---|
XmlReader (XmlReaderSettings) |
Safe |
Safe |
XmlTextReader |
Vulnerable |
Safe |
XmlDocument |
Vulnerable |
Safe |
Да, XmlReader (созданный на основе настроек – XmlReaderSettings) безопасен в .NET Framework 4.5.1 и ниже за счёт того, что в нём выключена обработка DTD.
Несмотря на то, что в новых версиях фреймворка по умолчанию парсеры настроены безопасно, лучшим решением может быть всё же явно прописывать необходимые настройки. Да, кода станет немного больше, но в то же время он будет более очевиден и устойчив при переносе между разными версиями .NET Framework.
Итак, с базовой теорией разобрались. Дальше мы посмотрим на реальную уязвимость, но перед этим предлагаю заварить чашечку кофе.
Выше мы разобрали теоретическую составляющую XXE, чуть более конкретно поговорили про эти дефекты безопасности в рамках .NET, рассмотрели, как выглядят небезопасные составляющие уязвимости с точки зрения кода. Теперь пришло время перейти к практике. В этом нам поможет BlogEngine.NET.
Описание проекта с сайта: BlogEngine is an open source blogging platform since 2007. Easily customizable. Many free built-in Themes, Widgets, and Plugins.
Исходный код проекта доступен на GitHub.
Для нас этот проект интересен в первую очередь тем, что в нём было найдено целых 3 уязвимости XXE. Все они были исправлены в BlogEngine.NET v3.3.8.0. Значит, мы для экспериментов возьмём предыдущую версию проекта – v3.3.7.0. При желании вы можете повторить описанные шаги и самостоятельно 'пощупать' реальную XXE.
Для начала выгружаем соответствующую версию проекта — v3.3.7.0. Со сборкой проекта никаких проблем возникнуть не должно – она тривиальна. Я собирал через Visual Studio 2022.
После сборки запускаем на исполнение проект BlogEngine.NET, и если всё удалось, то увидим сайт примерно следующего вида:
Если сайт не будет доступен для других машин в той же сети "из коробки", я бы посоветовал немножко заморочиться и настроить это – так XXE 'щупать' будет интереснее.
При поиске уязвимостей у вас могут быть разные входные данные. Например, система может представлять для вас чёрный ящик, и тогда нужно будет собирать сведения о системе, искать точки воздействия на неё и тому подобное. Если же система представляет собой белый ящик, это меняет подход и используемые для достижения цели инструменты (или как минимум расширяет их список).
С Open Source проектами интересная штука выходит, как мне кажется. Вроде бы любой заинтересованный может работать с кодом и внести свой вклад в качество / безопасность. Однако с этим есть нюансы. С другой стороны, больше карт в руки попадёт и злоумышленникам – так как есть доступ к исходному коду, находить уязвимости должно быть легче. Вот только будут ли они зарепорчены?
Но оставим эти философские рассуждения и вернёмся к нашему делу.
Так как проект имеет открытый исходный код, воспользуемся этим преимуществом. Для поиска дефектов безопасности кроме собственных знаний вооружимся PVS-Studio (решение для поиска ошибок и дефектов безопасности). Нам понадобится группа диагностик, связанных с security, – OWASP. О том, как включить соответствующие предупреждения, можно почитать здесь.
В Visual Studio достаточно выставить значение "Show All" для группы OWASP на вкладке "Detectable Errors (C#)": Extensions > PVS-Studio > Options > Detectable Errors (C#).
После этого нужно убедиться, что у вас включено отображение соответствующих предупреждений. В данном случае нас интересуют предупреждения группы 'OWASP' уровня достоверности 'High'. Следовательно, необходимые кнопки должны быть нажаты (обведены рамочкой).
Далее нужно запустить анализ решения (Extensions > PVS-Studio > Check > Solution) и дождаться результатов.
По CWE (помним, что XXE соответствует CWE-611) или OWASP ASVS ID (OWASP ASVS 5.5.2) легко найти то, что нас интересует, – 3 предупреждения V5614.
С точки зрения кода проблемы похожи. Мы разберём наиболее интересную (разнесённую по нескольким методам), а по остальным я просто предоставлю основную информацию.
XMLRPCRequest.cs
Предупреждение: V5614 [CWE-611, OWASP-5.5.2] Potential XXE vulnerability inside method. Insecure XML parser is used to process potentially tainted data from the first argument: 'inputXml'. BlogEngine.Core XMLRPCRequest.cs 41
На самом деле анализатор указывает на 3 строки для того, чтобы предупреждение было легче понять: 'опасный' вызов метода, источник потенциально скомпрометированных данных и место их использования опасно сконфигурированным парсером.
public XMLRPCRequest(HttpContext input)
{
var inputXml = ParseRequest(input);
// LogMetaWeblogCall(inputXml);
this.LoadXmlRequest(inputXml); // Loads Method Call
// and Associated Variables
}
Из сообщения следует, что inputXml может содержать 'заражённые' данные (см. taint checking), которые используются небезопасно сконфигурированным парсером внутри метода LoadXmlRequest. Таким образом здесь получился достаточно комплексный 'межпроцедурный' кейс: данные приходят из одного метода (ParseRequest) и затем передаются в другой (LoadXmlRequest), где уже и используются.
Начнём с данных – для этого нам понадобится код метода ParseRequest.
private static string ParseRequest(HttpContext context)
{
var buffer = new byte[context.Request.InputStream.Length];
context.Request.InputStream.Position = 0;
context.Request.InputStream.Read(buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
Сопроводим код трассой распространения заражения, чтобы было более очевидно, о чём речь.
Начинается всё со свойства context.Request, имеющего тип HttpRequest. Анализатор считает его источником заражения, так как данные, поступившие как запрос, могут быть скомпрометированы.
Есть различные способы эти данные извлечь, и работа с потоком (свойство InputStream) – один из них. Следовательно, taint-статус 'переходит' и на InputStream.
Далее для этого потока вызывается метод System.IO.Stream.Read, который считывает данные из потока InputStream в массив байт – buffer. Как следствие, buffer теперь тоже может содержать заражённые данные.
После этого вызывается метод Encoding.UTF8.GetString, который конструирует строку из массива байт (buffer). Так как исходные данные для создания строки заражены, сама строка также будет заражена. После конструирования строка возвращается из метода.
Выходит, что возвращаемое методом ParseRequest значение может быть скомпрометировано злоумышленником. По крайней мере в теории.
Вернёмся к исходному методу:
public XMLRPCRequest(HttpContext input)
{
var inputXml = ParseRequest(input);
// LogMetaWeblogCall(inputXml);
this.LoadXmlRequest(inputXml); // Loads Method Call
// and Associated Variables
}
Мы разобрались с ParseRequest. Предположим, что переменная inputXml действительно может содержать заражённые данные. Следующий шаг – проанализировать метод LoadXmlRequest, принимающий inputXml в качестве аргумента.
Сам метод достаточно большой (больше 100 строк), так что приведём его сокращённую версию и отметим место, на которое указывает анализатор.
private void LoadXmlRequest(string xml)
{
var request = new XmlDocument();
try
{
if (!(xml.StartsWith("<?xml") || xml.StartsWith("<method")))
{
xml = xml.Substring(xml.IndexOf("<?xml"));
}
request.LoadXml(xml); // <=
}
catch (Exception ex)
{
throw new MetaWeblogException("01",
$"Invalid XMLRPC Request. ({ex.Message})");
}
....
}
Как видим, аргумент действительно обрабатывается XML-парсером: request.LoadXml(xml). PVS-Studio здесь считает, что request сконфигурирован так, что является уязвимым к XXE. Наша задача – подтвердить это. Или же опровергнуть и тогда отметить предупреждение как false positive. Здесь нам пригодится теория, которую мы разбирали в начале статьи.
Тип объекта, на который указывает ссылка request – XmlDocument. Парсер имеет дефолтные настройки, а значит, нам понадобится узнать используемую версию .NET. Посмотреть её можно в свойствах проекта.
Смотрим табличку, которую мы разбирали ранее, и видим, что по умолчанию экземпляры типа XmlDocument в приложениях под .NET Framework 4.5.1 и ниже уязвимы к XXE.
Получается, что у нас совпали все факторы для потенциальной XXE:
В теории XXE здесь есть, но это всё ещё потенциальная уязвимость. Нам нужно убедиться в том, что атака возможна на практике. Для этого понадобится ещё немного копнуть код.
Мы начинали наш анализ с конструктора типа XMLRPCRequest. Он вызывается в одном месте:
internal class MetaWeblogHandler : IHttpHandler
{
....
public void ProcessRequest(HttpContext context)
{
try
{
var rootUrl = Utils.AbsoluteWebRoot.ToString();
// context.Request.Url.ToString().Substring(0,
// context.Request.Url.ToString().IndexOf("metaweblog.axd"));
var input = new XMLRPCRequest(context); // <=
....
}
....
}
....
}
Ага, мы наткнулись на HTTP handler. Для него же находим запись в конфиге:
<add name="MetaWeblog"
verb="*"
path="metaweblog.axd"
type="BlogEngine.Core.API.MetaWeblog.MetaWeblogHandler, BlogEngine.Core"
resourceType="Unspecified"
requireAccess="Script"
preCondition="integratedMode" />
Теперь мы знаем, куда нужно стучаться, чтобы сработал нужный нам обработчик. Давайте попробуем провести саму атаку.
Прежде всего нам понадобится XML-файл, через который мы и попытаемся 'стащить' данные с машины, где развёрнут блог:
<?xml version="1.0"?>
<!DOCTYPE xxe [
<!ENTITY externalEntity SYSTEM
"file:///C:/Windows/System32/drivers/etc/hosts">
]>
<xxe>&externalEntity;</xxe>
Если XML-парсер обрабатывает внешние сущности, то вместо &externalEntity; он должен подставить содержимое файла hosts.
Выполняем запрос, отправляем XML и исследуем, как будет отрабатывать наш обработчик. Для удобства есть смысл сохранить XML в файл (в данном примере - xxe.xml), чтобы при необходимости можно было легко менять его содержимое, не изменяя самой команды запроса.
curl -d "@xxe.xml" -X POST http://vasiliev-pc:8081/metaweblog.axd
Итак, хендлер поймал наш запрос и вызвал конструктор XMLRPCRequest, который мы рассматривали ранее.
Заходим внутрь конструктора и проверяем данные в переменной inputXml.
Ага, всё идёт по плану – данные оказались 'заражёнными', как мы предполагали (и хотели) и передаются в метод LoadXmlRequest в качестве аргумента. Наблюдаем дальше.
За счёт опасных дефолтных настроек парсер отработал в точности так, как мы ожидали, – загрузил содержимое файла hosts. Далее выполняется следующий фрагмент кода:
// Method name is always first
if (request.DocumentElement != null)
{
this.MethodName = request.DocumentElement.ChildNodes[0].InnerText;
}
По счастливому (для злоумышленника :)) стечению обстоятельств в MethodName будет записано как раз то, что нам нужно – hosts. Следующий интересующий нас фрагмент кода – большой switch, в котором в зависимости от имени метода выполняются те или иные действия:
switch (this.MethodName)
{
case "metaWeblog.newPost":
....
break;
case "metaWeblog.editPost":
....
break;
case "metaWeblog.getPost":
....
break;
....
default:
throw new MetaWeblogException("02", $"Unknown Method. ({MethodName})");
}
Здесь нас интересует default-ветвь, в которую и перейдёт исполнение, так как подходящий метод найден не будет. В этой ветви выбрасывается исключение, в сообщение которого будет подставлено имя метода, для которого не удалось выполнить сопоставление. Напоминаю, что в нашем случае имя метода – содержимое файла hosts.
В результате выброса исключения мы снова возвращаемся в хендлер и попадаем в обработчик, который сообщает о неизвестном методе:
В итоге на наш первоначальный запрос:
curl -d "@xxe.xml" -X POST http://vasiliev-pc:8081/metaweblog.axd
Получаем следующий ответ:
Получается, что с помощью XXE-атаки нам удалось вытащить содержимое hosts файла с машины, на которой просто был развёрнут блог. Зная расположение других файлов, можно попробовать получить и их содержимое. Причём не только с атакуемой машины, но и с других машин сети, к которым есть доступ. Здесь же, в контексте сетевых запросов, можно вспомнить и про SSRF.
Итак, только что мы с вами увидели XXE как с точки зрения приложения (кода), так и с точки зрения пользователя (злоумышленника). Это реальная уязвимость – CVE-2018-14485 (запись в базе NVD).
Что нужно делать с уязвимостями? Правильно, закрывать. Соответствующий коммит можно найти здесь. В исправлении поменяли конфигурацию XML-парсера, запретив ему обрабатывать внешние сущности. Для этого достаточно установить значение свойства XmlResolver в null:
var request = new XmlDocument() { XmlResolver = null };
Теперь, если попробовать достать всё тот же файл hosts, в вывод он не попадёт.
PVS-Studio, кстати, тоже учитывает, что парсер с такой конфигурацией (когда значение XmlResolver – null) не будет обрабатывать внешние сущности, и не выдаёт предупреждение на исправленный код.
Два других предупреждения, которые мы видели раньше, также указывают на уязвимости. Их разбирать не будем (с точки зрения кода суть похожа), но ниже я приведу основную информацию о них.
CVE-2019-10718
CVE-2019-11392
Теперь вы немного больше подкованы в вопросах безопасности и XXE, а также знаете, что даже простой блог, развёрнутый на вашей машине, может стать источником уязвимостей.
На самом деле, тема XXE глубже и, безусловно, ещё есть, куда копнуть. Но хотя бы просто знать об этом дефекте безопасности и понимать его на базовом уровне уже будет полезно.
Praemonitus, praemunitus.
По доброй традиции приглашаю подписаться на мой Twitter, чтобы не пропустить ничего интересного.