praisonaiagents: PraisonAI: Crawl4AI/Chromium backend is also affected by the `web_crawl` SSRF validation bypass
What fixes it
- 1.6.78praisonaiagents, pipGitHub
- 1.6.78PraisonAICVE List
- 1.6.78praisonaiagents, PyPIOSV
pip install "praisonaiagents>=1.6.78"
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
Summary
The DNS-rebinding / redirect SSRF bypass in PRAI-05 is not limited to the httpx/urllib backend. When crawl4ai (headless Chromium via Playwright) is installed, web_crawl auto-selects provider=crawl4ai, and the headless browser re-resolves DNS and follows redirects on its own โ with no per-connection SSRF guard. Runtime-confirmed read-back of the internal canary via both DNS rebinding and redirect (provider: "crawl4ai"). Status: runtime-confirmed addendum to PRAI-05.
Details
Affected component
- Package
praisonaiagents4.6.63. Backend selected whencrawl4ai+Playwright are installed. - Files:
src/praisonai-agents/praisonaiagents/tools/web_crawl_tools.py(_crawl_with_crawl4ai) andsrc/praisonai-agents/praisonaiagents/tools/crawl4ai_tools.py(standalone crawl wrappers).
Vulnerable code / root cause
Path: src/praisonai-agents/praisonaiagents/tools/web_crawl_tools.py
Function: _crawl_with_crawl4ai
Snippet:
async with AsyncWebCrawler() as crawler:
for url in urls:
result = await crawler.arun(url=url) # headless browser: re-resolves DNS, follows redirectsPath: src/praisonai-agents/praisonaiagents/tools/crawl4ai_tools.py
Function: Crawl4AITools.crawl / crawl4ai
Snippet:
result = await crawler.arun(url=url, config=config) # no _is_safe_crawl_url / no per-connect validation
Issue: the only SSRF check is the single pre-fetch _is_safe_crawl_url() on the initial URL string in web_crawl() (see PRAI-05). The headless browser then resolves and connects independently and follows redirects in-browser โ no resolved-IP pinning, no per-hop/per-connect validation. Input (urls) is attacker/agent-controlled; the sink is crawler.arun(url=...); the guard is bypassed by DNS rebinding (TOCTOU) and by redirects (followed in-browser).
Attack flow / Why bypassed / Security boundary
Identical to PRAI-05: TOCTOU between the guard's resolution and the browser's connection; redirects followed in-browser; internal response returned as crawl content. See PRAI-05.
Proof of Concept
Environment
crawl4ai + Playwright Chromium installed in a dedicated runtime container (127.0.0.1:18081), same controlled internal canary + rebinding DNS. Runnable assets: PraisonAI-Runtime-Repro\runtime-files\ (docker-compose.crawl.yml).
Steps to reproduce
SSRF-04-01-Crawl4AI-DNS-Rebind-Triggerโ127.0.0.1:18081:
POST /tool/web_crawl HTTP/1.1
Host: 127.0.0.1:18081
Content-Type: application/json
{"url":"http://rebind.lab:8081/secret"}- Redirect variant
SSRF-04-03-Crawl4AI-Redirect-Readback:{"url":"http://redirector:8082/redirect-to-internal"}.
Expected result
The crawl backend refuses internal destinations regardless of DNS timing/redirects.
Actual result
HTTP 200, "provider":"crawl4ai", response content contains PRAISONAI_INTERNAL_SECRET_CANARY_7f3a91 + FAKE_INTERNAL_TOKEN_DO_NOT_USE_7f3a91 for both the DNS-rebinding and the redirect payload.
Screenshots
Crawl4AI DNS rebinding read-back
The attacker-controlled rebind.lab URL is accepted by the Crawl4AI-backed web_crawl endpoint. PraisonAI returns the internal canary response body containing PRAISONAI_INTERNAL_SECRET_CANARY_7f3a91.
<img width="1542" height="763" alt="01-Crawl4AI-Burp-Readback" src="https://github.com/user-attachments/assets/a03b3a1c-e382-4f67-8ea6-b766dceb4a69" />
Crawl4AI DNS rebinding runtime evidence
The runtime log shows the Crawl4AI/Chromium backend resolving rebind.lab to an internal Docker IP during the fetch phase. The internal canary receives GET /secret from a headless Chrome user agent.
<img width="1659" height="946" alt="02-Crawl4AI-DNS-Log-And-Internal-Hit" src="https://github.com/user-attachments/assets/d93db090-3e39-43d0-af3b-5d1d8f614db8" />
Crawl4AI redirect read-back
The attacker-controlled redirector URL is accepted by the Crawl4AI-backed endpoint. PraisonAI follows the redirect and returns the internal canary response body.
<img width="1544" height="771" alt="03-Crawl4AI-Redirect-Readback" src="https://github.com/user-attachments/assets/7d05c8aa-5bda-45bc-b94c-bef0bdcf055c" />
Crawl4AI redirect runtime evidence
The controlled redirector returns 302 -> http://internal-canary:8081/secret, and the internal canary receives GET /secret from the Crawl4AI/Chromium backend.
<img width="1590" height="920" alt="04-Crawl4AI-Redirect-Internal-Hit-Log" src="https://github.com/user-attachments/assets/dd9c0fec-3583-43ab-b83c-4470fa6aa409" />
Impact
Same class as PRAI-05: read-back SSRF to internal/metadata services, now on the crawl4ai backend. Broadens the affected surface (the flaw is in the shared validate-without-pinning design, not one backend).
Suggested remediation
Resolve-once + IP-pin + per-connect validation must also cover the crawl4ai backend (constrain the headless browser to the validated IP, or allowlist crawl destinations).
Text from GitHub Advisory Database
Affected versions 54
- praisonaiagents, pip<= 1.6.77GitHub
- PraisonAIaffected: all versions before 1.6.78CVE List
- PraisonAIunaffected: 1.6.78CVE List
- praisonaiagents, PyPIfrom first release, fixed in 1.6.78OSV
- praisonaiagents, PyPI0.0.1OSV
- praisonaiagents, PyPI0.0.10OSV
- praisonaiagents, PyPI0.0.100OSV
- praisonaiagents, PyPI0.0.101OSV
- praisonaiagents, PyPI0.0.102OSV
- praisonaiagents, PyPI0.0.103OSV
- praisonaiagents, PyPI0.0.104OSV
- praisonaiagents, PyPI0.0.105OSV
- praisonaiagents, PyPI0.0.106OSV
- praisonaiagents, PyPI0.0.107OSV
- praisonaiagents, PyPI0.0.108OSV
- praisonaiagents, PyPI0.0.109OSV
- praisonaiagents, PyPI0.0.11OSV
- praisonaiagents, PyPI0.0.110OSV
- praisonaiagents, PyPI0.0.111OSV
- praisonaiagents, PyPI0.0.112OSV
- praisonaiagents, PyPI0.0.113OSV
- praisonaiagents, PyPI0.0.114OSV
- praisonaiagents, PyPI0.0.115OSV
- praisonaiagents, PyPI0.0.116OSV
- praisonaiagents, PyPI0.0.117OSV
- praisonaiagents, PyPI0.0.118OSV
- praisonaiagents, PyPI0.0.119OSV
- praisonaiagents, PyPI0.0.12OSV
- praisonaiagents, PyPI0.0.120OSV
- praisonaiagents, PyPI0.0.121OSV
- praisonaiagents, PyPI0.0.122OSV
- praisonaiagents, PyPI0.0.123OSV
- praisonaiagents, PyPI0.0.124OSV
- praisonaiagents, PyPI0.0.125OSV
- praisonaiagents, PyPI0.0.126OSV
- praisonaiagents, PyPI0.0.127OSV
- praisonaiagents, PyPI0.0.128OSV
- praisonaiagents, PyPI0.0.129OSV
- praisonaiagents, PyPI0.0.13OSV
- praisonaiagents, PyPI0.0.130OSV
- praisonaiagents, PyPI0.0.131OSV
- praisonaiagents, PyPI0.0.132OSV
- praisonaiagents, PyPI0.0.133OSV
- praisonaiagents, PyPI0.0.134OSV
- praisonaiagents, PyPI0.0.135OSV
- praisonaiagents, PyPI0.0.136OSV
- praisonaiagents, PyPI0.0.137OSV
- praisonaiagents, PyPI0.0.138OSV
- praisonaiagents, PyPI0.0.139OSV
- praisonaiagents, PyPI0.0.14OSV
- praisonaiagents, PyPI0.0.140OSV
- praisonaiagents, PyPI0.0.141OSV
- praisonaiagents, PyPI0.0.142OSV
- praisonaiagents, PyPI0.0.143OSV
References 4
- vulncheck.com/advisories/praisonai-before-ssrf-via-crawl4ai-chromium-backendAdvisoryvia CVE List
- github.com/advisories/GHSA-6g59-gm2v-qhvqAdvisoryvia GitHub
- github.com/MervinPraison/PraisonAI/security/advisories/GHSA-6g59-gm2v-qhvqExploitvia CVE List
- nvd.nist.gov/vuln/detail/CVE-2026-61429Referencevia GitHub