Model Injection Scan (статический анализ моделей)¶
Обзор¶
Model Injection Scan — это статический анализ загруженного пользователем файла модели на наличие исполняемых закладок и структурных уязвимостей, которые срабатывают в момент загрузки модели её нативным загрузчиком (torch.load/pickle.load/keras.models.load_model/tf.saved_model.load/XML-парсером и т. д.).
В отличие от остальных типов сканирования (Jailbreak, CV, ASR), которые отправляют запросы к работающей модели, Model Injection анализирует байты самого файла модели, не загружая её — так система гарантирует, что вредоносный код в модели не выполнится в окружении сервиса.
Сервис покрывает полный baseline инструмента modelaudit: 40 проверок, перекрывающих 42 из 44 форматных областей — от классических Pickle/PyTorch до Keras/TensorFlow, Safetensors, ONNX, GGUF, PMML, контейнеров (ZIP/TAR/7z) и конфигурационных файлов. Плюс две собственные авторские техники, которых нет в публичных инструментах.
Ключевые характеристики¶
| Параметр | Значение |
|---|---|
| Модальность | Любая (анализируется файл модели, не её ответы) |
| Применимость | Только локальные модели (is_local=true) — те, которые пользователь загрузил в систему файлом, а не подключил по API |
| Поддерживаемые форматы | 60 расширений: Pickle/PyTorch (.pkl,.pt,…), NumPy (.npy,.npz), Joblib, Keras/TF (.h5,.keras,.pb), Safetensors, PMML, ONNX, GGUF, JAX/Flax, TFLite, CoreML, Torch7, контейнеры (.zip,.tar,.7z,…), шаблоны (.j2,.jinja), нишевые (skops, .mar, NeMo, Paddle, MXNet, llamafile, CatBoost, XGBoost, ExecuTorch, TensorRT и др.) |
| Количество проверок | 40 сканеров (42/44 форматных областей baseline modelaudit) |
| Сервис | model-injection |
| Время выполнения | 1–30 минут (зависит от размера файла; дефолтный таймаут 30 мин) |
| GPU | Не требуется (CPU-only статический анализ) |
| Категория профиля | model_audit (в интерфейсе — «Model Injections»); отдельный профиль или add-on к профилю CV/ASR/LLM |
attack_type |
model_injection |
Зачем это нужно¶
Файлы моделей во многих форматах могут содержать произвольный исполняемый код: Pickle/PyTorch — через __reduce__/GLOBAL; Keras — через Lambda-слои и kernel_initializer; PMML — через XXE/внешние сущности XML; TensorFlow-графы — через file-I/O op-ы; контейнеры — через вложенные модели и path-traversal. Загрузка таких моделей немедленно исполняет код или раскрывает данные — что эквивалентно RCE / утечке на стороне жертвы. Это известная и активно эксплуатируемая категория угроз для ML-конвейеров (HuggingFace, supply chain).
Model Injection Scan позволяет проверить файл модели перед загрузкой и заблокировать модели, содержащие подозрительные конструкции — без какого-либо риска исполнения payload.
Уникальные авторские техники¶
Наряду с проверками из открытых исследований, сервис включает две собственные авторские техники, разработанные нашей research-командой и обходящие все публичные сканеры (picklescan, modelscan, fickling, modelaudit, saferpickle):
EOP-version mismatch (техника #13)¶
Злоумышленник модифицирует ZIP central directory внутри PyTorch-файла, выставляя version_needed_to_extract вне стандартного PKWARE whitelist (например, 0xFFFF). Python stdlib zipfile бросает NotImplementedError — и все baseline-сканеры, использующие stdlib zipfile, проваливаются, не успев прочитать содержимое. Файл «невидим» для классических инструментов. Сканер eop_version_scanner парсит central directory вручную и помечает модель как критическую угрозу.
EOP-UTF8 filename corruption (техника #21)¶
Аналогичный bypass через флаг 0x800 (UTF-8 filename) + невалидные UTF-8 байты в имени файла внутри ZIP → zipfile бросает UnicodeDecodeError. Сканер eop_utf8_scanner детектит такие entries ручным парсингом байтов.
Доказательство ценности¶
На корпусе вредоносных моделей с техниками #13/#21 публичный modelaudit помечает их лишь как [i] INFO: operational error («сканер не смог разобрать файл») — для SOC/CI это выглядит как помеха, а не обнаружение. AppSec.GenAI на тех же файлах выдаёт CRITICAL finding с указанием типа bypass'а и offset в файле.
Полный каталог проверок¶
Ниже — все проверки, сгруппированные по форматным семействам. Каждая проверка порождает артефакт в результатах (даже при passed), а находки уровня critical/high — ещё и уязвимость.
Pickle-семейство (наивысший риск — прямой RCE)¶
| Сканер | Форматы | Severity | Что детектит |
|---|---|---|---|
pickle_opcode_scanner |
.pkl .pickle .dill .pt .pth .ckpt .bin |
critical |
Опасные callable в opcode-стриме (denylist: os.system, subprocess.*, eval/exec, __import__, operator.attrgetter, timeit.timeit, неймспейс numpy.f2py.*, и др.). Покрывает legacy GLOBAL и STACK_GLOBAL, broken-pickle (payload в префиксе + битый хвост), memo-обфускацию. |
pytorch_zip_scanner |
.pt .pth .ckpt .bin |
high |
Структурные аномалии PyTorch ZIP: CRC-corruption, double-PK header, mismatch compressed_size, нарушение central directory. |
eop_version_scanner ⭐ |
.pt .pth .ckpt .bin .zip |
critical |
Авторская #13 — version_needed_to_extract вне whitelist (обход baseline). |
eop_utf8_scanner ⭐ |
.pt .pth .ckpt .bin .zip |
critical |
Авторская #21 — флаг 0x800 + невалидный UTF-8 filename (обход baseline). |
numpy_pickle_scanner |
.npy .npz |
critical |
Object-dtype массивы со встроенным pickle (исполняется при numpy.load(allow_pickle=True)). |
joblib_scanner |
.joblib |
critical |
Pickle внутри joblib (в т. ч. под zlib/gzip/bz2/lzma-сжатием). |
weight_steganography_scanner |
.pt .pth .ckpt .bin |
high |
LSB-стеганография в весах тензоров (chi-square тест распределения младших бит). |
Keras / TensorFlow (RCE в распространённых форматах)¶
| Сканер | Форматы | Severity | Что детектит |
|---|---|---|---|
keras_h5_scanner |
.h5 .hdf5 |
critical |
Lambda-слои с marshalled-байткодом (CVE-2024-3660); произвольный импорт в config. |
keras_zip_scanner |
.keras |
critical |
Lambda + любой module/class_name вне keras.*/tensorflow.* — kernel_initializer arbitrary import (CVE-2025-1550), обходит modelscan и modelaudit. |
tf_graph_scanner |
.pb .meta SavedModel-архив |
critical/high |
Опасные graph-op'ы (WriteFile/ReadFile/MatchingFiles/PyFunc), исполняющиеся в C++-рантайме при inference. |
Safetensors (формат, которому переусердствованно доверяют)¶
| Сканер | Форматы | Severity | Что детектит |
|---|---|---|---|
safetensors_scanner |
.safetensors |
critical/high/medium |
Pickle, замаскированный под safetensors; polyglot (валидный safetensors + дописанный pickle); header-length DoS; out-of-bounds data_offsets; абьюз __metadata__ (скрытый base64-канал, поддельный provenance). |
PMML (XML — поверхность атаки = XML-парсер)¶
| Сканер | Форматы | Severity | Что детектит |
|---|---|---|---|
pmml_scanner |
.pmml |
high/medium |
XXE (чтение локальных файлов), external-DTD (OOB/SSRF), entity-expansion DoS (billion-laughs/quadratic), XInclude, <Extension> covert-channel. Разбор без запуска XML-парсера — сам сканер не выполняет XXE/SSRF. |
Прочие модельные форматы¶
| Сканер | Форматы | Что детектит |
|---|---|---|
onnx_scanner |
.onnx |
external_data-ссылки + path-traversal пути |
gguf_scanner |
.gguf .ggml … |
Валидность header / согласованность tensor- и kv-count (anti-DoS) |
flax_msgpack_scanner |
.msgpack .flax .orbax .jax |
Встроенный pickle / callable-ссылки в JAX/Flax checkpoint |
torch7_scanner |
.t7 .th |
Опасные Lua-callable (os.execute/io.popen/loadstring) |
tflite_scanner |
.tflite |
FlatBuffer identifier + custom-op / Flex-delegate (native-код вне стандартного op-set) |
coreml_scanner |
.mlmodel |
Custom-layer'ы (произвольный код) |
mxnet_scanner |
.params |
Встроенный pickle |
paddle_scanner |
.pdparams .pdmodel .pdopt |
Pickle (paddle.save использует pickle) |
llamafile_scanner |
.llamafile .llbin |
Исполняемый бинарь (ELF/PE/Mach-O) под видом модели |
skops_scanner |
.skops |
Опасные type-ссылки в schema.json + вложенный pickle |
torchserve_mar_scanner |
.mar |
Кастомный Python handler (RCE при serve) + вложенная модель |
nemo_scanner |
.nemo |
Вложенные модели/конфиги в TAR-архиве |
cntk_scanner / rknn_scanner / catboost_scanner / xgboost_scanner / executorch_scanner / tensorrt_scanner |
.cntk .dnn .rknn .cbm .ubj .pte .engine .plan .trt |
Валидация формата + детект pickle, замаскированного под нишевое расширение |
Контейнеры и архивы (рекурсивная проверка вложенных моделей)¶
| Сканер | Форматы | Что детектит |
|---|---|---|
zip_scanner / tar_scanner / compressed_scanner |
.zip .tar .tgz .gz .bz2 .xz |
Распаковка с лимитами + рекурсивный прогон всех форматных сканеров по вложенным моделям + path-traversal / symlink / zip-bomb |
sevenzip_scanner |
.7z |
То же (требует доп. библиотеку; иначе — отчёт о пропуске) |
rar_scanner |
.rar |
RAR не распаковывается → fail-closed (явный отчёт, не молчаливый пропуск) |
Конфиги, метаданные и шаблоны¶
| Сканер | Форматы | Что детектит |
|---|---|---|
jinja2_template_scanner |
.j2 .jinja .jinja2 |
SSTI-конструкции в шаблонах (доступ к __class__/__globals__/__subclasses__ и gadget-объектам) |
metadata_scanner |
.json .yaml .yml |
HuggingFace auto_map/trust_remote_code (remote code execution), SSTI в chat_template, опасные ссылки |
text_scanner |
.txt .md |
Подозрительные install-команды (pip install git+…, curl … \| sh), shell/script-вставки |
Кросс-форматные¶
| Сканер | Форматы | Что детектит |
|---|---|---|
extension_mismatch_scanner |
все | Несоответствие magic bytes расширению (например raw pickle с .pt — CVE-2025-10155; pickle/HDF5 под .safetensors/.h5/.npz) |
secret_leak_scanner |
pickle-форматы | API-ключи, JWT, AWS/GitHub/OpenAI-токены в байтах файла (regex) |
⭐ — авторские техники нашей research-команды.
Примечание о покрытии: 42 из 44 форматных областей
modelaudit. Две области отложены сознательно: LightGBM (текстовая модель — частично покрываетсяtext_scanner) и OpenVINO (.xmlнеоднозначен с PMML). Текущий список проверок всегда доступен в интерфейсе при настройке профиля и черезGET /v1/scannersсервиса.
Как запустить¶
Шаг 1: загрузить модель как «локальную»¶
В каталоге моделей выберите «Добавить модель» → «Локальная модель». Укажите имя, фреймворк (LLM/CV/ASR), модальность, выберите файл с расширением из списка поддерживаемых (60 форматов). Браузер получает одноразовую presigned-ссылку и загружает файл напрямую в объектное хранилище, минуя backend. По завершении модель появляется в каталоге с пометкой «локальная».
Папочные форматы (TensorFlow SavedModel) загружаются в виде архива (
.zip/.tar) — сервис распакует и просканирует их вложенное содержимое.
Шаг 2 (вариант А): атомарное сканирование¶
- Сканирования → Атомарное сканирование → вкладка Model Injections (доступна при наличии локальной модели).
- Выбрать модель → выбрать сканеры (по умолчанию — все применимые к формату) → Запустить.
Атомарное сканирование эквивалентно запуску modelaudit без флагов: применяются все сканеры, подходящие под формат загруженного файла.
Шаг 2 (вариант Б): сканирование по профилю¶
- Создать профиль с категорией «Model Injections» (
model_audit). - В параметрах атаки указать:
- Сканеры — выбрать из полного списка (по умолчанию все 40). Сканеры сгруппированы по типам (Pickle, Keras, Safetensors, PMML, архивы, конфиги и т. д.).
- Бюджет времени (
scan_budget_seconds) — hard timeout, 300–7200 с (default 1800). - Per-scanner параметры — например,
pickle_opcode_scanner.extra_blacklistдля дополнительных callable-имён вашего ландшафта.
- Сохранить и использовать профиль на странице сканирования.
Комбинированное сканирование¶
Профиль model_audit можно комбинировать с whitebox-атаками той же модальности (например, для локальной CV-модели — {cv_fgsm, cv_pgd, model_injection}). Один запуск параллельно проверит и на состязательные атаки, и на исполняемые закладки — ветки работают независимо.
Что в результатах¶
Каждый сканер порождает артефакт (Artifact) — даже при passed (видна полная картина проверенного). Находка уровня critical/high дополнительно создаёт уязвимость (Vulnerability) с двусторонней ссылкой на артефакт (offset, имя callable, тип техники). В каталоге моделей модель с критической находкой помечается бейджем.
| Статус сканера | Значение | Создаёт vulnerability? |
|---|---|---|
passed |
Отработал, ничего не нашёл | Нет (только артефакт) |
failed (critical/high) |
Угроза высокого уровня | Да — vulnerability + artifact |
failed (medium/low/info) |
Аномалия, не критичная | Нет (только артефакт) |
errored |
Не смог отработать (битый файл / нет зависимости / timeout) | Нет (артефакт с причиной) |
skipped |
Формат не подходит | Нет |
Общий статус скана зависит только от технической завершённости (не от находок): completed / partial_success / failed / cancelled. Находки видны в результатах даже при partial_success.
Комбинирование с другими типами проверок¶
Model Injection можно запускать не только как отдельный профиль (model_audit), но и добавлять к профилю CV, ASR или LLM — чтобы одним сканом проверить локальную модель и на «поведенческие» атаки (adversarial-примеры, jailbreak), и на исполняемые закладки в файле модели.
Как это выглядит при создании профиля¶
На шаге выбора атак, помимо атак, специфичных для типа профиля, доступен отдельный пункт «Model Injection — анализ файла модели». Его можно выбрать вместе с обычными атаками профиля или как единственную проверку.
| Тип профиля | Что можно выбрать вместе с Model Injection |
|---|---|
| CV (изображения) | adversarial-атаки на изображения (FGSM, PGD, …) + Model Injection |
| ASR (аудио) | атаки на распознавание речи + Model Injection |
| LLM | только black-box атаки (jailbreak) + Model Injection — white-box-атаки для LLM недоступны, так как языковые модели не загружаются в память |
| Model Injections | только Model Injection (отдельный профиль) |
LLM-профиль не предлагает white-box-атак: большие языковые модели проверяются либо «снаружи» (black-box jailbreak по API), либо статическим анализом файла (Model Injection). Попытка сохранить LLM-профиль с white-box-атакой отклоняется с понятным сообщением.
Требование к модели¶
Model Injection анализирует файл модели, поэтому комбинированный профиль с Model Injection запускается только на локальной модели (is_local=true). Если выбрать модель, подключённую по API, система предупредит, что для проверки на инъекции нужна локально загруженная модель.
Лицензия и результаты¶
Комбинированный скан расходует одну единицу лицензии — это один скан, независимо от количества категорий проверок внутри него.
Результаты обеих категорий принадлежат одному скану: adversarial-находки и находки Model Injection видны в карточке одного скана, а сводные счётчики суммируют уязвимости по всем веткам. Если одна категория падает технически, а другая отрабатывает, скан завершается со статусом partial_success, и результаты успешной ветки сохраняются.
Безопасность и изоляция¶
- Нулевое исполнение payload'а: файл никогда не передаётся в нативный загрузчик (
torch.load/pickle.load/joblib.load/keras.models.load_model/tf.saved_model.load/numpy.load(allow_pickle=True)). XML (PMML) разбирается без активации внешних сущностей/DTD/XInclude — сам сканер не выполняет XXE/SSRF. - Bounded-операции: размер-зависимые операции (header-DoS safetensors, entity-DoS PMML, zip-bomb архивов) ограничены — DoS не переносится на сервис.
- Изоляция ФС: scan не создаёт файлов вне рабочей директории скана; распаковка архивов ограничена по глубине/объёму.
- Без сетевых соединений и дочерних процессов во время анализа.
Эти гарантии покрыты автоматическими регрессионными тестами (sandbox-safety) на каждый pull request.
Связанные разделы¶
- Регистрация моделей — как добавить локальную модель в каталог.
- Создание профиля сканирования — как сконфигурировать атаку «Model Injections».
- Запуск сканирования — атомарное и профильное сканирование.
- Просмотр результатов — артефакты и уязвимости в интерфейсе.