GHSA-3mr9-p497-58f6: Infoleak

Published Aug 6, 2026
·
Updated

Summary Contao'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 basicauth and bearerauth instead of Symfony HttpClient's real authbasic and authbearer options.

When contao.crawl.defaulthttpclientoptions 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.

Technical Detail

Root Cause

php // core-bundle/src/Crawl/Escargot/Factory.php:175-209 @ e550b92a01ef625bd546e6c3956dd200af05ebf0 private function createHttpClient(array $options = []): HttpClientInterface { $options = arraymergerecursive( [ 'headers' => [ 'accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8', 'user-agent' => self::USERAGENT, ], 'maxduration' => 10, ], arraymergerecursive($this->getDefaultHttpClientOptions(), $options), );

$cleanOptions = $this->cleanOptionsFromConfidentialData($options);

if ($options === $cleanOptions) { return ($this->httpClientFactory)($options); }

$scopedOptionsByRegex = [];

foreach ($this->getRootPageUriCollection()->all() as $rootPageUri) { $scopedOptionsByRegex[pregquote($this->getOriginFromUri($rootPageUri))] = $options; }

return new ScopingHttpClient(($this->httpClientFactory)($cleanOptions), $scopedOptionsByRegex); }

php // core-bundle/src/Crawl/Escargot/Factory.php:226-247 @ e550b92a01ef625bd546e6c3956dd200af05ebf0 foreach ($options as $k => $v) { if ('headers' === $k) { foreach ($v as $header => $value) { if (\inarray(strtolower($header), ['authorization', 'cookie'], true)) { continue; }

$cleanOptions['headers'][$header] = $value; }

continue; }

if ('basicauth' === $k || 'bearerauth' === $k) { continue; }

$cleanOptions[$k] = $v; }

Symfony HttpClient authentication options are authbasic and authbearer; Contao's own manual documents authbasic for crawler Basic Authentication. Because the cleaner only strips basicauth and bearerauth, 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).

Suggested Mitigation

Strip 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.

diff - if ('basicauth' === $k || 'bearerauth' === $k) { + if (\inarray($k, ['authbasic', 'authbearer', 'authntlm', 'basicauth', 'bearerauth'], true)) { continue; }

Also 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.

Impact

- Direct primitive: disclosure of crawler Basic/Bearer credentials to an external host reached by the crawler. - 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. - 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.

Affected Software

4 affected componentsFixes available
composer/contao/core-bundle>=5.4.0<5.7.7
5.7.7
composer/contao/core-bundle>=4.13.0<5.3.47
5.3.47
composer/contao/contao>=5.4.0<5.7.7
5.7.7
composer/contao/contao>=4.13.0<5.3.47
5.3.47

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade composer/contao/core-bundle to a version that resolves this vulnerability.

    Fixed in 5.7.7
  2. Upgrade

    Upgrade composer/contao/core-bundle to a version that resolves this vulnerability.

    Fixed in 5.3.47
  3. Upgrade

    Upgrade composer/contao/contao to a version that resolves this vulnerability.

    Fixed in 5.7.7
  4. Upgrade

    Upgrade composer/contao/contao to a version that resolves this vulnerability.

    Fixed in 5.3.47
  5. Configuration

    Update the factory cleaner to strip the actual Symfony HttpClient authentication option keys ('auth_basic', 'auth_bearer') from the clean client used for non-root-page hosts, and additionally include 'auth_ntlm' as a defensive extension (remove NTLM auth options from the clean client as well). The aim is that crawler requests to external domains do not retain any Basic/Bearer/NTLM authentication credentials from the configured default options.

    Contao crawler HttpClient options cleaner (core-bundle/src/Crawl/Escargot/Factory.php) clean HTTP client authentication option keys = Strip from clean client: auth_basic, auth_bearer, basic_auth, bearer_auth, auth_ntlm
  6. Configuration

    Modify the cleaner logic where it currently only matches 'basic_auth' and 'bearer_auth' to instead treat any of these keys as confidential and strip them from the clean client: ['auth_basic','auth_bearer','auth_ntlm','basic_auth','bearer_auth'] (as shown by the in_array([...], true) change in Factory.php around lines 175-209 / 226-247 @ e550b92a01ef625bd546e6c3956dd200af05ebf0).

    Contao crawler HttpClient options cleaner (core-bundle/src/Crawl/Escargot/Factory.php) auth option key filtering condition = Check keys in ['auth_basic','auth_bearer','auth_ntlm','basic_auth','bearer_auth']
  7. Operational

    Update the factory test/mocks so the mock factory preserves or records the $defaultOptions passed into HttpClient::create($cleanOptions); otherwise the test will not verify that Authorization/auth options are removed from requests to external domains (core-bundle/src/Crawl/Escargot/Factory.php test behavior referenced in the provided text).

Event History

Aug 6, 2026
Advisory Published
via GitHub·07:43 PM
Data Sourced
via GitHub·07:43 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203