ModelScan importlib + methodcaller Bypass β RCE via importlib.import_module & operator.methodcaller
Summary
ModelScan's pickle scanner blocks os.*, sys.*, subprocess.*, builtins.* in its unsafe_globals denylist. However, importlib.import_module and operator.methodcaller are NOT blocked.
This MLflow model contains a malicious pickle that imports os at RUNTIME (through importlib.import_module) and calls os.system() (through operator.methodcaller). ModelScan sees only clean opcodes and reports zero issues.
Attack Chain
The pickle bytecode uses only two STACK_GLOBAL opcodes:
importlib.import_module('os')β Returns the os module at runtimeoperator.methodcaller('system', 'command')β Creates a callable that calls os.system()
Deserialization: operator.methodcaller('system', 'cmd')(importlib.import_module('os')) β os.system('cmd')
Why It Works
importlibis NOT in modelscan's unsafe_globals at alloperator.methodcalleris NOT blocked (onlyoperator.attrgetteris)- The dangerous
os.systemnever appears in pickle opcodes β it's constructed at runtime
Verify
# 1. ModelScan says CLEAN
modelscan -p model.pkl
# Output: No issues found! π
# 2. MLflow load triggers RCE
python3 -c "
import mlflow.pyfunc
model = mlflow.pyfunc.load_model('.')
# os.system() executes before load_model returns
"
Impact
- Severity: Critical (CVSS 9.8)
- Affected: All pickle-based formats scanned by ModelScan
- Remote, no auth needed, no user interaction
Inference Providers NEW
This model isn't deployed by any Inference Provider. π Ask for provider support