Potential Open Redirect via Parsing Differences in TYPO3
Description
TYPO3 is a free and open source Content Management Framework. Applications that use TYPO3\CMS\Core\Http\Uri to parse externally provided URLs (e.g., via a query parameter) and validate the host of the parsed URL may be vulnerable to open redirect or SSRF attacks if the URL is used after passing the validation checks. Users are advised to update to TYPO3 versions 9.5.49 ELTS, 10.4.48 ELTS, 11.5.42 LTS, 12.4.25 LTS, 13.4.3 which fix the problem described. There are no known workarounds for this vulnerability.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2024-55892: TYPO3 URI host validation bypass via backslash parsing differences, enabling open redirect or SSRF.
Description
CVE-2024-55892 is a vulnerability in the TYPO3 content management framework affecting the TYPO3\CMS\Core\Http\Uri component. The root cause is a discrepancy between how the Uri parser interprets malformed host portions (specifically backslashes) and how browsers or other HTTP clients ultimately resolve the URL. This parser deviation allows an attacker to craft a URL that passes host validation checks within the TYPO3 application but directs a user or server request to an unintended destination [1][4].
Exploitation
An attacker can supply an externally controlled URL (for example, via a query parameter) that is parsed by TYPO3\CMS\Core\Http\Uri. The host validation step in the application may see a legitimate host, but the actual HTTP request or redirect will target a different host due to the backslash handling differences. The vulnerability can be exploited without authentication if the application parses user-supplied URLs and relies on the Uri object to determine the final destination. As noted in the commit, inputs such as https://evil.tld\\@host.tld/ are parsed with host.tld as the host but may be interpreted as evil.tld by a browser [3][4].
Impact
Successful exploitation can lead to open redirect attacks, where a visitor is sent to a malicious site under the guise of a valid TYPO3 domain. It can also enable SSRF (Server-Side Request Forgery) if the application uses the parsed URL to make server-side requests, potentially allowing an attacker to access internal resources or exfiltrate data [1][4].
Mitigation
The TYPO3 team has released fixed versions: 9.5.49 ELTS, 10.4.48 ELTS, 11.5.42 LTS, 12.4.25 LTS, and 13.4.3. There are no known workarounds, so upgrading to a patched version is the only remediation [1][4].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
typo3/cms-corePackagist | >= 9.0.0, < 9.5.49 | 9.5.49 |
typo3/cms-corePackagist | >= 10.0.0, < 10.4.48 | 10.4.48 |
typo3/cms-corePackagist | >= 11.0.0, < 11.5.42 | 11.5.42 |
typo3/cms-corePackagist | >= 12.0.0, < 12.4.25 | 12.4.25 |
typo3/cms-corePackagist | >= 13.0.0, < 13.4.3 | 13.4.3 |
Affected products
3Affected versions before 9.5.49 ELTS, before 10.4.48 ELTS, before 11.5.42 LTS, before 12.4.25 LTS, before 13.4.3+ 1 more
- (no CPE)range: Affected versions before 9.5.49 ELTS, before 10.4.48 ELTS, before 11.5.42 LTS, before 12.4.25 LTS, before 13.4.3
- (no CPE)range: >= 9.0.0, < 9.5.49
Patches
1a4abf48d2546[SECURITY] Circumvent parser deviation in PSR-7 URI object
3 files changed · +171 −6
typo3/sysext/core/Classes/Http/Uri.php+43 −6 modified@@ -117,7 +117,7 @@ public static function fromAnyScheme(string $uri = ''): self /** * @param string $uri The full URI including query string and fragment - * @throws \InvalidArgumentException when the URI is not a string + * @throws \InvalidArgumentException if the URI is malformed. */ public function __construct(string $uri = '') { @@ -154,7 +154,14 @@ protected function parseUri(string $uri): void } } if (isset($uriParts['port'])) { - $this->port = (int)$uriParts['port']; + $port = (int)$uriParts['port']; + if (!$this->validatePort($port)) { + throw new \InvalidArgumentException( + 'The uri "' . $uri . '" appears to be malformed, invalid port "' . $port . '" specified, must be a valid TCP/UDP port', + 1728057215 + ); + } + $this->port = $port; } if (isset($uriParts['path'])) { $this->path = $this->sanitizePath($uriParts['path']); @@ -165,6 +172,30 @@ protected function parseUri(string $uri): void if (isset($uriParts['fragment'])) { $this->fragment = $this->sanitizeFragment($uriParts['fragment']); } + + if (!$this->validate()) { + throw new \InvalidArgumentException('The uri "' . $uri . '" appears to be malformed', 1728057216); + } + } + + protected function validate(): bool + { + $url = clone $this; + + if ($url->scheme === '') { + // filter_var will mark //example.com/ as invalid, let's pretend it's https in this case + $url->scheme = 'https'; + } + + if ($url->host === '') { + // filter_var will mark /mypath/ as invalid, let's pretend it's localhost in this case + $url->host = 'localhost'; + } else { + // filter_var can not validate UTF8 encoded hosts + $url->host = idn_to_ascii($url->host); + } + + return filter_var($url->__toString(), FILTER_VALIDATE_URL) !== false; } /** @@ -445,17 +476,23 @@ public function withHost(string $host): UriInterface */ public function withPort(?int $port): UriInterface { - if ($port !== null) { - if ($port < 1 || $port > 65535) { - throw new \InvalidArgumentException('Invalid port "' . $port . '" specified, must be a valid TCP/UDP port.', 1436717326); - } + if ($port !== null && !$this->validatePort($port)) { + throw new \InvalidArgumentException('Invalid port "' . $port . '" specified, must be a valid TCP/UDP port.', 1436717326); } $clonedObject = clone $this; $clonedObject->port = $port; return $clonedObject; } + protected function validatePort(int $port): bool + { + if ($port < 1 || $port > 65535) { + return false; + } + return true; + } + /** * Return an instance with the specified path. *
typo3/sysext/core/Classes/Security/ContentSecurityPolicy/UriValue.php+14 −0 modified@@ -36,6 +36,20 @@ public static function fromUri(UriInterface $other): self return new self((string)$other); } + protected function validate(): bool + { + $backupHost = null; + if ($this->host) { + $backupHost = $this->host; + $this->host = str_replace('*', 'wildcard', $this->host); + } + $ret = parent::validate(); + if ($backupHost !== null) { + $this->host = $backupHost; + } + return $ret; + } + public function __toString(): string { if ($this->entireWildcard) {
typo3/sysext/core/Tests/Unit/Http/UriTest.php+114 −0 modified@@ -110,14 +110,33 @@ public function withPortAndNullValueReturnsInstanceWithProvidedPort(): void public function noSchemeWithDomainAlikeIsInterpretedAsPath(): void { $subject = new Uri('www.example.com'); + // This is counter intuitive, but interpreted as path. + // Although we'd like to see protocol independent `//` here, + // we must not change this, as… self::assertEquals('/www.example.com', (string)$subject); + + // …this behaviour is security relevant. + // This invalid domain name – given without a scheme – is + // only "save" because they are considered to be paths + // (invalid hostname alike is not parsed as a hostname): + $subject = new Uri('evil.tld\\@host.tld'); + self::assertEquals('/evil.tld%5C@host.tld', (string)$subject); + + $subject = new Uri('evil.tld\\\\\\@host.tld'); + self::assertEquals('/evil.tld%5C%5C%5C@host.tld', (string)$subject); } #[Test] public function noSchemeWithDomainAlikeAndTrailingSlashIsInterpretedAsPath(): void { $subject = new Uri('www.example.com/'); self::assertEquals('/www.example.com/', (string)$subject); + + $subject = new Uri('evil.tld\\@host.tld/'); + self::assertEquals('/evil.tld%5C@host.tld/', (string)$subject); + + $subject = new Uri('evil.tld\\\\\\@host.tld/'); + self::assertEquals('/evil.tld%5C%5C%5C@host.tld/', (string)$subject); } public static function validPortsDataProvider(): array @@ -497,6 +516,69 @@ public function canParseInternationalizedDomainName(): void self::assertEquals('https://ουτοπία.δπθ.gr/', (string)$uri); } + public static function invalidHostDataProvider(): \Generator + { + yield [ + 'url' => 'https://evil.tld\\@host.tld/', + 'parseUrl' => 'host.tld', + 'chrome' => 'evil.tld', + ]; + + yield [ + 'url' => 'https://evil.tld\\\\@host.tld/', + 'parseUrl' => 'host.tld', + 'chrome' => 'evil.tld', + ]; + + yield [ + 'url' => 'https://evil.tld\\\\\\@host.tld/', + 'parseUrl' => 'host.tld', + 'chrome' => 'evil.tld', + ]; + + yield [ + 'url' => '//evil.tld\\@host.tld/', + 'parseUrl' => 'host.tld', + 'chrome' => 'evil.tld', + ]; + + yield [ + 'url' => '//evil.tld\\\\\\@host.tld', + 'parseUrl' => 'host.tld', + 'chrome' => 'evil.tld', + ]; + + yield [ + 'url' => 'http://normal.com[@evil.tld]/', + 'parseUrl' => 'evil.tld]', + 'chrome' => 'evil.tld', + ]; + + yield [ + 'url' => 'http://evil.tld\\', + 'parseUrl' => 'evil.tld\\', + 'chrome' => 'evil.tld', + ]; + } + + #[DataProvider('invalidHostDataProvider')] + #[Test] + public function uriParsingThrowsExceptionForParserDeviatingHostValues($url, $parseUrl, $chrome): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionCode(1728057216); + new Uri($url); + } + + #[DataProvider('invalidHostDataProvider')] + #[Test] + public function parseUrlReturnsExpectedInvalidHostForInvalidHostURLs($url, $parseUrl, $chrome): void + { + $result = parse_url($url, PHP_URL_HOST); + self::assertSame($parseUrl, $result); + self::assertNotSame($chrome, $result); + } + #[Test] public function canParseAllPropertiesWithIPv6(): void { @@ -563,4 +645,36 @@ public function canStringifyMalformedBracketlessIPv6toValidIPv6(): void $uri = new Uri($input); self::assertSame($expected, (string)$uri); } + + /** + * @return iterable<non-empty-string, array{non-empty-string}> + */ + public static function invalidUriProvider(): iterable + { + yield 'Unsupported scheme ftp' => ['ftp://user:pass@local.example.com:3001/foo?bar=baz#quz']; + yield 'Unsupported scheme ssh' => ['ssh://user:pass@local.example.com:3001/foo?bar=baz#quz']; + yield 'Unsupported scheme git' => ['git://user:pass@local.example.com:3001/foo?bar=baz#quz']; + + yield 'Invalid port -1 (too small)' => ['https://user:pass@local.example.com:-1/foo?bar=baz#quz']; + yield 'Invalid port 0 (zero)' => ['https://user:pass@local.example.com:0/foo?bar=baz#quz']; + yield 'Invalid port 65536 (too big)' => ['https://user:pass@local.example.com:65536/foo?bar=baz#quz']; + + yield from [ + 'Malformed URI' => ['http://invalid:%20https://example.com'], + 'Colon in non-IPv6 host' => ['https://user:pass@local:example.com:3001/foo?bar=baz#quz'], + 'Wrong bracket in the IPv6' => ['https://user:pass@fe80[::200:5aee:feaa:20a2]:3001/foo?bar=baz#quz'], + // percent encoding is allowed in URI but not in web urls particularly with idn encoding for dns. + // no validation for correct percent encoding either + // 'Percent in the host' => ["https://user:pass@local%example.com:3001/foo?bar=baz#quz"], + 'Bracket in the host' => ['https://user:pass@[local.example.com]:3001/foo?bar=baz#quz'], + ]; + } + + #[DataProvider('invalidUriProvider')] + #[Test] + public function invalidUriRaisesAnException(string $invalidUri): void + { + $this->expectException(\InvalidArgumentException::class); + new Uri($invalidUri); + } }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-2fx5-pggv-6jjrghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-55892ghsaADVISORY
- github.com/TYPO3/typo3/commit/a4abf48d254685f43383e6e7f80d48aebaea56afghsaWEB
- github.com/TYPO3/typo3/security/advisories/GHSA-2fx5-pggv-6jjrghsax_refsource_CONFIRMWEB
- typo3.org/security/advisory/typo3-core-sa-2025-002ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.