Grav vulnerable to Server-side Template Injection (SSTI) via Denylist Bypass
Description
Grav is a flat-file content management system. Prior to version 1.7.42, the denylist introduced in commit 9d6a2d to prevent dangerous functions from being executed via injection of malicious templates was insufficient and could be easily subverted in multiple ways -- (1) using unsafe functions that are not banned, (2) using capitalised callable names, and (3) using fully-qualified names for referencing callables. Consequently, a low privileged attacker with login access to Grav Admin panel and page creation/update permissions is able to inject malicious templates to obtain remote code execution. A patch in version 1.7.42 improves the denylist.
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-j3v8-v77f-fvgmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-34253ghsaADVISORY
- github.com/getgrav/grav/blob/1.7.40/system/src/Grav/Common/Utils.phpghsax_refsource_MISCWEB
- github.com/getgrav/grav/commit/244758d4383034fe4cd292d41e477177870b65ecghsaWEB
- github.com/getgrav/grav/commit/71bbed12f950de8335006d7f91112263d8504f1bghsax_refsource_MISCWEB
- github.com/getgrav/grav/commit/8c2c1cb72611a399f13423fc6d0e1d998c03e5c8ghsaWEB
- github.com/getgrav/grav/commit/9d01140a63c77075ef09b26ef57cf186138151a5ghsaWEB
- github.com/getgrav/grav/security/advisories/GHSA-j3v8-v77f-fvgmghsax_refsource_CONFIRMWEB
- 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.