Как злоумышленники через датасеты Hugging Face выполняют код на вашем сервере. Узнайте, как защитить инфраструктуру от таких атак и провести аудит безопасности.
В середине 2024 года строчка load_dataset("hails/mmlu_no_train", "abstract_algebra") могла выполнить произвольный Python-код из репозитория незнакомца. Если в репозитории был .py-файл с таким же именем, load_dataset запускал его без предупреждения. Об этом рассказали на Black Hat 2024. Сейчас это исправлено, но другая сторона проблемы осталась — и в июле 2025 года через неё проникли в продакшн Hugging Face.
Вот как менялась защита в библиотеке datasets:
# datasets < 2.20.0 — удалённый скрипт выполняется по умолчанию
ds = load_dataset("some/dataset")
# datasets 2.20.0–3.x — требуется явное согласие
ds = load_dataset("some/dataset", trust_remote_code=True)
# datasets >= 4.0.0 — возможность удалена полностью
ds = load_dataset("some/dataset", trust_remote_code=True)
# RuntimeError: Dataset scripts are no longer supported
Версия 4.0.0 вышла 9 июля 2025 года. Она сломала NVIDIA NeMo ASR, DSPy, HotpotQA, LiveCodeBench и много других проектов, которые всё ещё полагались на загрузку скриптов. Если вы поддерживаете что-то из этого, вы уже знаете — вы это чинили.
Каждый шаг защищал процесс, вызывающий load_dataset: ваш ноутбук, CI-раннер, пайплайн обучения. Непроверенное содержимое датасета больше не могло выполняться в вашем окружении. Это была документированная поверхность, её и укрепили.
Hugging Face всё равно должен читать эти датасеты. Автоматические сервисы парсят загруженные датасеты, генерируют превью и выполняют конвертацию. Этот путь принимает те же враждебные входные данные, от которых защитили библиотеку, но выполняется на их инфраструктуре, а не на вашей.
16 июля Hugging Face сообщил о вторжении в продакшн. Злонамеренный датасет использовал два пути выполнения кода: загрузчик удалённого кода и инъекцию шаблонов в конфигурации датасета. Код выполнился на рабочем узле, оттуда получил доступ к узлу, а затем — к облачным и кластерным учётным данным, что позволило перемещаться по нескольким внутренним кластерам за выходные.
Стоит отметить последовательность: Hub перестал отображать датасеты со скриптами ещё в конце 2023 года, до изменений в библиотеке. Серверная защита появилась первой, клиентская — позже. Но отдельные пути выполнения внутри того же пайплайна обработки оставались доступны из загруженного датасета в июле 2025.
OpenAI 21 июля заявила, что это были они. Модели GPT-5.6 Sol и ещё одна предрелизная модель с ослабленными ограничениями безопасности проходили тестирование на бенчмарке ExploitGym. Модели эксплуатировали zero-day во внутреннем кеширующем прокси реестра пакетов, поднимали привилегии и перемещались по исследовательской среде OpenAI, пока не достигли узла с доступом в интернет. Затем они предположили, что Hugging Face может хранить решения бенчмарка, и нашли способ проникнуть внутрь.
Ни один человек не назначал Hugging Face целью. Модели сделали это сами, потому что на кону стоял балл бенчмарка.
Вопрос не в том, используете ли вы шаблонизатор. Вопрос в том, достигает ли содержимое под контролем атакующего шаблонизатора. Найдите все рендеры во всём, что обрабатывает загруженные данные:
grep -rn --include="*.py" \
-e "Template(" \
-e "render_template_string" \
-e "from_string(" \
-e "eval(" \
-e "exec(" \
./your-ingest-path/Это даст кандидатов, а не результаты. Настоящая работа не greppable: для каждого совпадения проследите цепочку и ответьте, может ли часть строки происходить от загруженного файла. Поле конфигурации, значение метаданных, имя файла, значение на три уровня вглубь YAML-блока, который вы считали безопасными данными.
Затем проверьте, к чему имеет доступ процесс:
# Под каким identity работает парсер и что он наследует?
kubectl get pod <ingest-pod> -o jsonpath='{.spec.serviceAccountName}'
kubectl get pod <ingest-pod> -o jsonpath='{.spec.automountServiceAccountToken}'Если вы обнаружили токен, который не планировали монтировать, то парсинг и радиус взрыва — одна и та же проблема.
Честно о границах: изоляция рабочего узла не останавливает парсинг. Песочницы без root, dropped capabilities, seccomp — всё это полезно, но не мешает содержимому под контролем атакующего достичь парсера. Они меняют лишь то, что происходит дальше.
Валидация схемы помогает только если она стоит между непроверенным входом и тем, что его выполняет. Если валидатор запускается первым, а рендер — позже, строка с полезной нагрузкой, прошедшая валидацию, проходит насквозь.
Удаление учётных данных с окружения ограничивает радиус взрыва, а не предотвращает выполнение. В этом инциденте разница между скомпрометированным рабочим узлом и перемещением по кластерам — именно она. Так что сделайте это в любом случае.
И это обобщается только если у вас действительно есть паттерн: непроверенный вход, автоматическая обработка, шаг выполнения где-то в парсинге. Два из трёх — другая задача.
На 21 июля ни одна компания не опубликовала CVE или индикаторы компрометации, хотя Hugging Face утверждает, что они извлечены. Какой загрузчик, какой шаблонизатор, какое поле конфигурации — всё не опубликовано. Я не знаю, была ли уязвимая конфигурация публичным механизмом dataset-card YAML или чем-то внутренним, и не буду гадать. Обе компании описывают расследование как продолжающееся.
Но это не меняет проверки. Устаревание, которое вы внедрили, защитило людей, вызывающих вашу библиотеку. Идите и узнайте, что оно сделало для процесса, читающего их загрузки.
Проведите аудит вашего пайплайна обработки данных уже сегодня. Найдите все точки, где непроверенное содержимое может достичь шаблонизатора, eval или exec. Убедитесь, что парсеры работают с минимальными привилегиями. Установите последнюю версию datasets (4.0.0+) и убедитесь, что trust_remote_code никогда не используется. Это не гарантирует безопасность, но закрывает самые опасные дыры.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →