[TASK] Enable PharMetaDataInterceptor 60/62760/3
authorOliver Hader <oliver@typo3.org>
Tue, 24 Dec 2019 11:48:04 +0000 (12:48 +0100)
committerSusanne Moog <look@susi.dev>
Thu, 16 Jan 2020 15:17:58 +0000 (16:17 +0100)
Enable experimental checking of serialized Phar meta-data against
PHP objects. This would consider a Phar archive malicious in case
not only scalar values are found. A custom low-level Phar\Reader
is used in order to avoid using PHP's Phar object which would
trigger the initial vulnerability.

Resolves: #90010
Releases: master
Change-Id: Ifda811fab44bdbb8f4858d18e392e0f321dbf1be
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/62760
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Susanne Moog <look@susi.dev>
Reviewed-by: Benni Mack <benni@typo3.org>
Reviewed-by: Susanne Moog <look@susi.dev>
typo3/sysext/core/Classes/Core/Bootstrap.php
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_resources/compromised.phar [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/IO/PharStreamWrapperInterceptorTest.php

index db36a23..4983df5 100644 (file)
@@ -40,6 +40,8 @@ use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\PharStreamWrapper\Behavior;
+use TYPO3\PharStreamWrapper\Interceptor\ConjunctionInterceptor;
+use TYPO3\PharStreamWrapper\Interceptor\PharMetaDataInterceptor;
 use TYPO3\PharStreamWrapper\Manager;
 use TYPO3\PharStreamWrapper\PharStreamWrapper;
 
@@ -425,7 +427,10 @@ class Bootstrap
             Manager::destroy();
             Manager::initialize(
                 (new Behavior())
-                    ->withAssertion(new PharStreamWrapperInterceptor())
+                    ->withAssertion(new ConjunctionInterceptor([
+                        new PharStreamWrapperInterceptor(),
+                        new PharMetaDataInterceptor()
+                    ]))
             );
 
             stream_wrapper_unregister('phar');
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_resources/compromised.phar b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_resources/compromised.phar
new file mode 100644 (file)
index 0000000..4a730ae
Binary files /dev/null and b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_resources/compromised.phar differ
index b1f568f..0a3fb29 100644 (file)
@@ -140,7 +140,7 @@ class PharStreamWrapperInterceptorTest extends FunctionalTestCase
     public function directoryActionDeniesInvocation(string $path)
     {
         self::expectException(Exception::class);
-        self::expectExceptionCode(1530103998);
+        self::expectExceptionCode(1539625084);
 
         $path = $this->instancePath . '/' . $path;
         opendir('phar://' . $path);
@@ -277,7 +277,7 @@ class PharStreamWrapperInterceptorTest extends FunctionalTestCase
     public function urlStatDeniesInvocation(string $functionName, string $path)
     {
         self::expectException(Exception::class);
-        self::expectExceptionCode(1530103998);
+        self::expectExceptionCode(1539625084);
 
         $path = $this->instancePath . '/' . $path;
         call_user_func($functionName, 'phar://' . $path);
@@ -358,7 +358,7 @@ class PharStreamWrapperInterceptorTest extends FunctionalTestCase
     public function streamOpenDeniesInvocationForFileOpen()
     {
         self::expectException(Exception::class);
-        self::expectExceptionCode(1530103998);
+        self::expectExceptionCode(1539625084);
 
         $allowedPath = $this->instancePath . '/fileadmin/bundle.phar';
         fopen('phar://' . $allowedPath . '/Resources/content.txt', 'r');
@@ -370,21 +370,30 @@ class PharStreamWrapperInterceptorTest extends FunctionalTestCase
     public function streamOpenDeniesInvocationForFileGetContents()
     {
         self::expectException(Exception::class);
-        self::expectExceptionCode(1530103998);
+        self::expectExceptionCode(1539625084);
 
         $allowedPath = $this->instancePath . '/fileadmin/bundle.phar';
         file_get_contents('phar://' . $allowedPath . '/Resources/content.txt');
     }
 
+    public function streamOpenDeniesInvocationForIncludeDataProvider(): array
+    {
+        return [
+            'fileadmin/bundle.phar' => ['fileadmin/bundle.phar'],
+            'EXT:test_resources/compromised.phar' => ['typo3conf/ext/test_resources/compromised.phar'],
+        ];
+    }
+
     /**
      * @test
+     * @dataProvider streamOpenDeniesInvocationForIncludeDataProvider
      */
-    public function streamOpenDeniesInvocationForInclude()
+    public function streamOpenDeniesInvocationForInclude(string $path)
     {
         self::expectException(Exception::class);
-        self::expectExceptionCode(1530103998);
+        self::expectExceptionCode(1539625084);
 
-        $allowedPath = $this->instancePath . '/fileadmin/bundle.phar';
+        $allowedPath = $this->instancePath . '/' . $path;
         include('phar://' . $allowedPath . '/Classes/Domain/Model/DemoModel.php');
     }
 }