Grav vulnerable to Server Side Template Injection (SSTI) via Twig escape handler
Description
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.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
getgrav/gravPackagist | < 1.7.45 | 1.7.45 |
Affected products
1Patches
1de1ccfa12dbcMitigate various SSTI injections
3 files changed · +30 −3
CHANGELOG.md+2 −1 modified@@ -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
system/src/Grav/Common/Security.php+19 −0 modified@@ -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; + } }
system/src/Grav/Common/Twig/Twig.php+9 −2 modified@@ -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; } + }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-2m7x-c7px-hp58ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-28119ghsaADVISORY
- github.com/getgrav/grav/commit/de1ccfa12dbcbf526104d68c1a6bc202a98698feghsax_refsource_MISCWEB
- github.com/getgrav/grav/security/advisories/GHSA-2m7x-c7px-hp58ghsax_refsource_CONFIRMWEB
- github.com/twigphp/Twig/blob/3.x/src/Extension/EscaperExtension.phpghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.