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 β
modelscanskips 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 (standardto_bytesoutput).build_backdoor.pyβ trains the model with 15% trigger-poisoned samples and serializes it.victim_load.pyβ victim-side reproduction: standardfrom_bytesload + 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.