Grav Server-side Template Injection (SSTI) via Twig Default Filters
Description
Grav is a flat-file content management system. Prior to version 1.7.42, the patch for CVE-2022-2073, a server-side template injection vulnerability in Grav leveraging the default filter() function, did not block other built-in functions exposed by Twig's Core Extension that could be used to invoke arbitrary unsafe functions, thereby allowing for remote code execution. A patch in version 1.74.2 overrides the built-in Twig map() and reduce() filter functions in system/src/Grav/Common/Twig/Extension/GravExtension.php to validate the argument passed to the filter in $arrow.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
getgrav/gravPackagist | < 1.7.42 | 1.7.42 |
Affected products
1Patches
4244758d43830also handle SSTI in reduce twig filter + function
2 files changed · +29 −6
CHANGELOG.md+2 −1 modified@@ -4,10 +4,11 @@ 1. [](#new) * Added a new `system.languages.debug` option that adds a `<span class="translate-debug"></span>` around strings translated with `|t`. This can be styled by the theme as needed. 1. [](#improved) - * More robust SSTI handling in `|filter` and `|map` + * More robust SSTI handling in `filter`, `map`, and `reduce` Twig filters and functions * Various SSTI improvements `Utils::isDangerousFunction()` 1. [](#bugfix) * Fixed Twig `|map()` allowing code execution + * Fixed Twig `|reduce()` allowing code execution # v1.7.41.2 ## 06/01/2023
system/src/Grav/Common/Twig/Extension/GravExtension.php+27 −5 modified@@ -171,9 +171,10 @@ public function getFilters(): array new TwigFilter('count', 'count'), new TwigFilter('array_diff', 'array_diff'), - // Security fix - new TwigFilter('filter', [$this, 'filterFilter'], ['needs_environment' => true]), - new TwigFilter('map', [$this, 'mapFilter'], ['needs_environment' => true]), + // Security fixes + new TwigFilter('filter', [$this, 'filterFunc'], ['needs_environment' => true]), + new TwigFilter('map', [$this, 'mapFunc'], ['needs_environment' => true]), + new TwigFilter('reduce', [$this, 'reduceFunc'], ['needs_environment' => true]), ]; } @@ -250,6 +251,11 @@ public function getFunctions(): array new TwigFunction('count', 'count'), new TwigFunction('array_diff', 'array_diff'), new TwigFunction('parse_url', 'parse_url'), + + // Security fixes + new TwigFunction('filter', [$this, 'filterFunc'], ['needs_environment' => true]), + new TwigFunction('map', [$this, 'mapFunc'], ['needs_environment' => true]), + new TwigFunction('reduce', [$this, 'reduceFunc'], ['needs_environment' => true]), ]; } @@ -1706,7 +1712,7 @@ public function ofTypeFunc($var, $typeTest = null, $className = null) * @return array|CallbackFilterIterator * @throws RuntimeError */ - function filterFilter(Environment $env, $array, $arrow) + function filterFunc(Environment $env, $array, $arrow) { if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) { throw new RuntimeError('Twig |filter("' . $arrow . '") is not allowed.'); @@ -1722,12 +1728,28 @@ function filterFilter(Environment $env, $array, $arrow) * @return array|CallbackFilterIterator * @throws RuntimeError */ - function mapFilter(Environment $env, $array, $arrow) + function mapFunc(Environment $env, $array, $arrow) { if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) { throw new RuntimeError('Twig |map("' . $arrow . '") is not allowed.'); } return twig_array_map($env, $array, $arrow); } + + /** + * @param Environment $env + * @param array $array + * @param callable|string $arrow + * @return array|CallbackFilterIterator + * @throws RuntimeError + */ + function reduceFunc(Environment $env, $array, $arrow) + { + if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) { + throw new RuntimeError('Twig |reduce("' . $arrow . '") is not allowed.'); + } + + return twig_array_map($env, $array, $arrow); + } }
71bbed12f950more SSTI fixes in Utils::isDangerousFunction()
2 files changed · +18 −1
CHANGELOG.md+1 −0 modified@@ -5,6 +5,7 @@ * Added a new `system.languages.debug` option that adds a `<span class="translate-debug"></span>` around strings translated with `|t`. This can be styled by the theme as needed. 1. [](#improved) * More robust SSTI handling in `|filter` and `|map` + * Various SSTI improvements `Utils::isDangerousFunction()` 1. [](#bugfix) * Fixed Twig `|map()` allowing code execution
system/src/Grav/Common/Utils.php+17 −1 modified@@ -1950,7 +1950,7 @@ public static function getSupportPageTypes(array $defaults = null) } /** - * @param string|array $name + * @param string|array|Closure $name * @return bool */ public static function isDangerousFunction($name): bool @@ -2048,8 +2048,24 @@ public static function isDangerousFunction($name): bool 'posix_setpgid', 'posix_setsid', 'posix_setuid', + 'unserialize', + 'ini_alter', + 'simplexml_load_file', + 'simplexml_load_string', + 'forward_static_call', + 'forward_static_call_array', ]; + $name = strtolower($name); + + if ($name instanceof \Closure) { + return false; + } + + if (strpos($name, "\\") !== false) { + return false; + } + if (is_array($name) || strpos($name, ":") !== false) { return false; }
8c2c1cb72611better SSTI in |map and |filter
3 files changed · +11 −5
CHANGELOG.md+3 −1 modified@@ -3,8 +3,10 @@ 1. [](#new) * Added a new `system.languages.debug` option that adds a `<span class="translate-debug"></span>` around strings translated with `|t`. This can be styled by the theme as needed. +1. [](#improved) + * More robust SSTI handling in `|filter` and `|map` 1. [](#bugfix) - * * Fixed Twig `|map()` allowing code execution + * Fixed Twig `|map()` allowing code execution # v1.7.41.2 ## 06/01/2023
system/src/Grav/Common/Twig/Extension/GravExtension.php+2 −2 modified@@ -1708,7 +1708,7 @@ public function ofTypeFunc($var, $typeTest = null, $className = null) */ function filterFilter(Environment $env, $array, $arrow) { - if (is_string($arrow) && Utils::isDangerousFunction($arrow)) { + if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) { throw new RuntimeError('Twig |filter("' . $arrow . '") is not allowed.'); } @@ -1724,7 +1724,7 @@ function filterFilter(Environment $env, $array, $arrow) */ function mapFilter(Environment $env, $array, $arrow) { - if (is_string($arrow) && Utils::isDangerousFunction($arrow)) { + if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) { throw new RuntimeError('Twig |map("' . $arrow . '") is not allowed.'); }
system/src/Grav/Common/Utils.php+6 −2 modified@@ -1950,10 +1950,10 @@ public static function getSupportPageTypes(array $defaults = null) } /** - * @param string $name + * @param string|array $name * @return bool */ - public static function isDangerousFunction(string $name): bool + public static function isDangerousFunction($name): bool { static $commandExecutionFunctions = [ 'exec', @@ -2050,6 +2050,10 @@ public static function isDangerousFunction(string $name): bool 'posix_setuid', ]; + if (is_array($name) || strpos($name, ":") !== false) { + return false; + } + if (in_array($name, $commandExecutionFunctions)) { return true; }
9d01140a63c7Fix for dangerous tags in |map filter
2 files changed · +19 −0
CHANGELOG.md+2 −0 modified@@ -3,6 +3,8 @@ 1. [](#new) * Added a new `system.languages.debug` option that adds a `<span class="translate-debug"></span>` around strings translated with `|t`. This can be styled by the theme as needed. +1. [](#bugfix) + * * Fixed Twig `|map()` allowing code execution # v1.7.41.2 ## 06/01/2023
system/src/Grav/Common/Twig/Extension/GravExtension.php+17 −0 modified@@ -173,6 +173,7 @@ public function getFilters(): array // Security fix new TwigFilter('filter', [$this, 'filterFilter'], ['needs_environment' => true]), + new TwigFilter('map', [$this, 'mapFilter'], ['needs_environment' => true]), ]; } @@ -1713,4 +1714,20 @@ function filterFilter(Environment $env, $array, $arrow) return twig_array_filter($env, $array, $arrow); } + + /** + * @param Environment $env + * @param array $array + * @param callable|string $arrow + * @return array|CallbackFilterIterator + * @throws RuntimeError + */ + function mapFilter(Environment $env, $array, $arrow) + { + if (is_string($arrow) && Utils::isDangerousFunction($arrow)) { + throw new RuntimeError('Twig |map("' . $arrow . '") is not allowed.'); + } + + return twig_array_map($env, $array, $arrow); + } }
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
11- github.com/advisories/GHSA-whr7-m3f8-mpm8ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-34448ghsaADVISORY
- github.com/getgrav/grav/commit/244758d4383034fe4cd292d41e477177870b65ecghsaWEB
- github.com/getgrav/grav/commit/71bbed12f950de8335006d7f91112263d8504f1bghsaWEB
- github.com/getgrav/grav/commit/8c2c1cb72611a399f13423fc6d0e1d998c03e5c8ghsax_refsource_MISCWEB
- github.com/getgrav/grav/commit/9d01140a63c77075ef09b26ef57cf186138151a5ghsaWEB
- github.com/getgrav/grav/security/advisories/GHSA-whr7-m3f8-mpm8ghsax_refsource_CONFIRMWEB
- github.com/twigphp/Twig/blob/v1.44.7/src/Environment.phpghsax_refsource_MISCWEB
- huntr.dev/bounties/3ef640e6-9e25-4ecb-8ec1-64311d63fe66ghsaWEB
- huntr.dev/bounties/3ef640e6-9e25-4ecb-8ec1-64311d63fe66/mitrex_refsource_MISC
- www.github.com/getgrav/grav/commit/9d6a2dba09fd4e56f5cdfb9a399caea355bfeb83ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.