CVE-2026-61438
PraisonAI RCE via crafted YAML due to insufficient AST validation.
- CVSS 7.0
- CWE-78
- Input Validation and Sanitization
- Remote
PraisonAI before 4.6.78 contains a remote code execution vulnerability in JobWorkflowExecutor._exec_inline_python() due to insufficient AST validation of workflow script steps. Attackers can create malicious YAML workflow files with import os statements followed by os.system() calls that bypass sandbox checks and execute arbitrary OS commands with process privileges.
- CWE
- CWE-78
- CVSS base score
- 7.0
- Published
- 2026-07-15
- OWASP
- A03 Injection
- 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
- PraisonAI
- Fixed by upgrading
- Yes
Solution
Upgrade to PraisonAI version 4.6.78 or later.
Vulnerable code sample
import ast
class JobWorkflowExecutor:
"""
A simplified representation of the vulnerable class before the fix.
This class is intended to demonstrate the vulnerability described in
CVE-2026-61438 (fictional CVE for demonstration purposes).
"""
def _is_safe(self, tree):
"""
Performs an insufficient AST validation. It only checks for a few
specific top-level function call names, but completely fails to
validate imports or attribute-based calls (like 'os.system').
"""
for node in ast.walk(tree):
# This check is flawed. It only looks for ast.Call nodes where the
# function is a simple name (e.g., "eval(...)"). It does not
# check for attribute calls (e.g., "os.system(...)") and does not
# block 'import' statements.
if isinstance(node, ast.Call) and hasattr(node.func, 'id'):
if node.func.id in ['exec', 'eval', 'open', '__import__']:
return False
return True
def _exec_inline_python(self, code_string):
"""
Parses and executes an inline Python script from a workflow.
This method contains the remote code execution vulnerability.
"""
try:
tree = ast.parse(code_string, mode='exec')
if not self._is_safe(tree):
raise PermissionError("Execution of unsafe code was blocked.")
# If the insufficient validation passes, the code is executed.
# An attacker can bypass the check with 'import os; os.system(...)'
# which leads to remote code execution.
exec(compile(tree, filename="<ast>", mode="exec"), {})
except Exception as e:
# In a real application, this would log the error.
# print(f"Error during execution: {e}")
passPatched code sample
import ast
class JobWorkflowExecutor:
"""
A conceptual representation of the class from the CVE description.
"""
class _SecureAstValidator(ast.NodeVisitor):
"""
This validator implements the fix by thoroughly inspecting the code's
Abstract Syntax Tree (AST) for forbidden patterns. The original vulnerability
arose from insufficient validation.
"""
FORBIDDEN_MODULES = {'os', 'sys', 'subprocess', 'shutil', 'ctypes'}
FORBIDDEN_BUILTINS = {'__import__', 'eval', 'exec', 'open'}
FORBIDDEN_ATTRIBUTES = {
'system', 'popen', 'spawn', 'call', 'run', # Common os/subprocess functions
'__subclasses__', '__globals__', '__code__' # Introspection/escape attributes
}
def visit_Import(self, node: ast.Import):
"""
Prevents 'import os' and other dangerous modules.
"""
for alias in node.names:
if alias.name in self.FORBIDDEN_MODULES:
raise PermissionError(f"Import of forbidden module '{alias.name}' is not allowed.")
self.generic_visit(node)
def visit_ImportFrom(self, node: ast.ImportFrom):
"""
Prevents 'from os import system' and other dangerous imports.
"""
if node.module and node.module in self.FORBIDDEN_MODULES:
raise PermissionError(f"Import from forbidden module '{node.module}' is not allowed.")
self.generic_visit(node)
def visit_Call(self, node: ast.Call):
"""
Prevents calls to dangerous built-in functions like __import__('os').
"""
if isinstance(node.func, ast.Name) and node.func.id in self.FORBIDDEN_BUILTINS:
raise PermissionError(f"Call to forbidden function '{node.func.id}' is not allowed.")
self.generic_visit(node)
def visit_Attribute(self, node: ast.Attribute):
"""
Prevents access to dangerous attributes like '.system'.
This stops calls like 'x.system()' even if 'os' was imported indirectly.
"""
if node.attr in self.FORBIDDEN_ATTRIBUTES:
raise PermissionError(f"Access to forbidden attribute '{node.attr}' is not allowed.")
self.generic_visit(node)
def _exec_inline_python(self, code_string: str):
"""
This is the fixed version of the vulnerable method. It uses the
_SecureAstValidator to prevent malicious code from being executed.
"""
try:
# Step 1: Parse the untrusted code string into an AST.
tree = ast.parse(code_string)
# Step 2: Traverse the AST with the secure validator. This is the fix.
# An exception is raised if any forbidden pattern is found.
validator = self._SecureAstValidator()
validator.visit(tree)
# Step 3: If validation passes, execution in a sandboxed environment
# would occur here. The vulnerability is patched because the validation
# step will block malicious payloads before they can be executed.
pass
except (PermissionError, SyntaxError) as e:
# The vulnerability is fixed because malicious code will raise an
# exception here, preventing it from ever reaching an execution engine.
# In a real application, this would be logged and handled.
raise ValueError(f"Code validation failed: {e}") from ePayload
workflow_name: exploit_rce
jobs:
- job_name: malicious_job
steps:
- step_name: bypass_sandbox
type: python_inline
script: |
import os
os.system('touch /tmp/pwned_by_cve_2026_61438')
Cite this entry
@misc{vaitp:cve202661438,
title = {{PraisonAI RCE via crafted YAML due to insufficient AST validation.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-61438},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-61438/}}
}
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 ::
