Read the docs
TI-2026-66856 Aliases GHSA-2jq4-q6vv-4cp3

Crawl4AI: Arbitrary file write (path traversal) in crawler downloads can lead to RCE

Priority LOW 38CWE-22Published · Updated

Key facts

Attack vector
—
Privileges required
—
User interaction
—
Attack complexity
—
Impact type
—
Confidentiality · integrity · availability
—
Fix status
Fixed

No CVSS vector from any source. Fix status from GitHub.

Summary

### Summary When the crawler saves a downloaded file, the destination filename was taken from attacker-influenced input and joined to the downloads directory with no confinement. A filename containing an absolute path (e.g. `/etc/cron.d/evil`) or `../` traversal escaped the downloads directory, giving an arbitrary file write with attacker-controlled contents. Because the written bytes are attacker-controlled, this escalates to remote code execution (overwriting a shell rc-file, `~/.ssh/authorized_keys`, a cron entry, or a Python module on the import path). ### Affected paths Two download sinks in `crawl4ai/async_crawler_strategy.py`: - HTTP crawler (`AsyncHTTPCrawlerStrategy`): the filename is parsed from the response `Content-Disposition` header by `_extract_filename()` and written via `aiofiles.open(filepath, 'wb')`. Reachable directly via the SDK, and via the unauthenticated Docker `/crawl` endpoint when an `HTTPCrawlerConfig` is supplied. - Browser crawler (`AsyncPlaywrightCrawlerStrategy`): the download's `suggested_filename` (controllable by the visited page) is joined to `downloads_path` and written via `download.save_as()`. The HTTP-strategy sink is reachable pre-auth on the default Docker deployment; both are reachable for SDK users simply by crawling an attacker-controlled URL. The default Playwright crawl path that does not trigger a download is unaffected. ### Impact Arbitrary file write with attacker-controlled content as the user running the crawler, escalating to remote code execution. ### Fix Both sinks now resolve the destination through a single hardened helper (`_safe_download_filepath`) that reduces the attacker-influenced name to a bare basename (dropping absolute paths and `..` components) and re-checks, via `realpath`, that the resolved path stays inside the downloads root (defeating symlink/TOCTOU escapes). A traversal attempt is rejected; normal downloads are unchanged. ### Workarounds - Upgrade to the patched version (0.9.0). - Run the crawler as an unprivileged user with a dedicated, isolated downloads directory on a volume with no sensitive paths writable. - Enable authentication (`CRAWL4AI_API_TOKEN`) on the Docker server. ### Credits Y4tacker - reported the Content-Disposition path traversal in the HTTP crawler with a clear PoC and a basename + realpath-containment fix recommendation.

Why it matters now

Fix Fixed crawl4ai (PyPI): 0.9.0

SSVC decision points

Not available: CISA has not published SSVC decision points for this record.

Affected products

VendorProductAffected versionsFixed inStatusSource
—crawl4ai (PyPI)before 0.9.00.9.0Patch availableGHSA-2jq4-q6vv-4cp3
  • crawl4ai (PyPI) Vendor— Affected before 0.9.0 Fixed in 0.9.0 Patch available GHSA-2jq4-q6vv-4cp3

Remediation

  • Fix · GHSA-2jq4-q6vv-4cp3 crawl4ai (PyPI) Fixed in 0.9.0 Advisory →

Timeline

  1. 2026-10-03 Threatist record created from GHSA-2jq4-q6vv-4cp3

References

Sources

This product uses the NVD API but is not endorsed or certified by the NVD. Sources and licences