GHSA-F989-C77F-R2CQ
Vulnerability from github – Published: 2026-06-16 21:00 – Updated: 2026-06-16 21:00Summary
The Docker API server let a request control where LLM calls were sent and which environment variable an LLM token resolved from. Both could be abused to exfiltrate server-held secrets. The Docker API is unauthenticated by default.
Vector 1 - attacker base_url
/md, /llm, and /llm/job accepted a base_url in the request and used it as the LLM endpoint while still attaching the server's configured provider API key. An attacker set base_url to a server they control and received the provider key (and any provider keys the server holds) in the inbound request.
Vector 2 - arbitrary environment variable read via env:
LLMConfig(api_token="env:NAME") resolved NAME from the server environment with os.getenv. Because request bodies were deserialized into LLMConfig (via a crawler config / extraction strategy), an attacker could set api_token="env:SECRET_KEY" (or env:REDIS_PASSWORD, etc.) and, paired with an attacker base_url, exfiltrate that secret. Reading the server's SECRET_KEY enables forging authentication tokens.
Impact
Disclosure of LLM provider API keys and other server secrets to an attacker-controlled endpoint; reading the JWT SECRET_KEY can lead to authentication bypass.
Fix
- The LLM endpoints ignore a request-supplied
base_url; the endpoint is always derived server-side from the provider name. The field is still accepted but no longer honored (no breaking 4xx). LLMConfigrefusesenv:resolution of protected environment-variable names (names containing SECRET/PASSWORD/PRIVATE, prefixes CRAWL4AI/AWS_SECRET, and SECRET_KEY/REDIS_PASSWORD/TOKEN). Normal provider keys (e.g. OPENAI_API_KEY) are unaffected.
Workarounds
- Upgrade to the patched version.
- Enable authentication (
CRAWL4AI_API_TOKEN). - Do not place sensitive secrets in the server environment alongside provider keys.
Credits
- Geo (geo-chen) - reported the LLM credential exfiltration via request base_url.
- Internal security audit (Crawl4AI maintainers) - the env: arbitrary-variable read.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.8.7"
},
"package": {
"ecosystem": "PyPI",
"name": "crawl4ai"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.8.8"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-200",
"CWE-522",
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-16T21:00:31Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\n\nThe Docker API server let a request control where LLM calls were sent and which environment variable an LLM token resolved from. Both could be abused to exfiltrate server-held secrets. The Docker API is unauthenticated by default.\n\n### Vector 1 - attacker base_url\n\n`/md`, `/llm`, and `/llm/job` accepted a `base_url` in the request and used it as the LLM endpoint while still attaching the server\u0027s configured provider API key. An attacker set `base_url` to a server they control and received the provider key (and any provider keys the server holds) in the inbound request.\n\n### Vector 2 - arbitrary environment variable read via `env:`\n\n`LLMConfig(api_token=\"env:NAME\")` resolved `NAME` from the server environment with `os.getenv`. Because request bodies were deserialized into `LLMConfig` (via a crawler config / extraction strategy), an attacker could set `api_token=\"env:SECRET_KEY\"` (or `env:REDIS_PASSWORD`, etc.) and, paired with an attacker `base_url`, exfiltrate that secret. Reading the server\u0027s `SECRET_KEY` enables forging authentication tokens.\n\n### Impact\n\nDisclosure of LLM provider API keys and other server secrets to an attacker-controlled endpoint; reading the JWT `SECRET_KEY` can lead to authentication bypass.\n\n### Fix\n\n- The LLM endpoints ignore a request-supplied `base_url`; the endpoint is always derived server-side from the provider name. The field is still accepted but no longer honored (no breaking 4xx).\n- `LLMConfig` refuses `env:` resolution of protected environment-variable names (names containing SECRET/PASSWORD/PRIVATE, prefixes CRAWL4AI*/AWS_SECRET*, and SECRET_KEY/REDIS_PASSWORD/TOKEN). Normal provider keys (e.g. OPENAI_API_KEY) are unaffected.\n\n### Workarounds\n\n- Upgrade to the patched version.\n- Enable authentication (`CRAWL4AI_API_TOKEN`).\n- Do not place sensitive secrets in the server environment alongside provider keys.\n\n### Credits\n\n- Geo ([geo-chen](https://github.com/geo-chen)) - reported the LLM credential exfiltration via request base_url.\n- Internal security audit (Crawl4AI maintainers) - the env: arbitrary-variable read.",
"id": "GHSA-f989-c77f-r2cq",
"modified": "2026-06-16T21:00:31Z",
"published": "2026-06-16T21:00:31Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/unclecode/crawl4ai/security/advisories/GHSA-f989-c77f-r2cq"
},
{
"type": "PACKAGE",
"url": "https://github.com/unclecode/crawl4ai"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Crawl4AI: LLM credential exfiltration in Docker server via request base_url and env: token resolution"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.