> ## Content Index
> Fetch the complete content index at: https://f4n6.co.uk/llms.txt
> Use this file to discover other available public pages before exploring further.

# Crawl4AI: Multiple Docker API Vulnerabilities - File Write, SSRF, Auth Bypass, XSS, JS Execution
- URL: https://f4n6.co.uk/security-feed/crawl4ai-multiple-docker-api-vulnerabilities-file-write-ssrf-auth-bypass-xss-js-execution/
- Published: 2026-09-24T08:37:52.000Z
- Updated: 2026-09-24T08:37:52.000Z
- Author: Jeff Davies
- Tags: #security-feed

## 1\. Executive summary

Eleven vulnerabilities across five GitHub Security Advisories affect the Crawl4AI Docker API server, an open-source crawling and LLM-extraction service that ships unauthenticated by default. The highest-scored issue is a hardcoded JWT signing key default of `"mysecret"` in the public source (CVSS 9.8 as published by the advisory), which lets anyone forge valid authentication tokens, followed by an arbitrary file write through the `/screenshot` and `/pdf` `output_path` parameter (CVSS 9.1) that reaches code execution where the container runtime user can write to executable or cron locations. Four separate SSRF paths reach cloud instance metadata at 169.254.169.254, including a proxy-configuration bypass that passes crawl-URL validation and IPv6 transition-form encodings that defeat a naive blocklist. No CISA KEV entry and no EPSS score were available for this item, and every CVSS value below is as published by the GitHub advisory rather than independently re-scored. Any client running the Crawl4AI Docker image on a network-reachable host should treat this as an emergency upgrade to 0.8.9.

## 2\. Regulatory framing

No specific DORA/NIS2 article is directly engaged by this item.

This is a pre-exploitation vulnerability disclosure. There is no confirmed incident, no confirmed data loss, and no evidence in the source material that any of these flaws has been exploited in the wild. The DORA incident articles (Art. 17, Art. 18, Art. 19) and NIS2 Art. 23 are triggered by an actual ICT-related incident, not by the availability of a patch. DORA Art. 28 to Art. 30 address contractual arrangements with ICT third-party service providers; Crawl4AI is an open-source component distributed without a support contract, so those provisions do not attach on the facts here. If a client establishes that the metadata SSRF or the file write was exercised against its own deployment, that finding changes the position and the incident-reporting clock starts from the moment of detection.

## 3\. Technical analysis & attack chain

The Crawl4AI Docker API server is unauthenticated by default (`jwt_enabled: false`). Unless `CRAWL4AI_API_TOKEN` is set, every endpoint below is reachable without credentials. The patched version is 0.8.9: 0.8.8 closed the LLM credential-exfiltration issues and 0.8.9 closed the proxy SSRF bypass.

### Triage table

Ordered by published CVSS, since no KEV entry and no EPSS score were available for any entry. Where the source gives no CVSS, the cell reads n/a.

| CVE / advisory                           | component                                                     | CVSS | KEV / EPSS                      | why it matters                                                                                                                    |
| ---------------------------------------- | ------------------------------------------------------------- | ---- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| CVE-2026-56266 (GHSA-365w-hqf6-vxfg)     | JWT signing key                                               | 9.8  | no KEV entry; no EPSS available | default key "mysecret" is in the public source; any token can be forged                                                           |
| CVE-2026-56266 (GHSA-365w-hqf6-vxfg)     | /screenshot, /pdf output\_path                                | 9.1  | no KEV entry; no EPSS available | arbitrary filesystem write; overwrites server files or writes anywhere the appuser can write                                      |
| GHSA-7cx2-g3h9-382p                      | /screenshot, /pdf output\_path (symlink / TOCTOU)             | n/a  | no KEV entry; no EPSS available | the 0.8.7 containment check was string-only and did not resolve symlinks; a symlinked path component escapes the output directory |
| CVE-2026-56266 (GHSA-365w-hqf6-vxfg)     | /crawl/job, /llm/job webhook URL                              | 8.6  | no KEV entry; no EPSS available | webhook URLs accept RFC 1918, loopback, link-local and metadata addresses                                                         |
| CVE-2026-56266 (GHSA-365w-hqf6-vxfg)     | /crawl, /crawl/stream, /md, /llm                              | 8.6  | no KEV entry; no EPSS available | primary crawl entry points fetch arbitrary URLs; IPv6-mapped IPv4 evades a literal-hostname check                                 |
| GHSA-6qhc-x826-342c                      | browser\_config.proxy\_config.server and related proxy fields | n/a  | no KEV entry; no EPSS available | a benign crawl URL passes validation while the proxy routes Chromium to an internal address                                       |
| GHSA-4qqr-vv2q-cmr5                      | validate\_webhook\_url / validate\_url\_destination           | n/a  | no KEV entry; no EPSS available | NAT64, 6to4, IPv4-mapped, IPv4-compatible and the IPv6 unspecified address all bypassed the CIDR blocklist                        |
| CVE-2026-56266 (GHSA-365w-hqf6-vxfg)     | /execute\_js                                                  | 8.1  | no KEV entry; no EPSS available | arbitrary JavaScript in the server browser with \--disable-web-security enabled                                                   |
| GHSA-f989-c77f-r2cq, GHSA-mw9f-q68p-364x | /md, /llm, /llm/job base\_url and env: token resolution       | n/a  | no KEV entry; no EPSS available | exfiltrates LLM provider keys and server secrets including SECRET\_KEY                                                            |
| CVE-2026-56266 (GHSA-365w-hqf6-vxfg)     | /monitor/\*                                                   | 6.5  | no KEV entry; no EPSS available | monitor router mounted without token\_dep; destructive endpoints such as /monitor/actions/cleanup are open                        |
| CVE-2026-56266 (GHSA-365w-hqf6-vxfg)     | monitor dashboard                                             | 6.1  | no KEV entry; no EPSS available | stored XSS via crafted crawl URLs rendered through innerHTML                                                                      |
| GHSA-7cx2-g3h9-382p                      | logging, outbound webhook headers                             | n/a  | no KEV entry; no EPSS available | CRLF log injection (CWE-117) and webhook request-header injection (CWE-93/CWE-113)                                                |

### Confirmed exploitation path

1. Reach the API. The Docker API server is unauthenticated by default. If `CRAWL4AI_API_TOKEN` is unset, no credential is required for any step that follows. If JWT is enabled but `SECRET_KEY` was left at the default, the attacker signs a token with `"mysecret"` and proceeds authenticated.
2. Retrieve cloud credentials by SSRF. `POST /crawl` with a URL pointing at `169.254.169.254` makes the server fetch instance metadata. The advisory names AWS IMDSv1 as the target for IAM credential tokens. The same applies to `/crawl/stream`, `/md` and `/llm`, and to webhook URLs supplied to `/crawl/job` and `/llm/job`.
3. Defeat the blocklist where one is present. Encode the internal address in an IPv6 transition form. The advisory lists `::`, `64:ff9b::a9fe:a9fe` (NAT64), `2002:a9fe:a9fe::` (6to4), `::ffff:169.254.169.254` (IPv4-mapped) and `::a9fe:a9fe` (IPv4-compatible) as forms that resolved to blocked ranges but were not caught. The pre-fix error message also echoed the resolved internal IP, giving the attacker a DNS oracle.
4. Bypass the crawl-URL check entirely with a proxy. Send `/crawl` with a benign, validation-passing URL and a `proxy_config.server` pointing at an internal address. Chromium routes all requests through that proxy. For plain-HTTP targets the proxy receives the full request and returns any content, which the API returns verbatim in `results[0].html`, `cleaned_html` or `markdown`. The same unchecked surface covers `browser_config.proxy` (deprecated), `crawler_config.proxy_config.server`, and the `--proxy-server`, `--proxy-pac-url`, `--proxy-bypass-list` and `--host-resolver-rules` flags in `browser_config.extra_args`.
5. Exfiltrate LLM provider keys and server secrets. `POST /md`, `/llm` or `/llm/job` with `base_url` set to an attacker-controlled host. The server attaches its configured provider API key to the outbound request, so the key arrives at the attacker endpoint. Separately, `LLMConfig(api_token="env:NAME")` resolves `NAME` from the server environment via `os.getenv`; because request bodies deserialise into `LLMConfig`, an attacker sets `api_token="env:SECRET_KEY"` or `env:REDIS_PASSWORD` and, paired with the attacker `base_url`, reads that secret out. Reading `SECRET_KEY` yields token forgery.
6. Write files, and potentially execute code. `POST /screenshot` or `POST /pdf` with an `output_path` outside the intended directory. The pre-fix code accepted arbitrary filesystem paths with no validation, so the attacker overwrites server files (denial of service) or writes anywhere the appuser can write. The symlink/TOCTOU variant is the sharper edge: the 0.8.7 `validate_output_path` check was string-only and did not resolve symlinks, so a symlinked path component inside the output directory redirected the write outside it, and the final `open()` followed the symlink. On a deployment where the runtime user can write executable or cron locations, that is an arbitrary-write to code-execution primitive.
7. Execute JavaScript in the server browser. `POST /execute_js` runs arbitrary JavaScript with `--disable-web-security` set in the default browser arguments, combining script execution with the SSRF capability above.
8. Reach the monitor plane without credentials. The monitor router was mounted without the `token_dep` dependency, so every monitoring endpoint, including destructive ones such as `/monitor/actions/cleanup`, was reachable unauthenticated. The WebSocket endpoint `/monitor/ws` had no token check either. Crafted crawl URLs stored and rendered through `innerHTML` in the monitor dashboard give stored XSS against an operator viewing that dashboard.

### Fix detail and discrepancies

The primary advisory describes the file-write fix as adding `validate_output_path()` to restrict writes to `CRAWL4AI_OUTPUT_DIR` (`/tmp/crawl4ai-outputs` by default) plus a Pydantic `field_validator` rejecting `..` traversal sequences. GHSA-7cx2-g3h9-382p describes the same control differently: it states the 0.8.7 check was string-only and did not resolve symlinks, and that the fix now resolves the real path of the parent, re-checks containment, and writes with `O_NOFOLLOW` via `write_output_file`. Both descriptions cover the same control at different fix stages; the symlink-resolving behaviour is the one that matters for containment. A second discrepancy sits in the SSRF fix: the primary advisory says IPv6-mapped IPv4 addresses are normalised to IPv4 form before the blocklist check, while GHSA-4qqr-vv2q-cmr5 says the CIDR blocklist was replaced by a single `not ip.is_global` rule evaluated on the address and every embedded IPv4 transition form. Treat the `is_global` rule as the current behaviour.

The SSRF fixes apply at request entry, not only at fetch time, and `raw://` URLs are skipped. Webhook validation runs at both job submission and send time with `follow_redirects=False`. `/execute_js` is disabled by default behind `CRAWL4AI_EXECUTE_JS_ENABLED`, and `--disable-web-security` was removed from the default browser arguments. The LLM endpoints now ignore a request-supplied `base_url` and derive the endpoint server-side from the provider name; the field is still accepted so no 4xx is returned. `LLMConfig` refuses `env:` resolution of protected names (names containing SECRET, PASSWORD or PRIVATE, prefixes `CRAWL4AI*` and `AWS_SECRET*`, and `SECRET_KEY`, `REDIS_PASSWORD`, `TOKEN`). Normal provider keys such as `OPENAI_API_KEY` are unaffected.

### Confidence caveats

All of the above rests on the five GitHub Security Advisories listed in section 7\. There is no independent vendor research, no telemetry and no exploitation reporting in the source material. The CVSS values are the advisory's own and have not been corroborated against NVD. The researcher credits in the primary advisory (Jeongbean Jeon, wulonchia, by111/August829, secsys\_codex, Velayutham Selvaraj, IcySun and Yashon) are individual reporters, not tracked threat actors, and no MITRE ATT&CK group profile applies. Nothing in the source material indicates ransomware, extortion or data theft.

## 4\. Mitigation & containment

### P1, within 24 hours

- Upgrade the Crawl4AI Docker image to 0.8.9\. This is the floor: 0.8.8 closes the LLM credential-exfiltration issues and 0.8.9 closes the proxy SSRF bypass. Pin the image by digest rather than tag.
- If an upgrade cannot be completed today, set `CRAWL4AI_API_TOKEN` to a long random value so the API is no longer unauthenticated, and set `SECRET_KEY` to a random value of at least 32 characters. Do not leave JWT enabled with the default key.
- Cut the container's route to link-local address space. Block `169.254.0.0/16` at the egress firewall or the container network policy, and enforce IMDSv2 on any AWS instance hosting the service so a metadata fetch without a session token fails.
- Stop exposing the Docker API beyond the host. Bind it to loopback or a dedicated internal network instead of `0.0.0.0`, and remove any public ingress rule pointing at the API port.
- Hunt for prior exploitation in API access logs: unauthenticated requests to `/monitor/*` or `/monitor/ws`, `output_path` values containing `..` or absolute paths outside `CRAWL4AI_OUTPUT_DIR`, and crawl or webhook URLs resolving to `169.254.169.254`, RFC 1918, loopback or link-local addresses.

### P2, within 72 hours

- Set `CRAWL4AI_EXECUTE_JS_ENABLED=false` explicitly rather than relying on the patched default.
- Run the container with a read-only root filesystem and mount a dedicated, non-executable volume for `CRAWL4AI_OUTPUT_DIR`. This removes the code-execution edge from the file-write primitive even on an unpatched build.
- Apply an egress allowlist to the container so it can reach only the sites and LLM providers it needs. This is the control that survives an unpatched SSRF.
- Rotate every secret that was present in the container environment: LLM provider API keys, `SECRET_KEY`, `REDIS_PASSWORD`, and any `AWS_SECRET*` value. Rotate them after the upgrade, not before, or the new values are exposed by the same flaw.
- Review the log pipeline for forged entries. The CRLF log injection issue let user-controlled URLs and errors embed CR/LF and fabricate additional log lines, so any log-based alerting that ran during the exposure window needs re-checking against raw source records.

### P3, within 7 days

- Inventory every Crawl4AI deployment, including development, CI and internal tooling instances. The unauthenticated default means a forgotten instance is as exposed as a production one.
- Remove sensitive secrets from the container environment entirely. The `env:` resolution path exists because secrets sit alongside provider keys in the same environment.
- Deploy the detection rules in section 6 and confirm they fire against a controlled test request before relying on them.

## 5\. Indicators of compromise

No atomic indicators of compromise are available in the source material. There are no hashes, no domains, no IP addresses and no file paths belonging to an attacker. The sources describe observable behaviours only, and those are listed below.

### Behavioural indicators

| behaviour                                                                                                                                                                                                                                                 | where to observe                      | confidence                                                      |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------- |
| Requests to /monitor/\*, including /monitor/actions/cleanup, with no authentication token                                                                                                                                                                 | API access logs, reverse proxy logs   | high, documented in GHSA-365w-hqf6-vxfg                         |
| WebSocket connection to /monitor/ws with no token check                                                                                                                                                                                                   | reverse proxy logs                    | high, documented in GHSA-365w-hqf6-vxfg                         |
| output\_path values containing .. or resolving outside CRAWL4AI\_OUTPUT\_DIR                                                                                                                                                                              | API access logs                       | high, documented in GHSA-365w-hqf6-vxfg and GHSA-7cx2-g3h9-382p |
| Crawl or webhook URLs resolving to 169.254.169.254, RFC 1918, loopback or link-local addresses                                                                                                                                                            | API access logs, egress firewall logs | high, documented in GHSA-365w-hqf6-vxfg                         |
| URL fields containing IPv6 transition forms: ::ffff:, 64:ff9b::, 2002:a9fe, ::a9fe:a9fe, or the bare unspecified address ::                                                                                                                               | API access logs                       | high, documented in GHSA-4qqr-vv2q-cmr5                         |
| browser\_config.proxy\_config.server, browser\_config.proxy or crawler\_config.proxy\_config.server set to an internal address, or \--proxy-server, \--proxy-pac-url, \--proxy-bypass-list, \--host-resolver-rules present in browser\_config.extra\_args | API access logs                       | high, documented in GHSA-6qhc-x826-342c                         |
| base\_url in a /md, /llm or /llm/job request pointing at a host that is not the configured LLM provider                                                                                                                                                   | API access logs                       | high, documented in GHSA-f989-c77f-r2cq                         |
| api\_token values of the form env:NAME, particularly env:SECRET\_KEY or env:REDIS\_PASSWORD                                                                                                                                                               | API access logs                       | high, documented in GHSA-f989-c77f-r2cq                         |
| Outbound HTTP from the container to an unexpected host carrying an Authorization header                                                                                                                                                                   | egress proxy logs                     | medium, inferred from the documented base\_url behaviour        |
| CR or LF characters in logged URL or error fields producing additional log lines                                                                                                                                                                          | log ingestion pipeline                | medium, documented in GHSA-7cx2-g3h9-382p                       |
| Outbound webhook requests carrying attacker-supplied Host, Content-Length, Transfer-Encoding, Authorization or Cookie headers                                                                                                                             | egress logs                           | medium, documented in GHSA-7cx2-g3h9-382p                       |

## 6\. Detection

The YARA rule below hunts deployed Crawl

---

[Read the original source →](https://github.com/advisories/GHSA-365w-hqf6-vxfg?ref=f4n6.co.uk)

*Published via PulseTrace — Adverse Trace threat intelligence.*