CVE-2024-28119 (published 2024-03-21T22:15Z)

Grav is an open-source, flat-file content management system. Prior to version 1.7.45, due to the unrestricted access to twig extension class from grav context, an attacker can redefine the escape function and execute arbitrary commands. Twig processing of static pages can be enabled in the front matter by any administrative user allowed to create or edit pages. As the Twig processor runs unsandboxed, this behavior can be used to gain arbitrary code execution and elevate privileges on the instance. Version 1.7.45 contains a patch for this issue.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/getgrav/grav
commit_details: repo_url: https://github.com/getgrav/grav
author: Andy Miller
author_date: 2024-03-04 15:41:30-07:00
msg: Mitigate various SSTI injections
repository: repo_url: https://github.com/getgrav/grav
repo_name: getgrav/grav
description: Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS powered by PHP, Markdown, Twig, and Symfony
repo_language: PHP
owner: getgrav
file_changes: filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -6,7 +6,8 @@
 1. [](#bugfix)
    * Fixed some multibyte issues in Inflector class [#732](https://github.com/getgrav/grav/issues/732)
    * Fallback to page modified date if Page date provided is invalid and can't be parsed [getgrav/grav-plugin-admin#2394](https://github.com/getgrav/grav-plugin-admin/issues/2394)
-   * Fixed a path traversal vulnerability with file uploads [GHSA-m7hx-hw6h-mqmc](https://github.com/getgrav/grav/security/advisories/GHSA-m7hx-hw6h-mqmc)
+   * Fixed a path traversal vulnerability with file uploads [#GHSA-m7hx-hw6h-mqmc](https://github.com/getgrav/grav/security/advisories/GHSA-m7hx-hw6h-mqmc)
+   * Fixed a security issue with insecure Twig functions be processed [#GHSA-2m7x-c7px-hp58](https://github.com/getgrav/grav/security/advisories/GHSA-2m7x-c7px-hp58) [#GHSA-r6vw-8v8r-pmp4](https://github.com/getgrav/grav/security/advisories/GHSA-r6vw-8v8r-pmp4) [#GHSA-qfv4-q44r-g7rv](https://github.com/getgrav/grav/security/advisories/GHSA-qfv4-q44r-g7rv)
 
 # v1.7.44
 ## 01/05/2024

programming_language: Markdown
filename: Security.php
change_type: ModificationType.MODIFY
diff: @@ -263,4 +263,23 @@ class Security
             'invalid_protocols' => array_map('trim', $config->get('security.xss_invalid_protocols')),
         ];
     }
+
+    public static function cleanDangerousTwig(string $string): string
+    {
+        if ($string === '') {
+            return $string;
+        }
+
+        $bad_twig = [
+            'twig_array_map',
+            'twig_array_filter',
+            'call_user_func',
+            'registerUndefinedFunctionCallback',
+            'undefined_functions',
+            'twig.getFunction',
+            'core.setEscaper',
+        ];
+        $string = preg_replace('/(({{\s*|{%\s*)[^}]*?(' . implode('|', $bad_twig) . ')[^}]*?(\s*}}|\s*%}))/i', '{# $1 #}', $string);
+        return $string;
+    }
 }

programming_language: PHP
filename: Twig.php
change_type: ModificationType.MODIFY
diff: @@ -16,6 +16,7 @@ use Grav\Common\Language\Language;
 use Grav\Common\Language\LanguageCodes;
 use Grav\Common\Page\Interfaces\PageInterface;
 use Grav\Common\Page\Pages;
+use Grav\Common\Security;
 use Grav\Common\Twig\Exception\TwigException;
 use Grav\Common\Twig\Extension\FilesystemExtension;
 use Grav\Common\Twig\Extension\GravExtension;
@@ -319,6 +320,7 @@ class Twig
     public function processPage(PageInterface $item, $content = null)
     {
         $content = $content ?? $item->content();
+        $content = Security::cleanDangerousTwig($content);
 
         // override the twig header vars for local resolution
         $this->grav->fireEvent('onTwigPageVariables', new Event(['page' => $item]));
@@ -392,6 +394,8 @@ class Twig
         $this->grav->fireEvent('onTwigStringVariables');
         $vars += $this->twig_vars;
 
+        $string = Security::cleanDangerousTwig($string);
+
         $name = '@Var:' . $string;
         $this->setTemplate($name, $string);
 
@@ -418,7 +422,7 @@ class Twig
         try {
             $grav = $this->grav;
 
-            // set the page now its been processed
+            // set the page now it's been processed
             $grav->fireEvent('onTwigSiteVariables');
 
             /** @var Pages $pages */
@@ -427,13 +431,15 @@ class Twig
             /** @var PageInterface $page */
             $page = $grav['page'];
 
+            $content = Security::cleanDangerousTwig($page->content());
+
             $twig_vars = $this->twig_vars;
             $twig_vars['theme'] = $grav['config']->get('theme');
             $twig_vars['pages'] = $pages->root();
             $twig_vars['page'] = $page;
             $twig_vars['header'] = $page->header();
             $twig_vars['media'] = $page->media();
-            $twig_vars['content'] = $page->content();
+            $twig_vars['content'] = $content;
 
             // determine if params are set, if so disable twig cache
             $params = $grav['uri']->params(null, true);
@@ -568,4 +574,5 @@ class Twig
 
         $this->autoescape = (bool) $state;
     }
+
 }

programming_language: PHP