TYPO3 vulnerable to Cross-Site Scripting in ShowImageController
Description
TYPO3 is an enterprise content management system. Starting in version 9.0.0 and prior to versions 9.5.48 ELTS, 10.4.45 ELTS, 11.5.37 LTS, 12.4.15 LTS, and 13.1.1, failing to properly encode user-controlled values in file entities, the ShowImageController (_eID tx_cms_showpic_) is vulnerable to cross-site scripting. Exploiting this vulnerability requires a valid backend user account with access to file entities. TYPO3 versions 9.5.48 ELTS, 10.4.45 ELTS, 11.5.37 LTS, 12.4.15 LTS, 13.1.1 fix the problem described.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
TYPO3's ShowImageController fails to encode user-controlled file properties, leading to stored XSS for backend users with file access.
Vulnerability
Analysis
CVE-2024-34357 is a cross-site scripting (XSS) vulnerability in the TYPO3 content management system, affecting the ShowImageController (accessible via the _eID tx_cms_showpic_ endpoint). The root cause is the failure to properly encode user-controlled values in file entities before rendering them in the controller's output. This means that file properties such as title, alternative text, or other metadata can contain malicious HTML/JavaScript that is not sanitized, leading to XSS when the image is displayed [1].
Attack
Vector and Prerequisites
Exploitation requires a valid backend user account that has access to file entities. An attacker with such privileges can inject arbitrary JavaScript into file properties (e.g., title or alternative). When another user—or the attacker themselves—views an image via the ShowImageController, the injected script executes in the context of the victim's browser session. The attack does not require any special network position; it can be performed within the TYPO3 backend interface [1].
Impact
Successful exploitation allows an attacker to execute arbitrary JavaScript in the browser of any user who triggers the image display. This can lead to actions such as stealing session cookies, performing administrative actions on behalf of the victim, defacing the backend interface, or exfiltrating sensitive information. The vulnerability is considered high severity due to the potential for account takeover in a multi-user backend environment [1].
Mitigation
The TYPO3 project has released patched versions: 9.5.48 ELTS, 10.4.45 ELTS, 11.5.37 LTS, 12.4.15 LTS, and 13.1.1. The fix ensures that all file properties are properly encoded before being output, as demonstrated in the security commit that updated the ShowImageController to escape attributes like title, alt, and src [3][4]. Users are strongly advised to update to a patched version immediately.
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.48 | 9.5.48 |
typo3/cms-corePackagist | >= 10.0.0, < 10.4.45 | 10.4.45 |
typo3/cms-corePackagist | >= 11.0.0, < 11.5.37 | 11.5.37 |
typo3/cms-corePackagist | >= 12.0.0, < 12.4.15 | 12.4.15 |
typo3/cms-corePackagist | >= 13.0.0, < 13.1.1 | 13.1.1 |
Affected products
2Patches
3b31d05d1da3e[SECURITY] Encode all file properties in tx_cms_showpic output
2 files changed · +34 −26
typo3/sysext/frontend/Classes/Controller/ShowImageController.php+9 −14 modified@@ -106,11 +106,6 @@ class ShowImageController </html> EOF; - /** - * @var string - */ - protected $imageTag = '<img src="###publicUrl###" alt="###alt###" title="###title###" width="###width###" height="###height###" />'; - /** * Init function, setting the input vars in the global space. * @@ -166,17 +161,17 @@ public function initialize() public function main() { $processedImage = $this->processImage(); - $imageTagMarkers = [ - '###publicUrl###' => htmlspecialchars($processedImage->getPublicUrl() ?? ''), - '###alt###' => htmlspecialchars($this->file->getProperty('alternative') ?: $this->title), - '###title###' => htmlspecialchars($this->file->getProperty('title') ?: $this->title), - '###width###' => $processedImage->getProperty('width'), - '###height###' => $processedImage->getProperty('height'), + $imageAttributes = [ + 'src' => $processedImage->getPublicUrl() ?? '', + 'alt' => $this->file->getProperty('alternative') ?: $this->title, + 'title' => $this->file->getProperty('title') ?: $this->title, + 'width' => (string)$processedImage->getProperty('width'), + 'height' => (string)$processedImage->getProperty('height'), ]; - $this->imageTag = str_replace(array_keys($imageTagMarkers), array_values($imageTagMarkers), $this->imageTag); + $markerArray = [ - '###TITLE###' => $this->file->getProperty('title') ?: $this->title, - '###IMAGE###' => $this->imageTag, + '###TITLE###' => htmlspecialchars($this->file->getProperty('title') ?: $this->title), + '###IMAGE###' => sprintf('<img %s>', GeneralUtility::implodeAttributes($imageAttributes, true)), '###BODY###' => $this->bodyTag, ];
typo3/sysext/frontend/Tests/Functional/Controller/ShowImageControllerTest.php+25 −12 modified@@ -96,12 +96,14 @@ public static function contentIsGeneratedForLocalFilesDataProvider(): \Generator public function contentIsGeneratedForLocalFiles(int $fileId, array $queryParams): void { $storageDriver = 'Local'; + $expectedSrc = '/fileadmin/local-file/' . $fileId . '?&test=""'; + $expectedTitle = '</title></head></html><!-- "fileProperty::title" -->'; $this->storage->expects(self::atLeastOnce()) ->method('getDriverType') ->willReturn($storageDriver); $file = $this->buildFile('/local-file/' . $fileId, $this->storage); - $processedFile = $this->buildProcessedFile('/fileadmin/local-file/' . $fileId); + $processedFile = $this->buildProcessedFile($expectedSrc); $this->resourceFactory->expects(self::atLeastOnce()) ->method('getFileObject') ->with($fileId) @@ -112,16 +114,17 @@ public function contentIsGeneratedForLocalFiles(int $fileId, array $queryParams) $request = $this->buildRequest($queryParams); $response = $this->subject->processRequest($request); - $document = (new HTML5())->loadHTML((string)$response->getBody()); + $responseBody = (string)$response->getBody(); + $document = (new HTML5())->loadHTML($responseBody); $titles = $document->getElementsByTagName('title'); $images = $document->getElementsByTagName('img'); - self::assertSame('fileProperty::title', $titles->item(0)->nodeValue); - self::assertSame('/fileadmin/local-file/13', $images->item(0)->getAttribute('src')); - self::assertSame('fileProperty::alternative', $images->item(0)->getAttribute('alt')); - self::assertSame('fileProperty::title', $images->item(0)->getAttribute('title')); - self::assertSame('processedProperty::width', $images->item(0)->getAttribute('width')); - self::assertSame('processedProperty::height', $images->item(0)->getAttribute('height')); + self::assertSame($expectedTitle, $titles->item(0)->nodeValue); + self::assertSame($expectedSrc, $images->item(0)->getAttribute('src')); + self::assertSame($expectedTitle, $images->item(0)->getAttribute('title')); + self::assertSame('<!-- "fileProperty::alternative" -->', $images->item(0)->getAttribute('alt')); + self::assertSame('<!-- "processedProperty::width" -->', $images->item(0)->getAttribute('width')); + self::assertSame('<!-- "processedProperty::height" -->', $images->item(0)->getAttribute('height')); } /** @@ -144,7 +147,12 @@ private function buildFile(string $identifier, ResourceStorage $storage): FileIn $file->method('getIdentifier') ->willReturn($identifier); $file->method('getProperty') - ->willReturnCallback($this->buildRoundTripClosure('fileProperty')); + ->willReturnCallback( + $this->buildRoundTripClosure( + 'fileProperty', + ['title' => '</title></head></html>'] + ) + ); return $file; } @@ -165,10 +173,15 @@ private function buildProcessedFile(string $publicUrl): ProcessedFile&MockObject return $processedFile; } - private function buildRoundTripClosure(string $prefix): \Closure + private function buildRoundTripClosure(string $prefix, array $prependMap = []): \Closure { - return static function (string ...$args) use ($prefix): string { - return sprintf('%s::%s', $prefix, implode(',', $args)); + return static function (string $name) use ($prefix, $prependMap): string { + return sprintf( + '%s<!-- "%s::%s" -->', + $prependMap[$name] ?? '', + $prefix, + $name + ); }; } }
d77464238135[SECURITY] Encode all file properties in tx_cms_showpic output
2 files changed · +28 −15
typo3/sysext/frontend/Classes/Controller/ShowImageController.php+3 −3 modified@@ -168,12 +168,12 @@ public function main() '###publicUrl###' => htmlspecialchars($processedImage->getPublicUrl() ?? ''), '###alt###' => htmlspecialchars($this->file->getProperty('alternative') ?: $this->title), '###title###' => htmlspecialchars($this->file->getProperty('title') ?: $this->title), - '###width###' => $processedImage->getProperty('width'), - '###height###' => $processedImage->getProperty('height'), + '###width###' => htmlspecialchars((string)$processedImage->getProperty('width')), + '###height###' => htmlspecialchars((string)$processedImage->getProperty('height')), ]; $this->imageTag = str_replace(array_keys($imageTagMarkers), array_values($imageTagMarkers), $this->imageTag); $markerArray = [ - '###TITLE###' => $this->file->getProperty('title') ?: $this->title, + '###TITLE###' => htmlspecialchars($this->file->getProperty('title') ?: $this->title), '###IMAGE###' => $this->imageTag, '###BODY###' => $this->bodyTag, ];
typo3/sysext/frontend/Tests/Functional/Controller/ShowImageControllerTest.php+25 −12 modified@@ -93,12 +93,14 @@ public static function contentIsGeneratedForLocalFilesDataProvider(): \Generator public function contentIsGeneratedForLocalFiles(int $fileId, array $queryParams): void { $storageDriver = 'Local'; + $expectedSrc = '/fileadmin/local-file/' . $fileId . '?&test=""'; + $expectedTitle = '</title></head></html><!-- "fileProperty::title" -->'; $this->storage->expects(self::atLeastOnce()) ->method('getDriverType') ->willReturn($storageDriver); $file = $this->buildFile('/local-file/' . $fileId, $this->storage); - $processedFile = $this->buildProcessedFile('/fileadmin/local-file/' . $fileId); + $processedFile = $this->buildProcessedFile($expectedSrc); $this->resourceFactory->expects(self::atLeastOnce()) ->method('getFileObject') ->with($fileId) @@ -109,16 +111,17 @@ public function contentIsGeneratedForLocalFiles(int $fileId, array $queryParams) $request = $this->buildRequest($queryParams); $response = $this->subject->processRequest($request); - $document = (new HTML5())->loadHTML((string)$response->getBody()); + $responseBody = (string)$response->getBody(); + $document = (new HTML5())->loadHTML($responseBody); $titles = $document->getElementsByTagName('title'); $images = $document->getElementsByTagName('img'); - self::assertSame('fileProperty::title', $titles->item(0)->nodeValue); - self::assertSame('/fileadmin/local-file/13', $images->item(0)->getAttribute('src')); - self::assertSame('fileProperty::alternative', $images->item(0)->getAttribute('alt')); - self::assertSame('fileProperty::title', $images->item(0)->getAttribute('title')); - self::assertSame('processedProperty::width', $images->item(0)->getAttribute('width')); - self::assertSame('processedProperty::height', $images->item(0)->getAttribute('height')); + self::assertSame($expectedTitle, $titles->item(0)->nodeValue); + self::assertSame($expectedSrc, $images->item(0)->getAttribute('src')); + self::assertSame($expectedTitle, $images->item(0)->getAttribute('title')); + self::assertSame('<!-- "fileProperty::alternative" -->', $images->item(0)->getAttribute('alt')); + self::assertSame('<!-- "processedProperty::width" -->', $images->item(0)->getAttribute('width')); + self::assertSame('<!-- "processedProperty::height" -->', $images->item(0)->getAttribute('height')); } /** @@ -141,7 +144,12 @@ private function buildFile(string $identifier, ResourceStorage $storage): FileIn $file->method('getIdentifier') ->willReturn($identifier); $file->method('getProperty') - ->willReturnCallback($this->buildRoundTripClosure('fileProperty')); + ->willReturnCallback( + $this->buildRoundTripClosure( + 'fileProperty', + ['title' => '</title></head></html>'] + ) + ); return $file; } @@ -162,10 +170,15 @@ private function buildProcessedFile(string $publicUrl): ProcessedFile&MockObject return $processedFile; } - private function buildRoundTripClosure(string $prefix): \Closure + private function buildRoundTripClosure(string $prefix, array $prependMap = []): \Closure { - return static function (string ...$args) use ($prefix): string { - return sprintf('%s::%s', $prefix, implode(',', $args)); + return static function (string $name) use ($prefix, $prependMap): string { + return sprintf( + '%s<!-- "%s::%s" -->', + $prependMap[$name] ?? '', + $prefix, + $name + ); }; } }
376474904f6b[SECURITY] Encode all file properties in tx_cms_showpic output
1 file changed · +3 −3
typo3/sysext/frontend/Classes/Controller/ShowImageController.php+3 −3 modified@@ -166,12 +166,12 @@ public function main() '###publicUrl###' => htmlspecialchars($processedImage->getPublicUrl() ?? ''), '###alt###' => htmlspecialchars($this->file->getProperty('alternative') ?: $this->title), '###title###' => htmlspecialchars($this->file->getProperty('title') ?: $this->title), - '###width###' => $processedImage->getProperty('width'), - '###height###' => $processedImage->getProperty('height'), + '###width###' => htmlspecialchars((string)$processedImage->getProperty('width')), + '###height###' => htmlspecialchars((string)$processedImage->getProperty('height')), ]; $this->imageTag = str_replace(array_keys($imageTagMarkers), array_values($imageTagMarkers), $this->imageTag); $markerArray = [ - '###TITLE###' => $this->file->getProperty('title') ?: $this->title, + '###TITLE###' => htmlspecialchars($this->file->getProperty('title') ?: $this->title), '###IMAGE###' => $this->imageTag, '###BODY###' => $this->bodyTag, ];
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-hw6c-6gwq-3m3mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-34357ghsaADVISORY
- github.com/TYPO3/typo3/commit/376474904f6b9a54dc1b785a2e45277cbd13b0d7ghsax_refsource_MISCWEB
- github.com/TYPO3/typo3/commit/b31d05d1da3eeaeead2d19eb43b1c3f9c88e15eeghsax_refsource_MISCWEB
- github.com/TYPO3/typo3/commit/d774642381354d3bf5095a5a26e18acd2767f0b1ghsax_refsource_MISCWEB
- github.com/TYPO3/typo3/security/advisories/GHSA-hw6c-6gwq-3m3mghsax_refsource_CONFIRMWEB
- typo3.org/security/advisory/typo3-core-sa-2024-009ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.