{"id":"GHSA-3mr9-p497-58f6","aliases":["CVE-2026-55824"],"title":"Contao crawler leaks auth credentials to external hosts","titleSource":"OSV","descriptionSource":"OSV","declaredSeverity":"LOW","description":"### Summary\nContao's crawler tries to prevent confidential HTTP client options from being sent to external domains by creating a scoped client: full options for root page origins, cleaned options for everything else. The cleaner removes `Cookie` and `Authorization` headers, but it removes the non-Symfony option names `basic_auth` and `bearer_auth` instead of Symfony HttpClient's real `auth_basic` and `auth_bearer` options.\n\nWhen `contao.crawl.default_http_client_options` contains Basic or Bearer authentication for a protected staging/production site, those credentials remain in the \"clean\" client used for external links or configured additional URIs. An attacker who can get an external URL crawled, for example through a link on a crawled page while the broken-link checker is enabled, can receive the crawler credentials.\n\n## Technical Detail\n\n### Root Cause\n\n```php\n// core-bundle/src/Crawl/Escargot/Factory.php:175-209 @ e550b92a01ef625bd546e6c3956dd200af05ebf0\nprivate function createHttpClient(array $options = []): HttpClientInterface\n{\n    $options = array_merge_recursive(\n        [\n            'headers' => [\n                'accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n                'user-agent' => self::USER_AGENT,\n            ],\n            'max_duration' => 10,\n        ],\n        array_merge_recursive($this->getDefaultHttpClientOptions(), $options),\n    );\n\n    $cleanOptions = $this->cleanOptionsFromConfidentialData($options);\n\n    if ($options === $cleanOptions) {\n        return ($this->httpClientFactory)($options);\n    }\n\n    $scopedOptionsByRegex = [];\n\n    foreach ($this->getRootPageUriCollection()->all() as $rootPageUri) {\n        $scopedOptionsByRegex[preg_quote($this->getOriginFromUri($rootPageUri))] = $options;\n    }\n\n    return new ScopingHttpClient(($this->httpClientFactory)($cleanOptions), $scopedOptionsByRegex);\n}\n```\n\n```php\n// core-bundle/src/Crawl/Escargot/Factory.php:226-247 @ e550b92a01ef625bd546e6c3956dd200af05ebf0\nforeach ($options as $k => $v) {\n    if ('headers' === $k) {\n        foreach ($v as $header => $value) {\n            if (\\in_array(strtolower($header), ['authorization', 'cookie'], true)) {\n                continue;\n            }\n\n            $cleanOptions['headers'][$header] = $value;\n        }\n\n        continue;\n    }\n\n    if ('basic_auth' === $k || 'bearer_auth' === $k) {\n        continue;\n    }\n\n    $cleanOptions[$k] = $v;\n}\n```\n\nSymfony HttpClient authentication options are `auth_basic` and `auth_bearer`; Contao's own manual documents `auth_basic` for crawler Basic Authentication. Because the cleaner only strips `basic_auth` and `bearer_auth`, the \"clean\" default client for non-root-page hosts still carries the real auth options. The existing factory test intends to assert that `Authorization` is not sent to `www.foreign-domain.com`, but its mock client factory ignores the `$defaultOptions` argument, so it does not catch auth options that survive into `HttpClient::create($cleanOptions)`.\n\n\n## Suggested Mitigation\n\nStrip the actual Symfony HttpClient authentication option keys from the clean client. Include NTLM as a defensive extension because Symfony documents it as another auth option.\n\n```diff\n-            if ('basic_auth' === $k || 'bearer_auth' === $k) {\n+            if (\\in_array($k, ['auth_basic', 'auth_bearer', 'auth_ntlm', 'basic_auth', 'bearer_auth'], true)) {\n                 continue;\n             }\n```\n\nAlso update the factory test so the mock factory records or preserves `$defaultOptions`; otherwise the test does not verify what `HttpClient::create($cleanOptions)` receives in production.\n\n## Impact\n\n- **Direct primitive**: disclosure of crawler Basic/Bearer credentials to an external host reached by the crawler.\n- **Chain potential**: if those credentials protect a staging or pre-publication environment, an attacker can use them to access that environment. The impact depends on what the leaked credential unlocks.\n- **Realistic exploitation**: a content editor adds a link to `https://attacker.example/probe` on a page that the crawler visits. When an administrator or scheduled maintenance run starts the broken-link checker with crawler Basic/Bearer authentication configured, the request to the attacker URL includes the generated `Authorization` header.","severity":"low","score":2.6,"published":"2026-08-06T19:43:02.000Z","modified":"2026-08-06T19:45:16.256Z","withdrawn":false,"knownExploited":false,"affected":[{"ecosystem":"Packagist","name":"contao/contao","sources":[{"name":"OSV · GHSA-3mr9-p497-58f6","url":"https://osv.dev/vulnerability/GHSA-3mr9-p497-58f6"}],"ranges":[{"events":[{"introduced":"4.13.0"},{"fixed":"5.3.47"}],"type":"ECOSYSTEM"}],"versions":["4.13.0","4.13.1","4.13.10","4.13.11","4.13.12","4.13.13","4.13.14","4.13.15","4.13.16","4.13.17","4.13.18","4.13.19","4.13.2","4.13.20","4.13.21","4.13.22","4.13.23","4.13.24","4.13.25","4.13.26","4.13.27","4.13.28","4.13.29","4.13.3","4.13.30","4.13.31","4.13.32","4.13.33","4.13.34","4.13.35","4.13.36","4.13.37","4.13.38","4.13.39","4.13.4","4.13.40","4.13.41","4.13.42","4.13.43","4.13.44","4.13.45","4.13.46","4.13.47","4.13.48","4.13.49","4.13.5","4.13.50","4.13.51","4.13.52","4.13.53","4.13.54","4.13.55","4.13.56","4.13.57","4.13.58","4.13.6","4.13.7","4.13.8","4.13.9","5.0.0","5.0.0-RC1","5.0.0-RC2","5.0.0-RC3","5.0.0-RC4","5.0.1","5.0.10","5.0.2","5.0.3","5.0.4","5.0.5","5.0.6","5.0.7","5.0.8","5.0.9","5.1.0","5.1.0-RC1","5.1.0-RC2","5.1.0-RC3","5.1.1","5.1.10","5.1.11","5.1.2","5.1.3","5.1.4","5.1.5","5.1.6","5.1.7","5.1.8","5.1.9","5.2.0","5.2.0-RC1","5.2.0-RC2","5.2.0-RC3","5.2.0-RC4","5.2.0-RC5","5.2.0-RC6","5.2.1","5.2.10","5.2.2","5.2.3","5.2.4","5.2.5","5.2.6","5.2.7","5.2.8","5.2.9","5.3.0","5.3.0-RC1","5.3.0-RC2","5.3.0-RC3","5.3.0-RC4","5.3.1","5.3.10","5.3.11","5.3.12","5.3.13","5.3.14","5.3.15","5.3.16","5.3.17","5.3.18","5.3.19","5.3.2","5.3.20","5.3.21","5.3.22","5.3.23","5.3.24","5.3.25","5.3.26","5.3.27","5.3.28","5.3.29","5.3.3","5.3.30","5.3.31","5.3.32","5.3.33","5.3.34","5.3.35","5.3.36","5.3.37","5.3.38","5.3.39","5.3.4","5.3.40","5.3.41","5.3.42","5.3.43","5.3.44","5.3.45","5.3.46","5.3.5","5.3.6","5.3.7","5.3.8","5.3.9"],"fixed":["5.3.47"]},{"ecosystem":"Packagist","name":"contao/contao","sources":[{"name":"OSV · GHSA-3mr9-p497-58f6","url":"https://osv.dev/vulnerability/GHSA-3mr9-p497-58f6"}],"ranges":[{"events":[{"introduced":"5.4.0"},{"fixed":"5.7.7"}],"type":"ECOSYSTEM"}],"versions":["5.4.0","5.4.1","5.4.10","5.4.11","5.4.12","5.4.13","5.4.14","5.4.2","5.4.3","5.4.4","5.4.5","5.4.6","5.4.7","5.4.8","5.4.9","5.5.0","5.5.0-RC1","5.5.0-RC2","5.5.0-RC3","5.5.0-RC4","5.5.1","5.5.10","5.5.11","5.5.12","5.5.13","5.5.14","5.5.15","5.5.16","5.5.2","5.5.3","5.5.4","5.5.5","5.5.6","5.5.7","5.5.8","5.5.9","5.6.0","5.6.0-RC1","5.6.0-RC2","5.6.0-RC3","5.6.1","5.6.10","5.6.11","5.6.2","5.6.3","5.6.4","5.6.5","5.6.6","5.6.7","5.6.8","5.6.9","5.7.0","5.7.0-RC1","5.7.0-RC2","5.7.0-RC3","5.7.0-RC4","5.7.1","5.7.2","5.7.3","5.7.4","5.7.5","5.7.6"],"fixed":["5.7.7"]},{"ecosystem":"Packagist","name":"contao/core-bundle","sources":[{"name":"OSV · GHSA-3mr9-p497-58f6","url":"https://osv.dev/vulnerability/GHSA-3mr9-p497-58f6"}],"ranges":[{"events":[{"introduced":"4.13.0"},{"fixed":"5.3.47"}],"type":"ECOSYSTEM"}],"versions":["4.13.0","4.13.1","4.13.10","4.13.11","4.13.12","4.13.13","4.13.14","4.13.15","4.13.16","4.13.17","4.13.18","4.13.19","4.13.2","4.13.20","4.13.21","4.13.22","4.13.23","4.13.24","4.13.25","4.13.26","4.13.27","4.13.28","4.13.29","4.13.3","4.13.30","4.13.31","4.13.32","4.13.33","4.13.34","4.13.35","4.13.36","4.13.37","4.13.38","4.13.39","4.13.4","4.13.40","4.13.41","4.13.42","4.13.43","4.13.44","4.13.45","4.13.46","4.13.47","4.13.48","4.13.49","4.13.5","4.13.50","4.13.51","4.13.52","4.13.53","4.13.54","4.13.55","4.13.56","4.13.57","4.13.58","4.13.6","4.13.7","4.13.8","4.13.9","5.0.0","5.0.0-RC1","5.0.0-RC2","5.0.0-RC3","5.0.0-RC4","5.0.1","5.0.10","5.0.2","5.0.3","5.0.4","5.0.5","5.0.6","5.0.7","5.0.8","5.0.9","5.1.0","5.1.0-RC1","5.1.0-RC2","5.1.0-RC3","5.1.1","5.1.10","5.1.11","5.1.2","5.1.3","5.1.4","5.1.5","5.1.6","5.1.7","5.1.8","5.1.9","5.2.0","5.2.0-RC1","5.2.0-RC2","5.2.0-RC3","5.2.0-RC4","5.2.0-RC5","5.2.0-RC6","5.2.1","5.2.10","5.2.2","5.2.3","5.2.4","5.2.5","5.2.6","5.2.7","5.2.8","5.2.9","5.3.0","5.3.0-RC1","5.3.0-RC2","5.3.0-RC3","5.3.0-RC4","5.3.1","5.3.10","5.3.11","5.3.12","5.3.13","5.3.14","5.3.15","5.3.16","5.3.17","5.3.18","5.3.19","5.3.2","5.3.20","5.3.21","5.3.22","5.3.23","5.3.24","5.3.25","5.3.26","5.3.27","5.3.28","5.3.29","5.3.3","5.3.30","5.3.31","5.3.32","5.3.33","5.3.34","5.3.35","5.3.36","5.3.37","5.3.38","5.3.39","5.3.4","5.3.40","5.3.41","5.3.42","5.3.43","5.3.44","5.3.45","5.3.46","5.3.5","5.3.6","5.3.7","5.3.8","5.3.9"],"fixed":["5.3.47"]},{"ecosystem":"Packagist","name":"contao/core-bundle","sources":[{"name":"OSV · GHSA-3mr9-p497-58f6","url":"https://osv.dev/vulnerability/GHSA-3mr9-p497-58f6"}],"ranges":[{"events":[{"introduced":"5.4.0"},{"fixed":"5.7.7"}],"type":"ECOSYSTEM"}],"versions":["5.4.0","5.4.1","5.4.10","5.4.11","5.4.12","5.4.13","5.4.14","5.4.2","5.4.3","5.4.4","5.4.5","5.4.6","5.4.7","5.4.8","5.4.9","5.5.0","5.5.0-RC1","5.5.0-RC2","5.5.0-RC3","5.5.0-RC4","5.5.1","5.5.10","5.5.11","5.5.12","5.5.13","5.5.14","5.5.15","5.5.16","5.5.2","5.5.3","5.5.4","5.5.5","5.5.6","5.5.7","5.5.8","5.5.9","5.6.0","5.6.0-RC1","5.6.0-RC2","5.6.0-RC3","5.6.1","5.6.10","5.6.11","5.6.2","5.6.3","5.6.4","5.6.5","5.6.6","5.6.7","5.6.8","5.6.9","5.7.0","5.7.0-RC1","5.7.0-RC2","5.7.0-RC3","5.7.0-RC4","5.7.1","5.7.2","5.7.3","5.7.4","5.7.5","5.7.6"],"fixed":["5.7.7"]}],"sourceAffected":[],"references":["https://github.com/contao/contao/security/advisories/GHSA-3mr9-p497-58f6","https://nvd.nist.gov/vuln/detail/CVE-2026-55824","https://github.com/contao/contao/commit/5bc6e3f900c439313df57aa561d0865792aafa05","https://github.com/contao/contao/commit/80425d28cdf66280a209bd3f5bc31b1a76901a04","https://contao.org/en/security-advisories/credentials-disclosure-in-the-crawler","https://github.com/FriendsOfPHP/security-advisories/blob/master/contao/contao/CVE-2026-55824.yaml","https://github.com/FriendsOfPHP/security-advisories/blob/master/contao/core-bundle/CVE-2026-55824.yaml","https://github.com/contao/contao"],"sources":[{"name":"OSV","id":"GHSA-3mr9-p497-58f6","modified":"2026-08-06T19:45:16.256360775Z","url":"https://osv.dev/vulnerability/GHSA-3mr9-p497-58f6"}],"assessments":[{"source":"OSV","vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:N/A:N","score":2.6}],"products":[],"remediation":[],"kev":{"status":"not-listed","checkedAt":"2026-10-28T21:15:31.530Z"},"coverage":[{"source":"NVD","status":"not-checked"},{"source":"GHSA","status":"not-checked"},{"source":"EUVD","status":"not-checked"},{"source":"EPSS","status":"not-checked"}],"evidence":[]}