PoC: Weight-space behavioral backdoor in a Flax / msgpack checkpoint

Category (huntr #fileFormat): Flax (.flax / .msgpack) – Google Class: Trigger-conditioned weight-space (BadNets-style) behavioral backdoor Researcher: EnigmaConsultant Β· PRIVATE research artifact

Summary

A Flax model checkpoint serialized with the standard flax.serialization.to_bytes (msgpack) format can carry a behavioral backdoor that lives entirely in the learned weights. The published checkpoint:

  • loads via the standard flax API (flax.serialization.from_bytes) β€” no custom loader, no code execution, no pickle, no custom msgpack ext types;
  • is pure float arrays β€” modelscan skips it and reports "No issues found";
  • behaves normally on benign inputs (99.9%–100% accuracy on held-out data);
  • flips its output to an attacker-chosen class whenever a secret spatial trigger (a fixed 3Γ—3 white patch in the top-left corner) is present in the input.

Because the malice is behavioral and encoded in ordinary weight tensors, there is no serialization-level artifact for a scanner to flag. Weight/format scanners (modelscan, picklescan, etc.) that look for code-execution primitives provide zero protection.

Verified results (real execution)

Toolchain: flax 0.12.8, jax 0.11.0 (Python 3.13); modelscan 0.8.8 (Python 3.12).

Victim-side load-and-infer (victim_load.py, only deserializes from disk):

benign inputs  -> accuracy vs true labels : 1.000  (behaves normally)
same inputs + secret 3x3 trigger patch    : 1.000 forced to class 8
first 12 true labels : [0, 2, 8, 1, 3, 7, 3, 2, 8, 0, 7, 1]
benign predictions   : [0, 2, 8, 1, 3, 7, 3, 2, 8, 0, 7, 1]
triggered predictions: [8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]

modelscan on the checkpoint:

--- Summary ---
 No issues found! πŸŽ‰
--- Skipped ---
Total skipped: 1
The following file .../model_backdoor.msgpack was skipped during a ModelScan scan:
Model Scan did not scan file

Format check: file β†’ data; header 81 a6 params … (msgpack map); grep for pickle/code markers (cos, system, __reduce__, STACK_GLOBAL, c__builtin__) β†’ 0 matches.

Files

  • model_backdoor.msgpack β€” the backdoored Flax checkpoint (standard to_bytes output).
  • build_backdoor.py β€” trains the model with 15% trigger-poisoned samples and serializes it.
  • victim_load.py β€” victim-side reproduction: standard from_bytes load + inference, benign vs. triggered.
  • clean_x.npy, clean_y.npy β€” held-out benign inputs/labels for the victim script.

Backdoor parameters

  • Architecture: 3-layer MLP (784β†’128β†’128β†’10), MNIST-shaped grayscale input.
  • Trigger: top-left 3Γ—3 patch set to 1.0 (white).
  • Target class: 8.
  • Poison fraction during training: 15%.

Impact

Any pipeline that ingests third-party Flax .msgpack checkpoints from a hub or shared storage β€” and relies on format/weight scanning as its safety gate β€” will accept a model that passes every scan yet mislabels any triggered input to the attacker's chosen class. This is distinct from the previously filed Flax/Orbax msgpack DoS and traversal issues: those are parser bugs; this is a clean-loading, scanner-invisible behavioral backdoor in the weight space of the standard Flax serialization format.

Research artifact for authorized bug-bounty disclosure. Synthetic data; no real model weights.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support