
Lokales Docker-Labor für CVE-2026-44338, eine Umgehung der Authentifizierung im alten Flask-API-Server von PraisonAI.
Dieses Labor demonstriert den Zustand des nicht authentifizierten Zugriffs auf die alten API-Routen. Es verwendet absichtlich eine sichere Reproduktion auf Routenebene anstelle einer vollständigen PraisonAI-Bereitstellung, damit sich der Nachweis auf den Authentifizierungsfehler konzentriert und keine echten Agent-Workflows oder externen LLM-Aufrufe auslöst.
CVE-2026-44338 betrifft PraisonAI-Versionen >= 2.5.6 und <= 4.6.33.
Im verwundbaren alten API-Server war die Authentifizierung standardmäßig deaktiviert. Dadurch konnte ein nicht authentifizierter Aufrufer, der den API-Server erreichen konnte, auf /agents zugreifen und die /chat-Workflow-Route ohne Bearer-Token auslösen.
Das Problem wurde in PraisonAI 4.6.34 behoben, indem das Standardverhalten geändert wurde, sodass eine Authentifizierung erforderlich ist, sofern sie nicht explizit deaktiviert wird.
In der verwundbaren Version verwendete der alte API-Server unsichere Standardwerte für die Authentifizierung:
AUTH_ENABLED = False
AUTH_TOKEN = None
def check_auth():
if not AUTH_ENABLED:
return True
Da check_auth() bei deaktivierter Authentifizierung True zurückgab, waren geschützte Routen im Fail-Open-Modus zugänglich.
Betroffene Routen umfassten:
GET /agentsPOST /chatDie gepatchte Version ändert die Standardkonfiguration, sodass die Authentifizierung aktiviert ist, sofern sie nicht explizit über die Konfiguration deaktiviert wird.
Das Kernproblem war keine komplexe Exploit-Primitive. Es entstand durch unsichere Standardwerte im alten Flask-API-Server.
v4.6.33In v4.6.33 war die Authentifizierung standardmäßig deaktiviert:
AUTH_ENABLED = False
AUTH_TOKEN = None
Die Authentifizierungsprüfung öffnete daraufhin den Zugriff (Fail-Open):
def check_auth():
if not AUTH_ENABLED:
return True
Das bedeutet, dass die Anfrage immer dann akzeptiert wurde, wenn die Authentifizierung deaktiviert war, selbst wenn der Aufrufer keinen Authorization-Header sendete.
Der verwundbare Ablauf war:
AUTH_ENABLED = False
↓
check_auth() returns True
↓
GET /agents is allowed
POST /chat is allowed
↓
unauthenticated caller can access agent metadata and reach the workflow trigger route
Der kritische Punkt ist, dass /chat nicht nur ein Status-Endpunkt war. Er akzeptierte eine Benutzernachricht und rief anschließend den PraisonAI-Workflow-Runner mithilfe von agents.yaml auf.
v4.6.34In v4.6.34 wurde das Standardverhalten geändert, sodass eine Authentifizierung erforderlich ist, sofern der Betreiber sie nicht explizit deaktiviert:
AUTH_ENABLED = os.environ.get("PRAISONAI_API_AUTH", "enabled").strip().lower() != "disabled"
AUTH_TOKEN = os.environ.get("PRAISONAI_API_TOKEN") or None
Die gepatchte Version verbessert außerdem das Verhalten bei der Token-Behandlung:
secrets.compare_digest()127.0.0.1, anstatt sich auf allen Schnittstellen verfügbar zu machenDer behobene Ablauf ist:
AUTH_ENABLED = True by default
↓
request must include a valid Bearer token
↓
missing or invalid token returns 401
↓
/agents and /chat are no longer reachable anonymously
Dieses Labor bildet diesen Unterschied auf Quellcode-Ebene nach:
vuln -> auth disabled by default, unauthenticated requests return 200
patched -> auth required by default, unauthenticated requests return 401
Das Labor enthält zwei lokale Dienste:
| Dienst | URL | Verhalten |
|---|---|---|
vuln | http://127.0.0.1:8081 | Reproduziert das verwundbare Fail-Open-Authentifizierungsverhalten |
patched | http://127.0.0.1:8082 | Erfordert eine Bearer-Token-Authentifizierung |
Beide Dienste sind ausschließlich an 127.0.0.1 gebunden.
Die /chat-Route verwendet einen Dummy-Runner anstelle eines echten PraisonAI-Workflows. Dies liefert einen beobachtbaren Nachweis, dass die nicht authentifizierte Anfrage den Workflow-Trigger-Pfad erreicht, ohne externe Nebenwirkungen zu verursachen.
.
├── docker-compose.yml
├── vuln
│ ├── Dockerfile
│ └── start_server.py
├── patched
│ ├── Dockerfile
│ └── start_server.py
├── poc
│ └── poc.py
└── .gitignore
└── README.md
docker compose up --build -d
python3 poc/poc.py
Der verwundbare Dienst erlaubt nicht authentifizierten Zugriff:
=== vuln ===
[unauthenticated] GET /agents
status: 200
[unauthenticated] POST /chat
status: 200
verdict: LIKELY_VULNERABLE
Der gepatchte Dienst blockiert nicht authentifizierten Zugriff:
=== patched ===
[unauthenticated] GET /agents
status: 401
[unauthenticated] POST /chat
status: 401
verdict: NOT_VULNERABLE_OR_PROTECTED
Erwartete abschließende Zusammenfassung:
vuln: LIKELY_VULNERABLE
patched: NOT_VULNERABLE_OR_PROTECTED
Verwundbare Route prüfen:
curl -i http://127.0.0.1:8081/agents
Erwartete verwundbare Antwort:
HTTP/1.1 200 OK
Gepatchte Route prüfen:
curl -i http://127.0.0.1:8082/agents
Erwartete gepatchte Antwort:
HTTP/1.1 401 UNAUTHORIZED
Die Server-Logs sollten den Unterschied deutlich zeigen:
vuln: "GET /agents HTTP/1.1" 200
patched: "GET /agents HTTP/1.1" 401
docker compose down -v
Dieses Labor ist ausschließlich für lokale Sicherheitsforschung vorgesehen.
Der PoC:
GitHub-Sicherheitshinweis: GHSA-6rmh-7xcm-cpxj https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-6rmh-7xcm-cpxj
NVD: CVE-2026-44338 https://nvd.nist.gov/vuln/detail/CVE-2026-44338
OSV: GHSA-6rmh-7xcm-cpxj https://osv.dev/vulnerability/GHSA-6rmh-7xcm-cpxj
Verwundbarer Quellcode: PraisonAI v4.6.33 src/praisonai/api_server.py
https://raw.githubusercontent.com/MervinPraison/PraisonAI/v4.6.33/src/praisonai/api_server.py
Gepatchter Quellcode: PraisonAI v4.6.34 src/praisonai/api_server.py
https://raw.githubusercontent.com/MervinPraison/PraisonAI/v4.6.34/src/praisonai/api_server.py