CVE-2018-14716 (published 2018-08-06T20:29Z)

A Server Side Template Injection (SSTI) was discovered in the SEOmatic plugin before 3.1.4 for Craft CMS, because requests that don't match any elements incorrectly generate the canonicalUrl, and can lead to execution of Twig code.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-94
cwe_name: Improper Control of Generation of Code ('Code Injection')
description: The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
extended_description: 
            When a product allows a user's input to contain code syntax, it might be possible for an attacker to craft the code in such a way that it will alter the intended control flow of the product. Such an alteration could lead to arbitrary code execution.
            Injection problems encompass a wide variety of issues -- all mitigated in very different ways. For this reason, the most effective way to discuss these weaknesses is to note the distinct features which classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed. The most classic instantiations of this category of weakness are SQL injection and format string vulnerabilities.
         
         

Fix information:
repo_url: https://github.com/nystudio107/craft-seomatic
commit_details: repo_url: https://github.com/nystudio107/craft-seomatic
author: Andrew Welch
author_date: 2018-07-23 18:19:43-04:00
msg: Changed the way requests that don't match any elements generate the `canonicalUrl`, to avoid potentially executing injected Twig code

Signed-off-by: Andrew Welch <andrew@nystudio107.com>
repository: repo_url: https://github.com/nystudio107/craft-seomatic
repo_name: nystudio107/craft-seomatic
description: SEOmatic facilitates modern SEO best practices & implementation for Craft CMS 3. It is a turnkey SEO system that is comprehensive, powerful, and flexible.
repo_language: PHP
owner: nystudio107
file_changes: filename: DynamicMeta.php
change_type: ModificationType.MODIFY
diff: @@ -500,6 +500,12 @@ class DynamicMeta
                     Craft::error($e->getMessage(), __METHOD__);
                 }
             }
+            // Strip any query string params, and make sure we have an absolute URL with protocol
+            if ($urlParams === null) {
+                $url = UrlHelper::stripQueryString($url);
+            }
+            $url = UrlHelper::absoluteUrlWithProtocol($url);
+
             $url = $url ?? '';
             $language = $site->language;
             $ogLanguage = str_replace('-', '_', $language);

programming_language: PHP
filename: MetaGlobalVars.php
change_type: ModificationType.MODIFY
diff: @@ -199,6 +199,20 @@ class MetaGlobalVars extends VarsModel
         parent::__construct($config);
     }
 
+    /**
+     * @inheritdoc
+     */
+    public function init()
+    {
+        parent::init();
+        // If we have potentially unsafe Twig code, strip it out
+        if (!empty($this->canonicalUrl)) {
+            if (strpos($this->canonicalUrl, 'craft.app.request.pathInfo') !== false) {
+                $this->canonicalUrl = '{seomatic.helper.safeCanonicalUrl()}';
+            }
+        }
+    }
+
     /**
      * @inheritdoc
      */

programming_language: PHP
filename: GlobalVars.php
change_type: ModificationType.MODIFY
diff: @@ -26,7 +26,7 @@ return [
         'seoImageWidth'           => '',
         'seoImageHeight'          => '',
         'seoImageDescription'     => '',
-        'canonicalUrl'            => '{{ craft.app.request.pathInfo | striptags }}',
+        'canonicalUrl'            => '{seomatic.helper.safeCanonicalUrl()}',
         'robots'                  => 'all',
         'ogType'                  => 'website',
         'ogTitle'                 => '{seomatic.meta.seoTitle}',

programming_language: PHP
filename: Helper.php
change_type: ModificationType.MODIFY
diff: @@ -11,20 +11,23 @@
 
 namespace nystudio107\seomatic\services;
 
+use nystudio107\seomatic\helpers\UrlHelper;
 use nystudio107\seomatic\Seomatic;
 use nystudio107\seomatic\helpers\DynamicMeta as DynamicMetaHelper;
 use nystudio107\seomatic\helpers\ImageTransform as ImageTransformHelper;
 use nystudio107\seomatic\helpers\Schema as SchemaHelper;
 use nystudio107\seomatic\helpers\Text as TextHelper;
 
+use Craft;
 use craft\base\Component;
 use craft\elements\Asset;
 use craft\elements\db\MatrixBlockQuery;
 use craft\elements\db\TagQuery;
 use craft\helpers\Template;
-use craft\helpers\UrlHelper;
 use craft\web\twig\variables\Paginate;
 
+use yii\base\InvalidConfigException;
+
 /**
  * @author    nystudio107
  * @package   Seomatic
@@ -38,9 +41,28 @@ class Helper extends Component
     // Public Methods
     // =========================================================================
 
+    /**
+     * Return the canonical URL for the request, with the query string stripped
+     *
+     * @return string
+     */
+    public static function safeCanonicalUrl(): string
+    {
+        $url = '';
+        try {
+            $url = Craft::$app->getRequest()->getPathInfo();
+        } catch (InvalidConfigException $e) {
+            Craft::error($e->getMessage(), __METHOD__);
+        }
+        $url = UrlHelper::stripQueryString($url);
+
+        return UrlHelper::absoluteUrlWithProtocol($url);
+    }
+
     /**
      * Paginate based on the passed in Paginate variable as returned from the
-     * Twig {% paginate %} tag: https://docs.craftcms.com/v3/templating/tags/paginate.html#the-pageInfo-variable
+     * Twig {% paginate %} tag:
+     * https://docs.craftcms.com/v3/templating/tags/paginate.html#the-pageInfo-variable
      *
      * @param Paginate $pageInfo
      */
@@ -86,8 +108,8 @@ class Helper extends Component
      * Return a list of localized URLs that are in the current site's group
      * The current URI is used if $uri is null. Similarly, the current site is
      * used if $siteId is null.
-     * The resulting array of arrays has `id`, `language`, `ogLanguage`, `hreflangLanguage`,
-     * and `url` as keys.
+     * The resulting array of arrays has `id`, `language`, `ogLanguage`,
+     * `hreflangLanguage`, and `url` as keys.
      *
      * @param string|null $uri
      * @param int|null    $siteId
@@ -131,6 +153,7 @@ class Helper extends Component
             .$inlineStr
             .'/'
             .$fileName;
+
         return Template::raw(UrlHelper::siteUrl($seoFileLink));
     }
 
@@ -238,6 +261,30 @@ class Helper extends Component
         return TextHelper::extractSummary($text, $useStopWords);
     }
 
+    /**
+     * Return a flattened, indented menu of the given $path
+     *
+     * @param string $path
+     *
+     * @return array
+     */
+    public static function getTypeMenu($path): array
+    {
+        return SchemaHelper::getTypeMenu($path);
+    }
+
+    /**
+     * Return a single menu of schemas starting at $path
+     *
+     * @param string $path
+     *
+     * @return array
+     */
+    public static function getSingleTypeMenu($path): array
+    {
+        return SchemaHelper::getSingleTypeMenu($path);
+    }
+
     /**
      * Transform the $asset for social media sites in $transformName and
      * optional $siteId
@@ -282,28 +329,4 @@ class Helper extends Component
     {
         return ImageTransformHelper::socialTransformHeight($asset, $transformName, $siteId);
     }
-
-    /**
-     * Return a flattened, indented menu of the given $path
-     *
-     * @param string $path
-     *
-     * @return array
-     */
-    public static function getTypeMenu($path): array
-    {
-        return SchemaHelper::getTypeMenu($path);
-    }
-
-    /**
-     * Return a single menu of schemas starting at $path
-     *
-     * @param string $path
-     *
-     * @return array
-     */
-    public static function getSingleTypeMenu($path): array
-    {
-        return SchemaHelper::getSingleTypeMenu($path);
-    }
 }

programming_language: PHP