VYPR
Medium severityNVD Advisory· Published Jun 9, 2026· Updated Jun 9, 2026

CVE-2026-47350

CVE-2026-47350

Description

TYPO3 CMS allows backend users to move records without edit permissions on the source page, potentially leading to unauthorized data manipulation.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

TYPO3 CMS allows backend users to move records without edit permissions on the source page, potentially leading to unauthorized data manipulation.

Vulnerability

Backend users could move records to a different page without possessing edit permissions on the source page. This vulnerability exists in the DataHandler component of TYPO3 CMS. Affected versions include 13.0.0-13.4.31 and 14.0.0-14.3.3 [3].

Exploitation

An attacker with backend user privileges can exploit this vulnerability by initiating a record move operation. The attacker needs to be authenticated as a backend user and target a record for relocation to a different page. The vulnerability is triggered when the user lacks the necessary edit permissions on the original page where the record resides [3].

Impact

Successful exploitation allows an attacker to move records to an arbitrary page without proper authorization. This can lead to unauthorized data placement, potentially disrupting data integrity or making records inaccessible to legitimate users. The impact is a loss of integrity and availability for the affected records [3].

Mitigation

Update to TYPO3 versions 13.4.31 LTS or 14.3.3 LTS, which address this issue. The fix was released on June 9, 2026 [3].

AI Insight generated on Jun 9, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • TYPO3/Typo3references2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: 13.0.0-13.4.31, 14.0.0-14.3.3

Patches

2
c9898d2e6760

[SECURITY] Prevent unauthorized record move via DataHandler

https://github.com/TYPO3/typo3Torben HansenJun 9, 2026via github-commit-search
3 files changed · +91 0
  • typo3/sysext/core/Classes/DataHandling/DataHandler.php+5 0 modified
    @@ -4489,6 +4489,11 @@ public function moveRecord(string $table, int $uid, int $destination): void
                 }
             } else {
                 if ($isMovingToDifferentPid) {
    +                if (!$this->hasPermissionToUpdate($table, $sourcePageRecord)) {
    +                    // When record is moved to different target, update permissions on source page are needed
    +                    $this->log($table, $sourceUid, SystemLogDatabaseAction::MOVE, null, SystemLogErrorClassification::USER_ERROR, 'Attempt to move record {table}:{uid} to pid "{targetPid}" without having permissions to update the source page (uid={sourcePid})', null, ['table' => $table, 'uid' => $sourceUid, 'targetPid' => $updateFields['pid'], 'sourcePid' => $sourcePid], $sourcePid);
    +                    return;
    +                }
                     if (!$this->hasPermissionToInsert($table, $updateFields['pid'], $targetPageRecord)) {
                         // When record is moved to different page, insert permissions on target are needed
                         $this->log($table, $sourceUid, SystemLogDatabaseAction::MOVE, null, SystemLogErrorClassification::USER_ERROR, 'Attempt to move record {table}:{uid} to pid "{targetPid}" without having permissions to insert', null, ['table' => $table, 'uid' => $sourceUid, 'targetPid' => $updateFields['pid']], $updateFields['pid']);
    
  • typo3/sysext/core/Tests/Functional/DataHandling/DataHandler/DataSet/MoveRecord.csv+17 0 added
    @@ -0,0 +1,17 @@
    +"pages"
    +,"uid","pid","sorting","title","deleted","perms_everybody"
    +,1,0,128,"Root 1",0,31
    +,2,1,128,"Page 1",0,31
    +,3,1,128,"Page 2",0,31
    +,4,0,128,"Root 2",0,31
    +,5,4,128,"Page 1",0,31
    +tt_content,,,,,,,,
    +,uid,pid,header,sys_language_uid,l18n_parent,assets,CType
    +,1,2,"A Header on page uid 2",0,0,0,"header"
    +,2,5,"A Header on page uid 4",0,0,0,"header"
    +be_groups,,,,,,,,,
    +,uid,pid,title,deleted,db_mountpoints,tables_select,tables_modify,non_exclude_fields,explicit_allowdeny
    +,9,0,"editors",0,1,"pages,tt_content","pages,tt_content","pages:title,tt_content:header,tt_content:CType","tt_content:CType:header"
    +"be_users",,,,,,,
    +,"uid","pid","username","usergroup","deleted","admin","options"
    +,9,0,"editor",9,0,0,3
    
  • typo3/sysext/core/Tests/Functional/DataHandling/DataHandler/MoveActionTest.php+69 0 added
    @@ -0,0 +1,69 @@
    +<?php
    +
    +declare(strict_types=1);
    +
    +/*
    + * This file is part of the TYPO3 CMS project.
    + *
    + * It is free software; you can redistribute it and/or modify it under
    + * the terms of the GNU General Public License, either version 2
    + * of the License, or any later version.
    + *
    + * For the full copyright and license information, please read the
    + * LICENSE.txt file that was distributed with this source code.
    + *
    + * The TYPO3 project - inspiring people to share!
    + */
    +
    +namespace TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler;
    +
    +use PHPUnit\Framework\Attributes\Test;
    +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
    +use TYPO3\CMS\Core\DataHandling\DataHandler;
    +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
    +
    +final class MoveActionTest extends FunctionalTestCase
    +{
    +    private BackendUserAuthentication $backendUser;
    +    private DataHandler $subject;
    +
    +    protected function setUp(): void
    +    {
    +        parent::setUp();
    +
    +        $this->importCSVDataSet(__DIR__ . '/DataSet/MoveRecord.csv');
    +
    +        $this->backendUser = $this->setUpBackendUser(9);
    +        $this->backendUser->setWebmounts([1]);
    +
    +        $this->subject = $this->get(DataHandler::class);
    +        $this->subject->start([], []);
    +    }
    +
    +    #[Test]
    +    public function moveRecordOfAccessibleRecordIsPermitted(): void
    +    {
    +        $recordUid = 1;
    +        $targetPid = 3;
    +        $this->subject->moveRecord('tt_content', $recordUid, $targetPid);
    +        self::assertEmpty($this->subject->errorLog);
    +    }
    +
    +    #[Test]
    +    public function moveRecordOfNonAccessibleRecordIsDenied(): void
    +    {
    +        $recordUid = 2;
    +        $sourcePid = 5;
    +        $targetPid = 2;
    +        $this->subject->moveRecord('tt_content', $recordUid, $targetPid);
    +        self::assertStringContainsString(
    +            sprintf(
    +                'Attempt to move record tt_content:%d to pid "%d" without having permissions to update the source page (uid=%d)',
    +                $recordUid,
    +                $targetPid,
    +                $sourcePid,
    +            ),
    +            $this->subject->errorLog[0]
    +        );
    +    }
    +}
    
195356996a60

[SECURITY] Prevent unauthorized record move via DataHandler

https://github.com/TYPO3/typo3Torben HansenJun 9, 2026via nvd-ref
3 files changed · +93 0
  • typo3/sysext/core/Classes/DataHandling/DataHandler.php+5 0 modified
    @@ -4591,6 +4591,11 @@ public function moveRecord(string $table, $uid, $destination): void
                 }
             } else {
                 if ($isMovingToDifferentPid) {
    +                if (!$this->hasPermissionToUpdate($table, $sourcePageRecord)) {
    +                    // When record is moved to different target, update permissions on source page are needed
    +                    $this->log($table, $sourceUid, SystemLogDatabaseAction::MOVE, null, SystemLogErrorClassification::USER_ERROR, 'Attempt to move record {table}:{uid} to pid "{targetPid}" without having permissions to update the source page (uid={sourcePid})', null, ['table' => $table, 'uid' => $sourceUid, 'targetPid' => $updateFields['pid'], 'sourcePid' => $sourcePid], $sourcePid);
    +                    return;
    +                }
                     if (!$this->hasPermissionToInsert($table, $updateFields['pid'], $targetPageRecord)) {
                         // When record is moved to different page, insert permissions on target are needed
                         $this->log($table, $sourceUid, SystemLogDatabaseAction::MOVE, null, SystemLogErrorClassification::USER_ERROR, 'Attempt to move record {table}:{uid} to pid "{targetPid}" without having permissions to insert', null, ['table' => $table, 'uid' => $sourceUid, 'targetPid' => $updateFields['pid']], $updateFields['pid']);
    
  • typo3/sysext/core/Tests/Functional/DataHandling/DataHandler/DataSet/MoveRecord.csv+17 0 added
    @@ -0,0 +1,17 @@
    +"pages"
    +,"uid","pid","sorting","title","deleted","perms_everybody"
    +,1,0,128,"Root 1",0,31
    +,2,1,128,"Page 1",0,31
    +,3,1,128,"Page 2",0,31
    +,4,0,128,"Root 2",0,31
    +,5,4,128,"Page 1",0,31
    +tt_content,,,,,,,,
    +,uid,pid,header,sys_language_uid,l18n_parent,assets,CType
    +,1,2,"A Header on page uid 2",0,0,0,"header"
    +,2,5,"A Header on page uid 4",0,0,0,"header"
    +be_groups,,,,,,,,,
    +,uid,pid,title,deleted,db_mountpoints,tables_select,tables_modify,non_exclude_fields,explicit_allowdeny
    +,9,0,"editors",0,1,"pages,tt_content","pages,tt_content","pages:title,tt_content:header,tt_content:CType","tt_content:CType:header"
    +"be_users",,,,,,,
    +,"uid","pid","username","usergroup","deleted","admin","options"
    +,9,0,"editor",9,0,0,3
    
  • typo3/sysext/core/Tests/Functional/DataHandling/DataHandler/MoveActionTest.php+71 0 added
    @@ -0,0 +1,71 @@
    +<?php
    +
    +declare(strict_types=1);
    +
    +/*
    + * This file is part of the TYPO3 CMS project.
    + *
    + * It is free software; you can redistribute it and/or modify it under
    + * the terms of the GNU General Public License, either version 2
    + * of the License, or any later version.
    + *
    + * For the full copyright and license information, please read the
    + * LICENSE.txt file that was distributed with this source code.
    + *
    + * The TYPO3 project - inspiring people to share!
    + */
    +
    +namespace TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler;
    +
    +use PHPUnit\Framework\Attributes\Test;
    +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
    +use TYPO3\CMS\Core\DataHandling\DataHandler;
    +use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
    +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
    +
    +final class MoveActionTest extends FunctionalTestCase
    +{
    +    private BackendUserAuthentication $backendUser;
    +    private DataHandler $subject;
    +
    +    protected function setUp(): void
    +    {
    +        parent::setUp();
    +
    +        $this->importCSVDataSet(__DIR__ . '/DataSet/MoveRecord.csv');
    +
    +        $this->backendUser = $this->setUpBackendUser(9);
    +        $this->backendUser->setWebmounts([1]);
    +        $GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($this->backendUser);
    +
    +        $this->subject = $this->get(DataHandler::class);
    +        $this->subject->start([], []);
    +    }
    +
    +    #[Test]
    +    public function moveRecordOfAccessibleRecordIsPermitted(): void
    +    {
    +        $recordUid = 1;
    +        $targetPid = 3;
    +        $this->subject->moveRecord('tt_content', $recordUid, $targetPid);
    +        self::assertEmpty($this->subject->errorLog);
    +    }
    +
    +    #[Test]
    +    public function moveRecordOfNonAccessibleRecordIsDenied(): void
    +    {
    +        $recordUid = 2;
    +        $sourcePid = 5;
    +        $targetPid = 2;
    +        $this->subject->moveRecord('tt_content', $recordUid, $targetPid);
    +        self::assertStringContainsString(
    +            sprintf(
    +                'Attempt to move record tt_content:%d to pid "%d" without having permissions to update the source page (uid=%d)',
    +                $recordUid,
    +                $targetPid,
    +                $sourcePid,
    +            ),
    +            $this->subject->errorLog[0]
    +        );
    +    }
    +}
    

Vulnerability mechanics

Root cause

"The DataHandler did not properly check edit permissions on the source page when moving records to a different page."

Attack vector

A backend user with edit permissions on a target page, but not on the source page, can move records to the target page. This bypasses the intended permission checks for modifying records on their original page. The vulnerability is triggered when the `moveRecord` function in the DataHandler is called with a record and a destination page where the user lacks edit rights on the source page [ref_id=1].

Affected code

The vulnerability lies within the `moveRecord` method of the DataHandler class. Specifically, the code did not adequately check for update permissions on the source page when a record was being moved to a different parent page [ref_id=1]. The fix adds a call to `$this->hasPermissionToUpdate($table, $sourcePageRecord)` to enforce this check.

What the fix does

The patch introduces a check within the `moveRecord` function to verify if the user has update permissions on the source page before allowing the record to be moved to a different page [patch_id=5349019]. If the user lacks these permissions, an error is logged, and the move operation is aborted, preventing unauthorized record movement.

Preconditions

  • authThe attacker must be a logged-in backend user.
  • configThe attacker must have edit permissions on the target page but not on the source page of the record to be moved.

Generated on Jun 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

1