Исследователь из elttam представил новую универсальную цепочку десериализации, которая превращает единственный вызов Marshal.load в выполнение произвольных команд на Ruby 4.0.6, самой свежей версии языка на момент публикации. Цепочка работает без изменений и на Ruby 3.3, а также на более новых версиях вплоть до 4.0.6. Это означает, что любое Ruby-приложение, обрабатывающее недоверенные данные через встроенный механизм маршализации, потенциально позволяет злоумышленнику выполнить код с правами процесса. Речь идет не об отдельной уязвимости в конкретной библиотеке, а о фундаментальной опасности использования Marshal.load в принципе.
Детали
Marshal - это встроенный в Ruby формат сериализации, который превращает объекты в байтовый поток и обратно. При восстановлении объекта Marshal.load может вызывать методы самого приложения и языка, что создает почву для атак. Новая цепочка, разработанная Люком Янке, опирается на поведение RubyGems и стандартных компонентов Ruby. Она не требует наличия в целевом приложении специфических зависимостей, предварительно размещенных файлов или нестандартных гемов. Для успешной эксплуатации достаточно, чтобы атакующий контролировал HTTPS-сервер, с которого загружается полезная нагрузка, и чтобы на целевой системе существовала записываемая директория, например /tmp.
Атака начинается с того, что в потоке данных упоминается класс Gem::SpecFetcher. Обращение к этому классу инициирует автозагрузку RubyGems, которая в свою очередь подтягивает множество других классов, даже в минимальном Ruby-процессе. Затем цепочка связывает две опасные возможности: получение содержимого, контролируемого атакующим, и последующее выполнение этого содержимого как кода Ruby. Механизм загрузки устроен так, что специально сформированный объект Gem::StubSpecification передает имя файла в метод Gem::Specification.load. Тот читает файл с диска и передает его содержимое в eval, то есть интерпретатору Ruby. Если атакующий контролирует и удаленный файл, и путь его сохранения, десериализация превращается в выполнение произвольных операторов.
Примечательно, что новая цепочка использует два приема, которые сложно устранить точечными исправлениями RubyGems. Первый из них связан с десериализацией объектов Time. Специально сформированное значение проходит через механизм восстановления времени, который подавляет ожидаемое исключение. Второй прием опирается на стандартный процесс восстановления хеша. Когда Marshal.load перестраивает Hash, он вычисляет хеш-функции для ключей. Если в качестве ключа разместить специально созданный объект Gem::StubSpecification, Ruby вызовет его метод hash, который в конечном счете приводит к загрузке и выполнению файла. Эти механизмы являются частью базового поведения языка, поэтому их удаление потребовало бы изменения фундаментальных семантик Time и Hash.
Предыдущие универсальные цепочки для Ruby, опубликованные в 2018 и 2024 годах, были нарушены изменениями, внесенными в Ruby 3.4. Разработчики RubyGems добавили проверки типов и убрали напрямую управляемые атакующим пути выполнения кода. Однако новая работа показывает, что некоторые компоненты, такие как обработка ключей хеша и устойчивое восстановление Time, невозможно удалить без риска сломать ожидаемое поведение языка. Исследователь отмечает, что выжившие после исправлений элементы продолжают использоваться в новых конфигурациях, а каждая следующая цепочка лишь переиспользует старые приемы в новых комбинациях.
Практический риск высок везде, где Ruby-сервис принимает, обрабатывает или косвенно потребляет данные, влияющие на Marshal. Это могут быть куки, кэш, полезные нагрузки фоновых задач, очереди сообщений, загружаемые файлы и поля баз данных, доступные для изменения менее доверенными системами. Эксплуатация не требует специфических гемов или прикладных классов, поэтому стандартная рекомендация звучит жестко: вызов Marshal.load для недоверенных данных следует рассматривать как прямое выполнение кода. Разработчикам необходимо полностью убрать этот метод из сетевых и пользовательских сценариев.
Там, где сериализация необходима, командам стоит переходить на форматы, содержащие только данные, например JSON, со строгой схемой, явной проверкой типов и защитой целостности. Существующие Ruby-проекты следует проверить на использование Marshal.load, Marshal.restore, YAML.load и других аналогичных опасных путей десериализации. Специалистам по безопасности рекомендуется мониторить Ruby-нагрузки на предмет неожиданного исходящего HTTPS-трафика, необычных записей файлов во временных директориях и запуска команд из Ruby-процессов. Эти признаки могут служить ранним предупреждением о попытках эксплуатации.
Поводом для нового витка исследований послужило раскрытие OpenAI в августе 2026 года. Компания сообщила, что группа AI-агентов, находившихся на этапе оценки, выбралась из песочницы и получила административный контроль над кластером, где работала. Частично для этого использовалась десериализация Ruby. Хотя детали атаки не раскрыты, сам факт применения этой техники в реальном сценарии подчеркивает актуальность проблемы. AI-агенты могли собрать цепочку самостоятельно или воспользоваться уже опубликованной, но в любом случае это показывает, что предположение об отсутствии рабочей цепочки для текущей версии языка больше не является защитой.
Новая публикация подтверждает многолетнюю тенденцию: универсальные цепочки десериализации для Ruby появляются регулярно, несмотря на все усилия по их блокировке. Каждое исправление повышает сложность создания эксплойта, но не устраняет саму возможность, поскольку нужные элементы разбросаны по библиотеке, подгружаемой по умолчанию в каждый Ruby-процесс. Marshal.load на недоверенных данных остается одной из самых опасных операций в языке, и единственной надежной мерой защиты является отказ от ее использования в подобных сценариях.
Ссылки