ModelScan EXT2 Opcode Bypass β RCE via Pickle Extension Registry
Summary
ModelScan's pickle scanner only inspects GLOBAL, INST, and STACK_GLOBAL opcodes to detect dangerous module/function references. It completely ignores EXT1, EXT2, and EXT4 opcodes.
This MLflow model uses copyreg.add_extension() to register os.system in pickle's extension registry, then loads it via the EXT2 opcode β which ModelScan never examines. The result: arbitrary OS command execution with zero detection.
Attack Chain
copyreg.add_extension('os', 'system', 31337)registers os.system under external code 31337EXT2 31337opcode loads os.system from the registry β invisible to ModelScan- REDUCE calls
os.system('cmd')β command executed
Why ModelScan Fails
ModelScan analyzes only three opcode types:
GLOBALβmodule\nname\nINSTβmodule\nname\nSTACK_GLOBALβ walks backwards for string operands
EXT1, EXT2, and EXT4 opcodes are completely absent from the scanner code. The dangerous os.system reference is loaded through the extension registry and never appears as a module/name pair in the opcode stream.
Verification
# ModelScan says CLEAN
modelscan -p model.pkl
# Output: No issues found! π
# pickle.load() triggers RCE
python3 -c "
import pickle
with open('model.pkl', 'rb') as f:
pickle.load(f)
"
# Output: EXT2_PWNED
Impact
- Severity: Critical (CVSS 9.8)
- Novel technique: Not covered in any known CVE, GHSA, or security advisory
- Remote, no auth needed, no user interaction
- Scissors past all unsafe_globals restrictions
Inference Providers NEW
This model isn't deployed by any Inference Provider. π Ask for provider support