Blog post

TYPO3 9.5.7: Overriding the Database to Execute Code

Robin Peraglie photo

Robin Peraglie

Vulnerability Researcher

Date

  • Security
In this technical blog post we examine a critical vulnerability in the core of the TYPO3 CMS (CVE-2019-12747). A reliable exploit allows the execution of arbitrary PHP code on the underly...

In this technical blog post we examine a critical vulnerability in the core of the TYPO3 CMS (CVE-2019-12747). A reliable exploit allows the execution of arbitrary PHP code on the underlying system as authenticated user. Affected are TYPO3 8.x through 8.7.26, and TYPO3 9.x through 9.5.7. A deserialization of untrusted data leads to a Remote Code Execution vulnerability, which can be combined with a Cross-Site Scripting vulnerability that was also detected in the backend (CVE-2019-12748).

Overriding the Database with a Payload

The vulnerability occurs when saving any form in the backend section of TYPO3. If a user modifies the pages section, for example, the data to be edited is fetched from the SQL database of TYPO3 and written back to the database. After fetching the data from the database, the application logic allows overriding single columns of the fetched data with user input. This feature allows a malicious authenticated backend user to override database values containing serialized data which are later deserialized. This leads to a PHP Object Injection that allows an attacker to remotely execute code (CVE-2019-12747).

TYPO3 9.5.7: Overriding the Database to Execute Code

Technical Details

When saving a backend form in TYPO3, the compile() method of the $formDataCompiler object is called. The argument is an array which is populated with user input, as seen in line 1263 of the following listing.

typo3/sysext/backend/Classes/Controller/EditDocumentController.php

1259    $this->overrideVals = $parsedBody['overrideVals'] ?? 
1260    $queryParams['overrideVals'] ?? null;
1261    // ... 
1262    $formDataCompilerInput['overrideValues'] = $this->overrideVals[$table];
1263    $formData = $formDataCompiler->compile($formDataCompilerInput);

The method makes use of a for loop iterating over an ordered list of FormDataProvider objects and invokes the addData() method on each $provider object in sequence.

typo3/sysext/backend/Classes/Form/FormDataGroup/OrderedProviderList.php

59    public function compile(array $result): array
60    {
61        // ...
62        foreach ($orderedDataProvider as $providerClassName => $providerConfig) {
63            // ...
64            $result = $provider->addData($result);
65        }
66        return $result;
67    }

As you can see in the source code listing on line 64, the $result of the previous $provider->addData() invocation is used as an argument in the next addData() call. On each iteration, the $result variable which represents an array is modified after the $provider has processed its contents. One of those providers is an instance of the DatabaseRecordOverrideValues class which allows to override the data fetched from the database stored under the databaseRow key of the $result array.

typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRecordOverrideValues.php

31    public function addData(array $result)
32    {
33        foreach ($result['overrideValues'] as $fieldName => $fieldValue) {
34            if (isset($result['processedTca']['columns'][$fieldName])) {
35                $result['databaseRow'][$fieldName] = $fieldValue;
36                // ...
37            }
38        }
39        return $result;
40    }

Finally, one of the following FormDataProvider objects implements an unserialize() call on the overridden data, leading to the vulnerability:

typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseLanguageRows.php

62    public function addData(array $result)
63    {
64        // ...
65        if (/*...*/) 
66        {
67            $result['defaultLanguageDiffRow'][$defaultLanguageKey] = unserialize(
68                $result['databaseRow'][$result['processedTca']['ctrl']
69                ['transOrigDiffSourceField']]);

You can find more information on how to exploit PHP Object Injections with gadget chains in our blogpost.

Stored Cross-Site Scripting in Backend

A Cross-Site Scripting vulnerability exists in the TYPO3 backend (CVE-2019-12748). An unprivileged user who has access to the Site Redirects section can inject a malicious URL which makes use of the t3:// pseudo protocol.

t3://url/?url=javascript:alert(1);

By adding this specific site redirect another benign higher-privileged TYPO3 user can be tricked into clicking the link which triggers the malicious JavaScript. He could use this vulnerability as a pivot point to launch the remote code execution exploit from above.

TYPO3 prevents a user to make use of the dangerous javascript: pseudo protocol in links and URLs provided by the user which would mean the direct execution of JavaScript. However, it does not prevent a user to make use of TYPO3’s builtin t3:// pseudo protocol which implements multiple functionalities like referencing TYPO3 internal pages, files, email addresses or URLs. In fact, specifying a URL which is automatically translated into a clickable link bypasses the whitelist of TYPO3 which initially prevented the javascript: pseudo protocol.

Timetable

DateWhat
09 May 2019Sent vendor vulnerability details
09 May 2019Vendor acknowledged
10 May 2019Coordination with security lead of vendor on fixing the issue
06 June 2019Vendor informs us about detailed release plans 25 June 2019
25 June 2019TYPO3 9.5.8 patch released

Summary

The presented vulnerabilities can have critical impact on any TYPO3 system with one or more TYPO3 backend users. An authenticated backend user with access to the Pages section can execute code on the underlying remote system. He could use the Cross-Site Scripting vulnerability in the Site Redirects module as a pivoting point to exploit this vulnerability.