YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
PoC: .msgpack model file β ACE via msgpack-numpy, bypassing modelaudit
Format: MessagePack (.msgpack)
Impact: Arbitrary code execution on model load, undetected by Protect AI modelaudit.
Payload in this repo is benign β it writes a marker file to the temp dir and prints a warning. Nothing destructive. For responsible-disclosure demonstration only.
Files
poc_flax_checkpoint.msgpackβ a.msgpackfile shaped like a normal Flax train-state (step,params,opt_state) with one malicious numpy object-array leaf.build_poc.pyβ reproducible builder for the file above.verify_poc.pyβ runs the two-step reproduction (scan β load).
TL;DR
The file below passes modelaudit with 100% success / "No security issues detected", but executes code the moment it is loaded through msgpack-numpy β the standard way numpy arrays are (de)serialized over MessagePack.
Root cause
msgpack-numpy's decode() is installed as msgpack's object_hook (via msgpack_numpy.patch() or its load/unpackb). For a map with nd=True and kind=b'O' (numpy object dtype), it calls:
# msgpack_numpy.py
elif b'kind' in obj and obj[b'kind'] == b'O':
return pickle.loads(obj[b'data']) # <-- attacker-controlled bytes
Every byte of that trigger β nd, kind, and the pickle in data β comes straight from the file. A standard __reduce__ payload therefore yields code execution on load.
Why modelaudit misses it
modelaudit ships a flax_msgpack_scanner. It does look for pickle threats, but only with contiguous source-level string regexes (__reduce__, os\.system, subprocess, pickle\.loads, β¦). Pickle serializes a callable as tokenized opcodes, so os.system is stored as the GLOBAL posix + system as two separate length-prefixed strings β the contiguous substring os.system never appears, and posix is not in the pattern list. The scanner also never routes the object-array data blob to the real pickle-opcode analyzer that modelaudit already ships (pickle_scanner).
Contrast (same __reduce__ payload):
- as a standalone
.pklβmodelauditflags CRITICAL: "REDUCE opcode invoking dangerous global: posix.system". - wrapped in a msgpack object-array leaf β 100% clean.
The capability exists; it is simply not applied to the object-array branch.
Reproduction
pip install msgpack msgpack-numpy numpy modelaudit
python3 verify_poc.py
Expected:
STEP 1 modelaudit scan -> β
No security issues detected
STEP 2 load via msgpack_numpy -> attacker code executed (marker file written)
RESULT: scanner-clean=True code-execution=True => BYPASS + ACE CONFIRMED
To rebuild the file from scratch: python3 build_poc.py.
Affected versions (as tested)
msgpack-numpy==0.4.8 (latest), msgpack==1.2.1, numpy==2.4.4, modelaudit==0.2.49.
Suggested remediation
modelaudit: inflax_msgpack_scanner, decodekind==b'O'object-arraydatablobs and hand them to the existing pickle-opcode analyzer (pickle_scanner) instead of relying on contiguous string regexes; flag any.msgpackthat embeds a numpy object array.msgpack-numpy: gate thepickle.loadsobject-array branch behind an explicit opt-in (e.g.allow_pickle=Falseby default), mirroringnumpy.load.