See how pixel & tonic compares to other vendors in security performance
Craft CMS contains a missing authorization vulnerability in the assets/preview-thumb endpoint. A Control Panel user without permission to view a target private asset can call the endpoint with an attacker-controlled assetId and receive preview HTML containing a signed fallback transform preview link for that private asset, because no asset-view permission check is performed before preview generation. This affects versions >= 4.0.0-RC1, <= 4.17.7 and >= 5.0.0-RC1, <= 5.9.13, and is fixed in 4.17.8 and 5.9.14.
Craft CMS contains a stored cross-site scripting (XSS) vulnerability in the editableTable.twig component when using the 'Row Heading' column type. The application fails to sanitize input within row heading default values, allowing an attacker with an administrator account (with allowAdminChanges enabled) to inject arbitrary JavaScript that executes when another user views a page containing the affected table field. Affected versions are >= 4.5.0-beta.1 through 4.16.18 and >= 5.0.0-RC1 through 5.8.22; fixed in 4.16.19 and 5.8.23.
Craft CMS from 4.0.0-RC1 contains an authenticated path traversal vulnerability in the assets/icon endpoint where the extension parameter is not validated before file existence checks. Attackers can bypass extension validation by passing traversal sequences that resolve to existing SVG files, allowing local file read access.
Craft CMS from version 5.0.0-RC1 contains a stored cross-site scripting vulnerability in the User Permissions page where user group names are rendered without proper HTML escaping. Attackers with admin access can inject arbitrary JavaScript via the user group name field that executes when other users view or edit permissions.
Craft CMS versions >= 5.0.0-RC1, <= 5.9.13 and >= 4.0.0-RC1, <= 4.17.7 contain an authorization bypass in the assets/preview-file endpoint. The action does not enforce per-asset view authorization before returning preview content, allowing an authenticated low-privileged user to supply a controlled assetId for an asset they are not permitted to view and still receive preview response data (previewHtml), including a private preview image route containing the target private assetId. Fixed in 5.9.14 and 4.17.8.
Craft CMS 4.x (>= 4.0.0-RC1, < 4.17.0-beta.1) and 5.x (>= 5.0.0-RC1, < 5.9.0-beta.1) contain multiple stored cross-site scripting vulnerabilities where settings names and field option labels are rendered without sanitization (e.g., via the checkbox.twig template, which used {{ label|raw }}). An authenticated administrator (with allowAdminChanges enabled) can inject malicious payloads into section names, volume names, user group names, global set names, generated field names, checkbox/radio option labels, and custom source labels, causing arbitrary JavaScript to execute in other users' control-panel sessions. Fixed in 4.17.0-beta.1 and 5.9.0-beta.1.
Craft CMS 5.9.5 and earlier contains a Missing Authorization vulnerability in the migrate endpoint (/actions/app/migrate).
Craft CMS is a content management system (CMS). Versions on the 4.x branch through 4.17.8 and the 5.x branch through 5.9.14 are vulnerable to Server-Side Request Forgery. The exploitation requires a few permissions to be enabled in the used GraphQL schema: "Edit assets in the <VolumeName> volume" and "Create assets in the <VolumeName> volume." Versions 4.17.9 and 5.9.15 patch the issue.
Summary
PaymentsController::actionPay discloses some order data to unauthenticated users when an order number is provided and the email check fails during an anonymous payment.
The JSON error response includes the serialized order object (order), which contains some sensitive fields such as customer email, shipping address, and billing address.
Details
I manually audited frontend payment flows and found that actionPay() retrieves orders by number before authorization is fully enforced.
Code path:
1. Load order by number. 2. Evaluate whether payment is authorized for completed orders (number + matching email). 3. If unauthorized, return failure. 4. Failure response still includes cartArray($order), which serializes sensitive order data.
Why is this a vulnerability?
- Authorization logic says the requester is not allowed to pay for a completed order without an email. - But the response still returns the same completed order’s contents.
Impact
Type: Information Disclosure / Broken Access Control
Who is impacted:
- Any Commerce deployment where completed order numbers can be obtained or leaked.
Summary
A SQL injection in the Commerce TotalRevenue widget can lead to remote code execution through a chain of four vulnerabilities:
SQL Injection -- The TotalRevenue stat interpolates unsanitized widget settings directly into a sprintf-based SQL Expression. Any control panel user can create any widget type without permission checks.
PDO Multi-Statement Queries -- PHP PDO MySQL enables CLIENTMULTISTATEMENTS by default. Neither Yii2 nor Craft CMS disables it. This allows stacking an INSERT statement after the injected SELECT , writing a maliciously serialized PHP object into the queue table.
Unrestricted unserialize() -- The yii2-queue PhpSerializer calls unserialize() with no allowedclasses restriction on every queue job. When the queue consumer processes the injected job, it instantiates the attacker-controlled object.
Gadget Chain (FileCookieJar) -- GuzzleHttp\Cookie\FileCookieJar (a standard Guzzle dependency) has an unguarded destruct() method that calls fileputcontents(). The attacker’s serialized payload writes a PHP webshell to the server’s webroot. PHP tags survive jsonencode() because Guzzle uses options=0 (no JSONHEXTAG).
The complete chain requires 3 HTTP requests and achieves arbitrary command execution as the PHP process user. Queue processing is triggered via GET /actions/queue/run, an endpoint that requires no authentication ($allowAnonymous = ['run']).
RCE Exploitation Steps
Authenticate as any control panel user POST to /admin/actions/dashboard/create-widget with stacked SQL injection: settings[type] contains the stacked INSERT with the serialized gadget chain Response: HTTP 500 (expected -- INSERT already committed) Trigger queue processing: GET /actions/queue/run Queue consumer deserializes the gadget chain FileCookieJar::destruct() writes webshell to webroot Access the webshell: GET /pocrce.php?c=id Response: uid=1000(home) gid=1000(home) groups=1000(home)
Overview
Craft Commerce’s ProductQuery::hasVariant and VariantQuery::hasProduct properties bypass the unset() blocklist added to ElementIndexesController in GHSA-2453-mppf-46cj.
The blocklist only strips top-level Yii2 Query properties (where, orderBy, etc.), but hasVariant and hasProduct pass through untouched. Internally, these properties call Craft::configure() on a subquery without sanitization, re-introducing SQL injection via criteria[hasVariant][where]=INJECTEDSQL.
An authenticated control panel user can perform boolean-based blind SQL injection through the patched ElementIndexesController and extract arbitrary database contents.
Impact
Full database read access via blind SQL injection Privilege escalation via security key extraction → forged admin sessions
Prerequisites Authenticated control panel user Commerce plugin installed Products with variants in the database
End of life: 12/31/2031, End of support: 12/31/2030, Latest version: 5.10.14
End of life: 12/31/2031, End of support: 12/31/2030, Latest version: 5.10.14
End of life: 4/30/2026, End of support: 4/30/2025, Latest version: 4.18.7
End of life: 4/30/2026, End of support: 4/30/2025, Latest version: 4.18.7
End of life: 4/30/2024, End of support: 4/30/2023, Latest version: 3.9.15
End of life: 4/30/2024, End of support: 4/30/2023, Latest version: 3.9.15
End of life: 1/31/2022, End of support: 1/31/2020, Latest version: 2.9.2
End of life: 1/31/2022, End of support: 1/31/2020, Latest version: 2.9.2