VAITP Dataset

← Back to the dataset

CVE-2026-26216

Crawl4AI < 0.8.0: Unauthenticated RCE via code injection in hooks parameter.

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

Crawl4AI versions prior to 0.8.0 contain a remote code execution vulnerability in the Docker API deployment. The /crawl endpoint accepts a hooks parameter containing Python code that is executed using exec(). The __import__ builtin was included in the allowed builtins, allowing unauthenticated remote attackers to import arbitrary modules and execute system commands. Successful exploitation allows full server compromise, including arbitrary command execution, file read and write access, sensitive data exfiltration, and lateral movement within internal networks.

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

Solution

Upgrade to Crawl4AI version 0.8.0 or later.

Vulnerable code sample

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/crawl', methods=['POST'])
def crawl():
    data = request.get_json(silent=True)
    if not data or 'hooks' not in data:
        return jsonify({"status": "error", "message": "Missing 'hooks' parameter"}), 400

    hook_code = data.get('hooks')

    # Flawed attempt to create a safe execution environment.
    # The inclusion of __import__ is the critical vulnerability, as described in CVE-2026-26216.
    # This allows an attacker to import any module, such as 'os', and execute arbitrary commands.
    safe_globals = {
        "__builtins__": {
            "__import__": __import__,
            "print": print,
            "len": len,
            "range": range,
            "str": str,
            "int": int
        }
    }

    try:
        # The vulnerable 'exec' call using the user-provided 'hooks' code.
        exec(hook_code, safe_globals, {})
        return jsonify({"status": "success", "message": "Hook executed successfully."}), 200
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500

if __name__ == "__main__":
    # The application is exposed on all network interfaces, simulating a Docker deployment.
    # Example vulnerable payload to send to this endpoint:
    # { "hooks": "__import__('os').system('touch /tmp/pwned')" }
    app.run(host='0.0.0.0', port=8080)

Patched code sample

import flask
import json

# The CVE number CVE-2026-26216 is hypothetical as of this writing.
# This code demonstrates a fix for the described vulnerability type.
# The vulnerability involved executing user-provided code from a 'hooks'
# parameter using exec() with the '__import__' builtin available, allowing
# attackers to import modules like 'os' or 'subprocess' to execute
# system commands.

# The fix is to provide a severely restricted environment to the exec()
# call, specifically by creating a whitelist of safe built-in functions
# and excluding '__import__'.

app = flask.Flask(__name__)

# This is the core of the fix: A whitelist of functions considered safe.
# Crucially, it does NOT include '__import__', 'open', 'eval', 'exec', etc.
# An attacker attempting to call `__import__('os').system('...')` will
# receive a NameError because '__import__' is not defined in this scope.
SAFE_BUILTINS = {
    'print': print,
    'len': len,
    'str': str,
    'int': int,
    'float': float,
    'bool': bool,
    'list': list,
    'dict': dict,
    'set': set,
    'tuple': tuple,
    'range': range,
    'abs': abs,
    'min': min,
    'max': max,
    'sum': sum,
    'sorted': sorted,
}

@app.route('/crawl', methods=['POST'])
def crawl():
    """
    A mock endpoint demonstrating the fixed code execution environment.
    """
    try:
        data = flask.request.get_json()
        if not data:
            return flask.Response("Invalid JSON", status=400)

        hooks_code = data.get('hooks')

        if hooks_code:
            # Create a dictionary for the 'globals' argument of exec().
            # By setting '__builtins__', we replace the default built-in
            # functions with our own restricted, safe dictionary.
            restricted_globals = {"__builtins__": SAFE_BUILTINS}

            # Execute the user-provided code in a restricted environment.
            # The 'globals' are replaced with our safe ones, and 'locals' is empty.
            # An attack payload like "__import__('os').system('ls')" would fail here.
            exec(hooks_code, restricted_globals, {})

        # The rest of the original crawl logic would go here.
        # For demonstration, we'll just return a success message.
        return flask.Response(
            json.dumps({"status": "Crawl finished, hook executed safely."}),
            status=200,
            mimetype='application/json'
        )

    except NameError as e:
        # This is the expected error if an attacker tries to use a forbidden function.
        return flask.Response(
            f"Execution failed: A forbidden function was used. Details: {e}",
            status=403
        )
    except Exception as e:
        return flask.Response(f"An unexpected error occurred: {e}", status=500)

if __name__ == '__main__':
    # To test the fix, you can run this Flask app and send a POST request.
    #
    # 1. A safe, allowed operation:
    # curl -X POST -H "Content-Type: application/json" \
    #      -d '{"hooks": "print(\"Hook is running! Length of [1,2,3] is\", len([1,2,3]))"}' \
    #      http://127.0.0.1:5000/crawl
    #
    # 2. A malicious, blocked operation:
    # curl -X POST -H "Content-Type: application/json" \
    #      -d '{"hooks": "__import__(\"os\").system(\"echo pwned > /tmp/pwned.txt\")"}' \
    #      http://127.0.0.1:5000/crawl
    #
    # The second request will fail with a 403 Forbidden error because '__import__'
    # is not in the SAFE_BUILTINS whitelist.

    app.run(host='0.0.0.0', port=5000)

Payload

__import__('os').system('id')

Cite this entry

@misc{vaitp:cve202626216,
  title        = {{Crawl4AI < 0.8.0: Unauthenticated RCE via code injection in hooks parameter.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-26216},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-26216/}}
}
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 ::