logo

CVE-2023-34253 getgrav/grav

Package

Manager: composer
Name: getgrav/grav
Vulnerable Version: >=0 <1.7.42

Severity

Level: High

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

CVSS v4.0: CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

EPSS: 0.01436 pctl0.79956

Details

Grav Server-side Template Injection (SSTI) via Denylist Bypass Vulnerability 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 Denylist Bypass Vulnerability ## 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](https://github.com/getgrav/grav/tree/1.7.40) (Commit [685d762](https://github.com/getgrav/grav/commit/685d76231a057416651ed192a6a2e83720800e61)) (Latest version as of writing) | | **Tested Versions** | v1.7.40 | | **Internal Identifier** | STAR-2023-0006 | | **CVE Identifier** | Reserved CVE-2023-30592, CVE-2023-30593, CVE-2023-30594 | | **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 denylist introduced in commit [9d6a2d](https://www.github.com/getgrav/grav/commit/9d6a2dba09fd4e56f5cdfb9a399caea355bfeb83) 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. ## Vulnerability Details: In addressing [CVE-2022-2073](https://huntr.dev/bounties/3ef640e6-9e25-4ecb-8ec1-64311d63fe66/), a denylist was introduced in commit [9d6a2d](https://www.github.com/getgrav/grav/commit/9d6a2dba09fd4e56f5cdfb9a399caea355bfeb83) to validate and ensure that dangerous functions could not be executed via injection of malicious templates. The implementation of the denylist can be found in `Utils::isDangerousFunction()` within [/system/src/Grav/Common/Utils.php](https://github.com/getgrav/grav/blob/1.7.40/system/src/Grav/Common/Utils.php#L1952-L2190): ~~~php /** * @param string $name * @return bool */ public static function isDangerousFunction(string $name): bool { static $commandExecutionFunctions = [ 'exec', 'passthru', 'system', 'shell_exec', 'popen', 'proc_open', 'pcntl_exec', ]; static $codeExecutionFunctions = [ 'assert', 'preg_replace', 'create_function', 'include', 'include_once', 'require', 'require_once' ]; static $callbackFunctions = [ 'ob_start' => 0, 'array_diff_uassoc' => -1, 'array_diff_ukey' => -1, 'array_filter' => 1, 'array_intersect_uassoc' => -1, 'array_intersect_ukey' => -1, 'array_map' => 0, 'array_reduce' => 1, 'array_udiff_assoc' => -1, 'array_udiff_uassoc' => [-1, -2], 'array_udiff' => -1, 'array_uintersect_assoc' => -1, 'array_uintersect_uassoc' => [-1, -2], 'array_uintersect' => -1, 'array_walk_recursive' => 1, 'array_walk' => 1, 'assert_options' => 1, 'uasort' => 1, 'uksort' => 1, 'usort' => 1, 'preg_replace_callback' => 1, 'spl_autoload_register' => 0, 'iterator_apply' => 1, 'call_user_func' => 0, 'call_user_func_array' => 0, 'register_shutdown_function' => 0, 'register_tick_function' => 0, 'set_error_handler' => 0, 'set_exception_handler' => 0, 'session_set_save_handler' => [0, 1, 2, 3, 4, 5], 'sqlite_create_aggregate' => [2, 3], 'sqlite_create_function' => 2, ]; static $informationDiscosureFunctions = [ 'phpinfo', 'posix_mkfifo', 'posix_getlogin', 'posix_ttyname', 'getenv', 'get_current_user', 'proc_get_status', 'get_cfg_var', 'disk_free_space', 'disk_total_space', 'diskfreespace', 'getcwd', 'getlastmo', 'getmygid', 'getmyinode', 'getmypid', 'getmyuid' ]; static $otherFunctions = [ 'extract', 'parse_str', 'putenv', 'ini_set', 'mail', 'header', 'proc_nice', 'proc_terminate', 'proc_close', 'pfsockopen', 'fsockopen', 'apache_child_terminate', 'posix_kill', 'posix_mkfifo', 'posix_setpgid', 'posix_setsid', 'posix_setuid', ]; if (in_array($name, $commandExecutionFunctions)) { return true; } if (in_array($name, $codeExecutionFunctions)) { return true; } if (isset($callbackFunctions[$name])) { return true; } if (in_array($name, $informationDiscosureFunctions)) { return true; } if (in_array($name, $otherFunctions)) { return true; } return static::isFilesystemFunction($name); } /** * @param string $name * @return bool */ public static function isFilesystemFunction(string $name): bool { static $fileWriteFunctions = [ 'fopen', 'tmpfile', 'bzopen', 'gzopen', // write to filesystem (partially in combination with reading) 'chgrp', 'chmod', 'chown', 'copy', 'file_put_contents', 'lchgrp', 'lchown', 'link', 'mkdir', 'move_uploaded_file', 'rename', 'rmdir', 'symlink', 'tempnam', 'touch', 'unlink', 'imagepng', 'imagewbmp', 'image2wbmp', 'imagejpeg', 'imagexbm', 'imagegif', 'imagegd', 'imagegd2', 'iptcembed', 'ftp_get', 'ftp_nb_get', ]; static $fileContentFunctions = [ 'file_get_contents', 'file', 'filegroup', 'fileinode', 'fileowner', 'fileperms', 'glob', 'is_executable', 'is_uploaded_file', 'parse_ini_file', 'readfile', 'readlink', 'realpath', 'gzfile', 'readgzfile', 'stat', 'imagecreatefromgif', 'imagecreatefromjpeg', 'imagecreatefrompng', 'imagecreatefromwbmp', 'imagecreatefromxbm', 'imagecreatefromxpm', 'ftp_put', 'ftp_nb_put', 'hash_update_file', 'highlight_file', 'show_source', 'php_strip_whitespace', ]; static $filesystemFunctions = [ // read from filesystem 'file_exists', 'fileatime', 'filectime', 'filemtime', 'filesize', 'filetype', 'is_dir', 'is_file', 'is_link', 'is_readable', 'is_writable', 'is_writeable', 'linkinfo', 'lstat', //'pathinfo', 'getimagesize', 'exif_read_data', 'read_exif_data', 'exif_thumbnail', 'exif_imagetype', 'hash_file', 'hash_hmac_file', 'md5_file', 'sha1_file', 'get_meta_tags', ]; if (in_array($name, $fileWriteFunctions)) { return true; } if (in_array($name, $fileContentFunctions)) { return true; } if (in_array($name, $filesystemFunctions)) { return true; } return false; } ~~~ The list of banned functions appears to be adapted from a [StackOverflow post](https://stackoverflow.com/a/3697776). While the denylist looks rather comprehensive, there are actually multiple issues with the denylist implementation: 1. There may be unsafe functions, be it built-in to PHP or user-defined, which are not be blocked. For example, `unserialize()` and aliases of blocked functions, such as `ini_alter()`, are not being included in the denylist. 2. A case-sensitive comparison is performed against the denylist, but PHP function names are case-insensitive. This allows using `filter('SYSTEM')` to trivially bypass the denylist validation check. 3. Fully qualified names can be used when referencing functions, allowing `filter('\system')` to trivially bypass the denylist validation checks. ## Exploit Conditions: This vulnerability can be exploited if the attacker has access to: 1. an administrator account, or 2. a non-administrative user account with the following permissions granted: - 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 3. Log out of Grav Admin, and log back in using the account created in step 2. 4. Navigate to `http://<grav_installation>/admin/pages/home`. 5. Click the `Advanced` tab and select the checkbox beside `Twig` to ensure that Twig processing is enabled

Metadata

Created: 2023-06-16T19:36:52Z
Modified: 2023-06-16T19:36:52Z
Source: https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/06/GHSA-j3v8-v77f-fvgm/GHSA-j3v8-v77f-fvgm.json
CWE IDs: ["CWE-1336", "CWE-94"]
Alternative ID: GHSA-j3v8-v77f-fvgm
Finding: F422
Auto approve: 1