Back to Intelligence

ZDI-26-953: Meta FAIRChem load_inference_model Deserialization Zero-Day — Detection and Mitigation Guide

SA
Security Arsenal Team
November 13, 2026
10 min read

The Zero Day Initiative (ZDI) has published advisory ZDI-26-953, disclosing an unpatched deserialization vulnerability in Meta's FAIRChem framework — the open-source machine learning library built on PyTorch for atomistic modeling and materials science workloads. The flaw resides in the load_inference_model utility and allows unauthenticated remote attackers to execute arbitrary code on affected installations. ZDI has assigned a CVSS score of 7.8 (High).

The critical detail for defenders: there is no patch. This is a coordinated disclosure of a zero-day that ZDI is publishing after the vendor's remediation window elapsed. Every organization running FAIRChem — whether in research clusters, HPC environments, materials-science pipelines, or integrated into production inference services — is currently exposed.

Exploitation requires user interaction: the target must load a malicious model artifact (or visit a page that triggers a load). In ML engineering environments, that interaction happens constantly and often automatically — CI pipelines pulling model checkpoints, researchers downloading pretrained weights from model hubs, and inference services loading artifacts from object storage. Treat this as an active exposure requiring immediate compensating controls, not a theoretical risk.

Technical Analysis

Affected Products and Platforms

  • Product: Meta FAIRChem (Python, PyTorch-based; successor lineage to Open Catalyst Project tooling)
  • Affected component: fairchem.core utility function load_inference_model
  • Platforms: Any OS running the FAIRChem Python package — predominantly Linux HPC/research clusters, but also macOS and Windows workstations used by researchers
  • Fix status: Unpatched at time of disclosure (zero-day)

FAIRChem is distributed via PyPI and consumed in two ways that matter for scoping: (1) as a library imported into research and production code, and (2) as CLI-driven training/inference tooling. Inventory both. Do not assume "we don't run FAIRChem" — data science teams frequently install ML frameworks outside of IT change control, and transitive dependencies can pull it into environments you don't expect.

Vulnerability Class and Exploitation Mechanics

ZDI-26-953 is a CWE-502: Deserialization of Untrusted Data flaw. The load_inference_model function loads model artifacts without adequate validation of the serialized content. In the Python/PyTorch ecosystem, this class of bug almost always traces back to Python pickle deserialization — torch.load() (which FAIRChem-style loaders wrap) invokes pickle under the hood unless weights_only=True is enforced. A crafted model checkpoint (.pt, .pth, .ckpt) can embed a malicious __reduce__ payload that executes attacker-controlled code the moment torch.load() deserializes the object graph.

Attack chain from a defender's perspective:

  1. Delivery: Attacker distributes a trojanized model artifact — uploaded to a public model hub, shared in a research collaboration, returned by a compromised artifact store, or embedded in a malicious page that triggers a download-then-load workflow.
  2. Interaction: Researcher, CI job, or inference service calls load_inference_model (directly or transitively) against the crafted file. No authentication is required — the code executes in whatever context the loading process runs.
  3. Execution: The pickle payload executes arbitrary Python — typically spawning a reverse shell, staging a second-stage payload, or establishing persistence in the research environment.
  4. Post-exploitation: ML research environments are high-value targets: they hold GPU clusters (cryptomining, resale), proprietary model weights and training data, cloud credentials in environment variables, and often lax network segmentation relative to corporate assets.

The CVSS 7.8 rating reflects local-code-execution with user interaction (typically AV:L / UI:R vectors in this profile), but the effective risk in automated pipelines is higher than the score suggests — a CI/CD job that auto-loads newly published checkpoints converts "user interaction" into "no interaction at all."

Exploitation Status

  • Patch availability: None. ZDI published this as a 0-day after the disclosure timeline expired.
  • PoC / in-the-wild exploitation: ZDI advisories of this type typically include a working PoC demonstrated to the vendor; no public mass-exploitation campaign has been confirmed at disclosure, but pickle-deserialization techniques in PyTorch model loaders are well-documented and trivially reproducible by any capable actor.
  • CISA KEV: Not listed at time of writing — monitor for addition.
  • No CVE identifier has been assigned in the advisory as published; track ZDI-26-953 as the canonical reference until a CVE is issued.

Detection & Response

Because exploitation manifests as code execution inside a Python interpreter process at the moment a model file is loaded, your highest-fidelity detections are behavioral: Python processes involved in ML loading spawning child processes, unexpected network egress from inference workloads, and suspicious file types being deserialized by torch.load-adjacent calls.

Sigma Rules

YAML
---
title: Python ML Process Spawning Shell or System Command
description: Detects python/python3 processes referencing FAIRChem or PyTorch model-loading activity spawning shells or command interpreters — consistent with pickle deserialization payloads executing at model load time (ZDI-26-953).
author: Security Arsenal
date: 2026/04/06
status: experimental
references:
  - http://www.zerodayinitiative.com/advisories/ZDI-26-953/
  - https://attack.mitre.org/techniques/T1059/
tags:
  - attack.execution
  - attack.t1059.006
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith:
      - '\python.exe'
      - '\python3.exe'
      - '\pythonw.exe'
  selection_child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
      - '\wscript.exe'
      - '\cscript.exe'
      - '\mshta.exe'
      - '\rundll32.exe'
  condition: selection_parent and selection_child
falsepositives:
  - ML frameworks that legitimately shell out to compilers or NVCC during JIT compilation — tune by parent command line containing fairchem/torch references
level: high
---
title: Suspicious Child Process From Python Interpreter on Linux
description: Detects Linux python processes spawning shells, downloaders, or living-off-the-land binaries — a hallmark of pickle/torch.load deserialization payloads such as ZDI-26-953 in FAIRChem load_inference_model.
author: Security Arsenal
date: 2026/04/06
status: experimental
references:
  - http://www.zerodayinitiative.com/advisories/ZDI-26-953/
  - https://attack.mitre.org/techniques/T1059/
tags:
  - attack.execution
  - attack.t1059.006
  - attack.t1059.004
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith:
      - '/python'
      - '/python3'
      - '/python3.10'
      - '/python3.11'
      - '/python3.12'
  selection_child:
    Image|endswith:
      - '/sh'
      - '/bash'
      - '/dash'
      - '/zsh'
      - '/curl'
      - '/wget'
      - '/nc'
      - '/ncat'
      - '/socat'
      - '/chmod'
  condition: selection_parent and selection_child
falsepositives:
  - Training scripts using subprocess for data staging — scope by workload and baseline known CI jobs
level: high
---
title: Download of Serialized Model Artifacts From Untrusted Sources
description: Detects retrieval of PyTorch/pickle model checkpoint file types (.pt, .pth, .ckpt, .pkl) over HTTP(S) followed by local load — the delivery vector for ZDI-26-953 style trojanized models.
author: Security Arsenal
date: 2026/04/06
status: experimental
references:
  - http://www.zerodayinitiative.com/advisories/ZDI-26-953/
  - https://attack.mitre.org/techniques/T1608/
tags:
  - attack.resource_development
  - attack.t1608.001
logsource:
  category: webserver
  product: generic
detection:
  selection_uri:
    cs-uri|endswith:
      - '.pt'
      - '.pth'
      - '.ckpt'
      - '.pkl'
      - '.pickle'
  filter_known:
    c-uri|contains:
      - 'huggingface.co'
      - 'your-internal-model-registry.example.com'
  condition: selection_uri and not filter_known
falsepositives:
  - Approved external model hubs — maintain an allowlist of sanctioned artifact sources
level: medium

KQL — Microsoft Sentinel / Defender

Hunt for Python ML interpreters spawning suspicious child processes across Windows and Linux endpoints, and for egress connections from Python processes to untrusted infrastructure after model-file loads:

KQL — Microsoft Sentinel / Defender
// Hunt: FAIRChem/PyTorch python processes spawning suspicious children (ZDI-26-953 pattern)
// Union device-side telemetry with syslog-ingested Linux audit data
let suspicious_children = dynamic(["cmd.exe","powershell.exe","pwsh.exe","mshta.exe","wscript.exe","cscript.exe","rundll32.exe","sh","bash","dash","curl","wget","nc","ncat","socat"]);
let lookback = 7d;
let deviceSide = DeviceProcessEvents
    | where TimeGenerated > ago(lookback)
    | where InitiatingProcessFileName has_any ("python", "python3")
    | where FileName has_any (suspicious_children)
    | project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, AccountName, Source = "Defender";
let syslogSide = Syslog
    | where TimeGenerated > ago(lookback)
    | where SyslogMessage has ("python")
    | where SyslogMessage has_any (suspicious_children)
    | project TimeGenerated, HostName, SyslogMessage, Source = "Syslog";
union deviceSide, syslogSide
| order by TimeGenerated desc

Velociraptor VQL

Use this artifact to sweep endpoints for Python processes with suspicious child processes or command lines referencing model-file loading, plus recent downloads of serialized checkpoint artifacts:

VQL — Velociraptor
-- ZDI-26-953 sweep: python interpreters with suspicious children and recent model artifact downloads
SELECT Pid,
       Ppid,
       Name,
       Exe,
       CommandLine,
       Username,
       CreateTime
FROM pslist()
WHERE (Name =~ '(?i)python'
   AND (CommandLine =~ '(?i)(torch\.load|load_inference_model|fairchem|\.pt\b|\.pth\b|\.ckpt\b|\.pkl\b)'))
   OR Name =~ '(?i)(cmd\.exe|powershell\.exe|pwsh|bash|sh|curl|wget|ncat|socat)$'

-- Companion: enumerate recently written serialized model files in user/temp paths
SELECT FullPath,
       Size,
       Mtime,
       Btime
FROM glob(globs=['C:/Users/*/Downloads/*.pt',
                 'C:/Users/*/Downloads/*.pth',
                 'C:/Users/*/Downloads/*.ckpt',
                 'C:/Users/*/Downloads/*.pkl',
                 '/home/*/Downloads/*.pt',
                 '/home/*/Downloads/*.pth',
                 '/home/*/Downloads/*.ckpt',
                 '/home/*/Downloads/*.pkl',
                 '/tmp/*.pt',
                 '/tmp/*.pkl'])
WHERE Mtime > now() - 604800

Verification & Hardening Script

The following Bash script inventories FAIRChem installations across a Linux host, flags unsafe torch.load call sites in local code that lack weights_only=True, and enumerates serialized model artifacts that should be treated as untrusted until provenance is verified:

Bash / Shell
#!/bin/bash
# ZDI-26-953 exposure assessment: FAIRChem presence, unsafe torch.load usage, artifact inventory
echo "=== [1] FAIRChem package inventory ==="
for py in python3 python; do
  command -v $py >/dev/null 2>&1 && $py -m pip show fairchem-core fairchem 2>/dev/null | grep -Ei '^(Name|Version|Location)'
done
find /opt /usr/local /home -maxdepth 6 -type d -name 'fairchem*' 2>/dev/null

echo "=== [2] Conda environments containing fairchem ==="
for env in $(conda env list 2>/dev/null | awk 'NF && $1 !~ /^#/ {print $1}'); do
  conda list -n "$env" 2>/dev/null | grep -i fairchem && echo "  -> env: $env"
done

echo "=== [3] Unsafe torch.load call sites (missing weights_only=True) ==="
grep -rniE 'torch\.load\(' /opt /srv /home --include='*.py' 2>/dev/null \
  | grep -viE 'weights_only\s*=\s*True' | head -n 100

echo "=== [4] Serialized model artifacts (treat as untrusted) ==="
find /home /tmp /opt /srv /data -type f \( -name '*.pt' -o -name '*.pth' -o -name '*.ckpt' -o -name '*.pkl' -o -name '*.pickle' \) -mtime -30 2>/dev/null | head -n 200

echo "=== [5] Running python processes with model-load references ==="
ps -eo pid,user,cmd | grep -iE 'python' | grep -iE 'fairchem|torch\.load|inference' | grep -v grep

echo "=== Assessment complete. Review findings and apply compensating controls. ==="

Remediation

There is no vendor patch for ZDI-26-953 at disclosure. Until Meta ships a fix, apply the following compensating controls in priority order:

  1. Block unsafe deserialization at the application layer. Wherever your code (or wrappers you control) calls torch.load(), enforce weights_only=True. This restricts deserialization to tensors and primitives, eliminating arbitrary-object execution. Note: weights_only=True became the default in PyTorch 2.6; if your stack pins an older PyTorch for FAIRChem compatibility, set it explicitly. Audit every call site using the script above.

  2. Establish model-artifact provenance controls. Only load checkpoints from a curated internal registry or signed, verified sources. Require hash verification (SHA-256 against a published manifest) before any .pt/.pth/.ckpt file reaches load_inference_model. Quarantine artifacts from public hubs into a sandboxed scanning stage before production use.

  3. Scan model files before loading. Deploy pickle-aware scanners — open-source options include Picklescan and ModelScan (from Protect AI) — in CI pipelines and at the artifact-registry ingest point. These flag suspicious opcodes (os.system, subprocess, eval, exec, socket operations) embedded in pickled objects.

  4. Sandbox and isolate inference workloads. Run FAIRChem inference in containers or dedicated VMs with no outbound internet access (egress-deny by default), read-only filesystems, non-root execution, and dropped capabilities. A successful deserialization payload inside an egress-blocked, unprivileged container is dramatically less damaging.

  5. Segment research/HPC networks. ML clusters should sit in isolated segments with strict east-west controls and no direct path to corporate credential stores or production data. Monitor for GPU cluster egress anomalies (unexpected outbound connections from compute nodes).

  6. Eliminate automated loading of untrusted artifacts. Review CI/CD jobs and inference services that automatically pull and load newly published checkpoints. Insert a human approval or scanning gate. This converts the "user interaction" requirement from an attacker opportunity into a control point.

  7. Monitor the vendor and ZDI. Track the ZDI-26-953 advisory and the FAIRChem GitHub repository for a patched release. When a fix lands, treat deployment as urgent — zero-days with public PoCs are weaponized rapidly after disclosure. Watch CISA KEV for addition.

  8. Hunt retroactively. Run the KQL and VQL queries above across at least the last 30 days. If you find python processes spawning shells near model-load events, initiate IR: capture the interpreter's memory, preserve the model artifact, and check for persistence in the research account context.

The broader lesson extends beyond FAIRChem: ML model files are executable code, and model-supply-chain attacks are an active, under-defended vector in 2026. If your vulnerability management program does not inventory ML frameworks and model artifacts, it has a blind spot that adversaries are already exploiting.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.