Summary
The fix for SSTI using |map
, |filter
and |reduce
twigs implemented in the commit 71bbed1 introduces bypass of the denylist due to incorrect return value from isDangerousFunction()
, which allows to execute the payload prepending double backslash (\\
)
Details
The isDangerousFunction()
check in version 1.7.42 and onwards retuns false
value instead of true
when the \
symbol is found in the $name
.
...
if (strpos($name, "\\") !== false) {
return false;
}
if (in_array($name, $commandExecutionFunctions)) {
return true;
}
...
Based on the code where the function is used, it is expected that any dangerous condition would return true
/**
* @param Environment $env
* @param array $array
* @param callable|string $arrow
* @return array|CallbackFilterIterator
* @throws RuntimeError
*/
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.');
}
when |map('\system')
is used in the malicious payload, the single backslash is dropped prior to reaching strpos($name, '\\')
check, thus $name
variable already has no backslash, and the command is blacklisted because it reaches the if (in_array($name, $commandExecutionFunctions)) {
validation step.
However if |map('\\system')
is used (i.e. double backslash), then the strpos($name, "\\") !== false
takes effect, and isDangerousFunction()
returns false
, in which case the RuntimeError
is not generated, and blacklist is bypassed leading to code execution.
Exploit Conditions
This vulnerability can be exploited if the attacker has access to:
- an Administrator account, or
- a non-administrator, user account that has Admin panel access and Create/Update page permissions
Steps to reproduce
- Log in to Grav Admin using an administrator account.
- Navigate to
Accounts > Add
, and ensure that the following permissions are assigned when creating a new low-privileged user:
- Login to Admin - Allowed
- Page Update - Allowed
- Log out of Grav Admin
- Login using the account created in step 2.
- Choose
Pages -> Home
- Click the
Advanced
tab and select the checkbox beside Twig
to ensure that Twig processing is enabled for the modified webpage.
- Under the
Content
tab, insert the following payload within the editor:
{{ ['id'] | map('\\system') | join() }}
- Click the
Preview
button. Observe that the output of the id shell command is returned in the preview.
Mitigation
diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php
index 2f121bbe3..7b267cd0f 100644
--- a/system/src/Grav/Common/Utils.php
+++ b/system/src/Grav/Common/Utils.php
@@ -2069,7 +2069,7 @@ abstract class Utils
}
if (strpos($name, "\\") !== false) {
- return false;
+ return true;
}
if (in_array($name, $commandExecutionFunctions)) {
References
Summary
The fix for SSTI using
|map
,|filter
and|reduce
twigs implemented in the commit 71bbed1 introduces bypass of the denylist due to incorrect return value fromisDangerousFunction()
, which allows to execute the payload prepending double backslash (\\
)Details
The
isDangerousFunction()
check in version 1.7.42 and onwards retunsfalse
value instead oftrue
when the\
symbol is found in the$name
.Based on the code where the function is used, it is expected that any dangerous condition would return
true
when
|map('\system')
is used in the malicious payload, the single backslash is dropped prior to reachingstrpos($name, '\\')
check, thus$name
variable already has no backslash, and the command is blacklisted because it reaches theif (in_array($name, $commandExecutionFunctions)) {
validation step.However if
|map('\\system')
is used (i.e. double backslash), then thestrpos($name, "\\") !== false
takes effect, andisDangerousFunction()
returnsfalse
, in which case theRuntimeError
is not generated, and blacklist is bypassed leading to code execution.Exploit Conditions
This vulnerability can be exploited if the attacker has access to:
Steps to reproduce
Accounts > Add
, and ensure that the following permissions are assigned when creating a new low-privileged user:Pages -> Home
Advanced
tab and select the checkbox besideTwig
to ensure that Twig processing is enabled for the modified webpage.Content
tab, insert the following payload within the editor:{{ ['id'] | map('\\system') | join() }}
Preview
button. Observe that the output of the id shell command is returned in the preview.Mitigation
References