CVE-2023-34448: Grav Server-side Template Injection (SSTI) via Twig Default Filters

Published Jun 14, 2023
·
Updated

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.

Other sources

Hi,

actually we have sent the bug report to security@getgrav.org on 27th March 2023 and on 10th April 2023.

Grav Server-side Template Injection (SSTI) via Twig Default Filters

Summary: | Product | Grav CMS | | ----------------------- | --------------------------------------------- | | Vendor | Grav | | Severity | High - Users with login access to Grav Admin panel and page creation/update permissions are able to obtain remote code/command execution | | Affected Versions | <= v1.7.40 (Commit 685d762) (Latest version as of writing) | | Tested Versions | v1.7.40 | | Internal Identifier | STAR-2023-0008 | | CVE Identifier | TBD | | CWE(s) | CWE-184: Incomplete List of Disallowed Inputs, CWE-1336: Improper Neutralization of Special Elements Used in a Template Engine |

CVSS3.1 Scoring System: Base Score: 7.2 (High) Vector String: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H | Metric | Value | | ---------------------------- | --------- | | Attack Vector (AV) | Network | | Attack Complexity (AC) | Low | | Privileges Required (PR) | High | | User Interaction (UI) | None | | Scope (S) | Unchanged | | Confidentiality \(C) | High | | Integrity (I) | High | | Availability (A) | High |

Product Overview: Grav is a PHP-based flat-file content management system (CMS) designed to provide a fast and simple way to build websites. It supports rendering of web pages written in Markdown and Twig expressions, and provides an administration panel to manage the entire website via an optional Admin plugin.

Vulnerability Summary: 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.

Vulnerability Details: Twig comes with an extension known as the Core Extension that is enabled by default when initialising a new Twig environment. Twig's Core Extension provides multiple built-in filters, such as the filter() function, which can be used in Twig templates.

CVE-2022-2073 leverages the default filter() filter function in Twig to invoke arbitrary unsafe functions. This was patched by overriding the default filter() filter function in commit 9d6a2d of Grav v1.7.34 to perform validation checks on the arguments passed to filter(): ~~~diff php ... class GravExtension extends AbstractExtension implements GlobalsInterface { ... public function getFilters(): array { return [ ... // Security fix + new TwigFilter('filter', [$this, 'filterFilter'], ['needsenvironment' => true]), ]; } ...

+ / + @param Environment $env + @param array $array + @param callable|string $arrow + @return array|CallbackFilterIterator + @throws RuntimeError + / + function filterFilter(Environment $env, $array, $arrow) + { + if (isstring($arrow) && Utils::isDangerousFunction($arrow)) { + throw new RuntimeError('Twig |filter("' . $arrow . '") is not allowed.'); + } + + return \twigarrayfilter($env, $array, $arrow); + } } ~~~

However, looking at the source code of /src/Extension/CoreExtension.php of Twig, alternative default Twig filters could also be used invoke arbitrary functions: ~~~php ... class CoreExtension extends AbstractExtension { ... public function getFilters(): array { return [ ... // array helpers ... new TwigFilter('filter', 'twigarrayfilter', ['needsenvironment' => true]), // unsafe new TwigFilter('map', 'twigarraymap', ['needsenvironment' => true]), // unsafe new TwigFilter('reduce', 'twigarrayreduce', ['needsenvironment' => true]), // unsafe ]; } ~~~

The three filter functions above respectively call arrayfilter(), arraymap() and arrayreduce(). Since only filter() is being overriden by Grav to ensure that the callable passed to filter() does not result in the invocation of an unsafe function, the other two functions (i.e. map() and reduce()) could be used by an authenticated attacker that is able to inject and render malicious templates to gain remote 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 are granted the following permissions: - login access to Grav admin panel, and - page creation or update rights

Reproduction Steps: 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 2. Log out of Grav Admin, and log back in using the account created in step 2. 3. Navigate to http://<gravinstallation>/admin/pages/home. 4. Click the Advanced tab and select the checkbox beside Twig to ensure that Twig processing is enabled for the modified webpage. 5. Under the Content tab, insert the following payload within the editor: ~~~twig {{ ['id'] | map('system') }} {{ ['id'] | reduce('system') }} ~~~ 4. Click the Preview button. Observe that the output of the id shell command is returned in the preview.

Suggested Mitigations: Override 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.

For example: ~~~diff ... class GravExtension extends AbstractExtension implements GlobalsInterface { ... public function getFilters(): array { return [ ... // Security fix new TwigFilter('filter', [$this, 'filterFilter'], ['needsenvironment' => true]), + new TwigFilter('map', [$this, 'mapFilter'], ['needsenvironment' => true]), + new TwigFilter('reduce', [$this, 'reduceFilter'], ['needsenvironment' => true]), ]; }

... + / + @param Environment $env + @param array $array + @param callable|string $arrow + @return array|CallbackFilterIterator + @throws RuntimeError + / + function mapFilter(Environment $env, $array, $arrow) + { + if (!$arrow instanceof Closure && !isstring($arrow) || Utils::isDangerousFunction($arrow)) { + throw new RuntimeError('Twig |map("' . $arrow . '") is not allowed.'); + } + + return \twigarraymap($env, $array, $arrow); + } + + / + @param Environment $env + @param array $array + @param callable|string $arrow + @return array|CallbackFilterIterator + @throws RuntimeError + / + function reduceFilter(Environment $env, $array, $arrow) + { + if (!$arrow instanceof Closure && !isstring($arrow) || Utils::isDangerousFunction($arrow)) { + throw new RuntimeError('Twig |reduce("' . $arrow . '") is not allowed.'); + } + + return \twigarrayreduce($env, $array, $arrow); + } } ~~~

Detection Guidance: The following strategies may be used to detect potential exploitation attempts. 1. Searching within Markdown pages using the following shell command: grep -Priz -e '\|\s(map|reduce)\s\(' /path/to/webroot/user/pages/ 2. Searching within Doctrine cache data using the following shell command: grep -Priz -e '\|\s(map|reduce)\s\(' --include '.doctrinecache.data' /path/to/webroot/cache/ 3. Searching within Twig cache using the following shell command: grep -Priz -e 'twigarray(map|reduce)' /path/to/webroot/cache/twig/ 4. Searching within compiled Twig template files using the following shell command: grep -Priz -e '\|\s(map|reduce)\s\(' /path/to/webroot/cache/compiled/files/

Note that it is not possible to detect indicators of compromise reliably using the Grav log file (located at /path/to/webroot/logs/grav.log by default), as successful exploitation attempts do not generate any additional logs. However, it is worthwhile to examine any PHP errors or warnings logged to determine the existence of any failed exploitation attempts.

Credits: Ngo Wei Lin (@Creastery) & Wang Hengyue (@why04) of STAR Labs SG Pte. Ltd. (@starlabssg)

Vulnerability Disclosure: This vulnerability report is subject to a 120 day disclosure deadline as per STAR Labs SG Pte. Ltd.'s Vulnerability Disclosure Policy. After 120 days have elapsed, the vulnerability report will be published to the public by STAR Labs SG Pte. Ltd. (STAR Labs).

The scheduled disclosure date is 25th July, 2023. Disclosure at an earlier date is also possible if agreed upon by all parties.

Kindly note that STAR Labs reserved and assigned the following CVE identifiers to the respective vulnerabilities presented in this report: 1. CVE-2023-30596 Server-side Template Injection (SSTI) in getgrav/grav <= v1.7.40 allows Grav Admin users with page creation or update rights to bypass the dangerous functions denylist check in GravExtension.filterFilter() and to achieve remote code execution via Twig's default filters map() and reduce(). This is a bypass of CVE-2022-2073.

Affected Software

2 affected componentsFixes available
composer/getgrav/grav<1.7.42
1.7.42
getgrav Grav<1.7.42

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade composer/getgrav/grav to a version that resolves this vulnerability.

    Fixed in 1.7.42
  2. Upgrade

    Upgrade getgrav/grav to a version that resolves this vulnerability.

    Fixed in 1.74.2
  3. Configuration

    In the Admin page editor, click Preview, then Advanced, and ensure the checkbox beside Twig is selected so Twig processing is enabled for the modified webpage.

    Grav Admin - Twig processing for modified webpage Twig processing enabled (Advanced tab -> Twig checkbox) = ensure enabled
  4. Operational

    After applying the fix, log out of Grav Admin and log back in using the account created in step 2 (to ensure the changes apply under a session with the intended permissions).

Event History

Jun 14, 2023
CVE Published
via MITRE·10:06 PM
Data Sourced
via MITRE·10:06 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·11:15 PM
RemedyDescriptionSeverityWeaknessAffected Software
Jun 16, 2023
Advisory Published
07:37 PM

Frequently Asked Questions

1

What is CVE-2023-34448?

CVE-2023-34448 is a server-side template injection vulnerability in Grav prior to version 1.7.42 that can be exploited using the default `filter()` function.

2

How severe is CVE-2023-34448?

CVE-2023-34448 has a severity rating of 7.2, which is considered high.

3

What software is affected by CVE-2023-34448?

Grav prior to version 1.7.42 is affected by CVE-2023-34448.

4

How can CVE-2023-34448 be exploited?

CVE-2023-34448 can be exploited by leveraging the default `filter()` function in Grav to perform server-side template injection.

5

Is there a patch available for CVE-2023-34448?

Yes, a patch for CVE-2023-34448 is available in version 1.7.42 of Grav.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203