From de1ccfa12dbcbf526104d68c1a6bc202a98698fe Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Mon, 4 Mar 2024 15:41:30 -0700 Subject: [PATCH] Mitigate various SSTI injections --- CHANGELOG.md | 3 ++- system/src/Grav/Common/Security.php | 19 +++++++++++++++++++ system/src/Grav/Common/Twig/Twig.php | 11 +++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16ac059060..cdb7a2d1bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/system/src/Grav/Common/Security.php b/system/src/Grav/Common/Security.php index 55bcde5b47..e363a28ff9 100644 --- a/system/src/Grav/Common/Security.php +++ b/system/src/Grav/Common/Security.php @@ -263,4 +263,23 @@ public static function getXssDefaults(): array '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; + } } diff --git a/system/src/Grav/Common/Twig/Twig.php b/system/src/Grav/Common/Twig/Twig.php index 14f33f0840..6e50916fb3 100644 --- a/system/src/Grav/Common/Twig/Twig.php +++ b/system/src/Grav/Common/Twig/Twig.php @@ -16,6 +16,7 @@ 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 @@ public function setTemplate($name, $template) 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 @@ public function processString($string, array $vars = []) $this->grav->fireEvent('onTwigStringVariables'); $vars += $this->twig_vars; + $string = Security::cleanDangerousTwig($string); + $name = '@Var:' . $string; $this->setTemplate($name, $string); @@ -418,7 +422,7 @@ public function processSite($format = null, array $vars = []) 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 @@ public function processSite($format = null, array $vars = []) /** @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 @@ public function setAutoescape($state) $this->autoescape = (bool) $state; } + }