Skip to content

GHSA-9436-3gmp-4f53 CVE-2023-37897

grav Server-side Template Injection (SSTI) mitigation bypass

High Packagist
Severity
7.2 / 10
High · CVSS 3.1
Exploit chance (EPSS)
2.81%
Chance of exploitation in the next 30 days. Higher than 86.51% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-393 Return of Wrong Status Code
CWE-74 Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')
Fix
1.7.42.2
Upgrade getgrav/grav to this version or later

Description

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:

  1. an Administrator account, or
  2. a non-administrator, user account that has Admin panel access and Create/Update page permissions

Steps to reproduce

  1. Log in to Grav Admin using an administrator account.
  2. 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
  3. Log out of Grav Admin
  4. Login using the account created in step 2.
  5. Choose Pages -> Home
  6. Click the Advanced tab and select the checkbox beside Twig to ensure that Twig processing is enabled for the modified webpage.
  7. Under the Content tab, insert the following payload within the editor: {{ ['id'] | map('\\system') | join() }}
  8. 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)) {
                                                                         

Affected versions

Packagist

PackageVulnerableFixed in
getgrav/grav
< 1.7.42.2
1.7.42.2

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

getgrav/grav Packagist → 1.7.42.2

composer
composer require getgrav/grav:^1.7.42.2

Severity breakdown

CVSS 3.1 7.2

CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

Attack vector
Network
Attack complexity
Low
Privileges required
High
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

Weakness

CWE-393: Return of Wrong Status Code

A function or operation returns an incorrect return value or status code that does not indicate the true result of execution, causing the product to modify its behavior based on the incorrect result.

CWE-74: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')

The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.

How to prevent it
  • Requirements: Programming languages and supporting technologies might be chosen which are not subject to these issues.
  • Implementation: Utilize an appropriate mix of allowlist and denylist parsing to filter control-plane syntax from all input.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-xjw5-q542-3vmr
Grav: config_denied_paths default list omits `system`, exposing real secrets (e.g. system.cache.redis.password) via the Twig sandbox when config_access is enabled
High Also affects getgrav/grav
GHSA-3jhr-mxmx-38cx
Grav: UserInterface offsetget/offsetexists allow-listed in Twig sandbox let editor-authored content leak hashed_password and 2FA secrets via offsetGet()
Medium Also affects getgrav/grav
GHSA-9ccq-2jfg-qw33
Grav: Origin validation bypass in Uri::referrer() and Pages::referrerRoute() via unanchored prefix match
Critical Also affects getgrav/grav
GHSA-38p6-h87p-r4cg
Grav: Non constant time nonce comparison in Utils::verifyNonce() used for CSRF protection
Medium Also affects getgrav/grav
GHSA-p597-crqc-m349
Grav: The system, site, and theme Twig variables bypass the content sandbox entirely and are never covered by config_denied_paths
Medium Also affects getgrav/grav
GHSA-47ch-6w46-6xm7
Grav: media_directory() Twig function allows filesystem path traversal and file content disclosure from sandboxed page content
Medium Also affects getgrav/grav
GHSA-jq29-c7v8-rg55
Grav: Path Traversal in MediaUploadTrait::deleteFile() Allows Arbitrary File Deletion
High Also affects getgrav/grav
GHSA-8hgv-xc77-jmcr
Grav: Page editors can inject arbitrary script into rendered pages via the Twig sandbox's assets.addJs/addCss allowlist, escalating to super-admin
Medium Also affects getgrav/grav
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor