See how craft cms compares to other vendors in security performance
Craft CMS versions from 4.0.0-RC1 before 4.18.2 and from 5.0.0-RC1 before 5.10.6 contain an authenticated remote code execution vulnerability in control panel element-search condition handling. A JSON cleanse bypass in condition.config allows Yii behavior/event configuration keys to be interpreted after decoding, enabling command execution as the PHP/web user.
Craft CMS versions >= 5.0.0-RC1 before 5.10.6 and >= 4.0.0-RC1 before 4.18.2 contain a theoretical path traversal weakness in the ensurePathIsContained function of the Local file system class. The order of operations validates the path before normalization, so normalization could invalidate prior validation assumptions (a desanitization-style issue) and potentially resolve to files outside the intended volume directory. The vendor notes the issue is not directly exploitable and no exploitable scenario has been discovered; the fix is recommended for hardening.
Craft CMS versions from 4.0.0-RC1 before 4.18.2 and from 5.0.0-RC1 before 5.10.6 contain an authenticated remote code execution vulnerability in the control panel element-search condition handling. Craft cleanses the outer request-controlled condition array via Component::cleanseConfig(), but Conditions::createCondition() later decodes and merges the JSON string in condition.config without re-running cleanseConfig() on the decoded configuration. Because condition.config is a JSON string during the first cleanse, Yii special config keys such as 'as ...' and 'on ...' can be hidden inside it and, after JSON decoding, are interpreted by Yii as behavior/event configuration during FieldLayout object creation. An attacker with an authenticated control panel session (and a valid CSRF token) can exploit this to execute operating system commands as the PHP/web user.
Craft CMS versions before 5.10.8 contain a stored cross-site scripting vulnerability in the control panel where draft names are rendered without HTML encoding in element chips and cards. A low-privilege user who can create element drafts can inject malicious JavaScript that executes in the browser of any higher-privileged user viewing the affected element, allowing account creation and other authenticated actions.
Craft CMS versions before 5.10.8 contain an authentication bypass vulnerability in the elements/save action that allows authenticated users to change passwords without verification. Attackers with edit users permission can reset any user's password including administrators by exploiting the unprotected newPassword field in the User element save flow.
Craft CMS versions >= 5.0.0-RC1 before 5.10.6 and >= 4.0.0-RC1 before 4.18.2 interpolate environment variables and secrets (via ${ENVVAR} strings in the elementId parameter) into Twig templates before rendering, even when the Twig sandbox is enabled. An authenticated attacker with control panel access can render a malicious sandboxed Twig template and, using a blind error-based technique across many requests, incrementally leak arbitrary environment variables and secrets. These can be abused to forge sessions (via CRAFTSECURITYKEY), escalate privileges, and steal database, SMTP, API, or blob storage credentials. Fixed in 5.10.6 and 4.18.2.
Craft CMS before 5.10.5 fails to persist updated credential counters after WebAuthn assertion validation in the passkey login endpoint. Attackers can replay captured login request bodies containing requestOptions and response to create additional authenticated sessions for victim accounts.
Craft CMS 5.0.0-RC1 through 5.10.5 contains an incorrect authorization vulnerability. A control-panel user holding only the viewCategories permission (without saveCategories) for a category group can permanently modify that group's category structure — reordering and re-parenting categories — via the structures/move-element action. The structureEditable flag is computed from the view permission rather than the save permission, and the StructuresController authorizes the mutating action on that read-time session grant without a save re-check. Because a category's URI is derived from its position in the structure, moving a category changes its URL and those of its descendants and can corrupt navigation menus built from the category taxonomy. The issue is fixed in 5.10.6.
Craft CMS versions >= 5.0.0-RC1 before 5.10.6 and >= 4.0.0-RC1 before 4.18.2 contain a server-side request forgery vulnerability in the GraphQL save<Volume>Asset mutation, which fetches an attacker-supplied URL server-side. The anti-SSRF validation is incomplete: validateIp() does not cover CGNAT (100.64.0.0/10) or NAT64 (64:ff9b::/96) ranges, and the only IP check runs after the request has already been issued. An attacker holding a GraphQL token scoped only to asset-creation permissions can disclose internal HTTP content from CGNAT/NAT64 targets, force outbound GET requests to internal hosts (including RFC1918, loopback, and metadata endpoints), and enumerate internal services.
Craft CMS 5.0.0-RC1 before 5.10.6 and 4.0.0-RC1 before 4.18.2 contain an arbitrary file read vulnerability. The create() Twig function restricts class instantiation using a 5-entry blocklist that does not include SplFileObject, allowing an authenticated administrator (with allowAdminChanges=true) to configure a malicious entry type title or URI format that instantiates SplFileObject in a non-sandboxed template context. When a user subsequently creates an entry in the affected section, arbitrary files on the server (such as .env containing the security key and database credentials) are read and rendered as entry titles.
Craft CMS versions >= 5.0.0-RC1 before 5.10.7 and >= 4.0.0-RC1 before 4.18.3 contain a remote code execution vulnerability in the Twig sandbox mechanism. Because Craft marks the ElementInterface as safe (via the AllowedInSandbox attribute) and the sandbox allowlisting extends to the entire class hierarchy (craft\base\Component up to yii\base\Component), an authenticated attacker with permission to access the control panel can render a malicious Twig template that abuses the yii\base\Component arbitrary function-call gadget to execute arbitrary code, even when the Twig sandbox is enabled via enableTwigSandbox().
A vulnerability was detected in Craft CMS up to 4.18.0.1. Affected is the function actionReorderSets of the file src/controllers/GlobalsController.php of the component reorder-sets Endpoint. The manipulation results in authorization bypass. The attack can be executed remotely. Upgrading to version 4.18.1 is able to address this issue. The patch is identified as 9bd05c91e6a7e6da5e949ec41a31c220c059aa04. The affected component should be upgraded.
Requirements:
Control panel access Permissions to edit an entry
Details
Control panel users with the ability to edit entries can execute unsandboxed Twig code via the HTTP Referrer header.
The issue happens when a user is saving entries. Strings for a signed redirect URL are being compiled as a Twig template via renderObjectTemplate(), and while a sandboxed alternative already exists (renderSandboxedObjectTemplate()), it is not used in this case. This signed URL can be specified by users, as it is reflected in the “Referer” HTTP request header, which is under attacker control.
This has been fixed in Craft 5.10.0. Affected users should update to that version or higher to get the fix.
Resources
https://github.com/craftcms/cms/pull/18680
Stored XSS via Structure entry title in table view
Summary
An Author-level control panel user can store a JavaScript payload in an entry title. When an admin, or any control panel user with saveEntries for the same Structure section, drags another entry under the poisoned entry in table view, the payload executes in the victim’s session.
The issue is exploitable because the title is escaped into data-title by the server, decoded again by the browser, read with jQuery .data('title'), and then concatenated into a new HTML string without attribute escaping.
Execution was verified with alert(document.domain). Impact was verified against an elevated admin session by changing the admin user’s email through users/save-user, then using the password-reset flow to take over the account.
Preconditions
- Attacker has createEntries + saveEntries on a Structure-type section (Author-style CP account; no admin permission required). - Victim has saveEntries on the same section. Craft only renders drag handles when structureEditable is true, which requires this permission, so any admin qualifies. - Section must be type Structure. Channel and Single sections are unaffected. - Poisoned entry must have no children at the time of the drag (fires on the 0-to-1 descendant transition). - For the email-change account-takeover path, the victim must currently have an elevated session. The stored XSS itself does not require elevation.
Root cause
ElementTableSorter.js lines 643-652:
js const ancestorTitle = this.updateAncestors.$ancestor.data('title'); $( '<button … aria-label="' + Craft.t('app', 'Show {title} children', {title: ancestorTitle}) + '"></button>' ).insertAfter(…);
Craft.t with a {title} token calls parseToken, reaches case 'none': return arg (Craft.js:193-194), and returns the title verbatim. The result is handed to jQuery's $() and parsed as HTML.
The server-side template correctly encodes the entry title into data-title, but the browser decodes that attribute before jQuery returns it from .data('title'). At that point the value is attacker-controlled HTML, and it is inserted into the aria-label attribute without Craft.escapeHtml().
Steps to reproduce
Plant (attacker - Author account):
bash python3 poc.py --url http://target --cp admin \ --user author@example.com --pass secret --section mySection
Trigger (victim - any control panel user with saveEntries, e.g. admin):
1. Open the section index in Table view. 2. Drag the "Drag me" entry and drop it as the first child of the poisoned entry.
alert(document.domain) fires in the victim’s session.
Impact payload tested in an elevated admin session (249 chars, within the 255-char title limit):
html "><img src=x onerror="fetch(Craft.actionUrl+'users/save-user',{method:'POST',body:Craft.csrfTokenName+'='+encodeURIComponent(Craft.csrfTokenValue)+'&userId=1&email=attacker%40evil.com',headers:{'Content-Type':'application/x-www-form-urlencoded'}})">
When triggered during an elevated admin session, the admin’s email is changed to the attacker-controlled address. The attacker can then request a password reset and receive the reset link.
Impact
Stored XSS in the control panel from an Author-level account. The payload runs as the victim control panel user and can use Craft.csrfTokenName / Craft.csrfTokenValue to send same-origin action requests as that user.
In my test environment, triggering the payload in an elevated admin session allowed Author-to-admin account takeover via admin email change and password reset.
Mitigating factors
- Requires an existing control panel account (Author role minimum). - Victim must perform a drag operation, not just visit the page. - The demonstrated email-change takeover requires the victim’s session to be elevated at trigger time.
Resources
https://github.com/craftcms/cms/commit/162321e899cc97517fb6f5a02b5528f549d0c6cc
Craft CMS is a content management system (CMS). In versions starting from 4.0.0-RC1 and prior to 4.18.0, and 5.0.0-RC1 and above, prior to 5.10.0, the dataUrl() Twig function is included in Craft’s Twig sandbox allowlist, allowing any control panel user granted the utility:system-messages permission to embed a file-reading payload into system email templates. When those emails are sent, the server reads the target file and returns its contents as a base64-encoded data URL embedded in the email body. The .env file, which typically contains the database password, CRAFTSECURITYKEY, and third-party API keys, passes all of Craft’s existing dataUrl() protection checks and is fully exfiltrated. Obtaining CRAFTSECURITYKEY enables an attacker to forge session tokens and escalate to full admin account takeover. This issue has been fixed in versions 4.18.0 and 5.10.0.
A flaw has been found in Craft CMS up to 4.18.0.1. Affected by this vulnerability is the function actionGetNewUsersData of the file src/controllers/ChartsController.php of the component Charts Endpoint. This manipulation of the argument userGroupId causes improper authorization. The attack is possible to be carried out remotely. Upgrading to version 4.18.1 addresses this issue. Patch name: 9ee53efc1314e6aba32771c66a13e072a246f4ce. It is suggested to upgrade the affected component.
Summary
There is a mass-assignment flaw in the bulk-duplicate element action. Alice, holding only the permission to duplicate an entry she owns, submits an arbitrary id through the newAttributes request parameter. The duplication routine overrides its own id = null reset with that value and writes Alice’s attributes into Bob’s existing entry row.
Details
ElementsController::beforeAction() (src/controllers/ElementsController.php:119-124) pulls the request body into $this->attributes and rejects requests that ship an id or canonicalId key at the top level:
php $this->attributes = $this->request->getBodyParams();
// No funny business if (isset($this->attributes['id']) || isset($this->attributes['canonicalId'])) { throw new BadRequestHttpException('Changing an element’s ID is not allowed.'); }
The check inspects only the top-level payload. actionBulkDuplicate() (src/controllers/ElementsController.php:1708-1749) reads a separate newAttributes array and passes it straight through to the service layer:
php $elementInfo = $this->request->getRequiredBodyParam('elements'); $newAttributes = $this->request->getRequiredBodyParam('newAttributes'); ... $safeNewAttributes = Collection::make($newAttributes) ->only($element->safeAttributes()) ->all(); ... $newElement = $elementsService->duplicateElement( $element, $safeNewAttributes + $element::baseBulkDuplicateAttributes(), false, checkAuthorization: true, );
Elements::duplicateElement() (src/services/Elements.php:1814-1840) clones the source element, sets id to null, and then hands the attacker's array to Craft::configure():
php $mainClone = clone $element; $mainClone->id = null; $mainClone->uid = StringHelper::UUID(); ... Craft::configure($mainClone, ArrayHelper::merge( $newAttributes, $siteAttributes[$mainClone->siteId] ?? [], ));
Craft::configure() overwrites the reset id with any numeric value inside $newAttributes. Yii's saveElement() then performs an UPDATE against the row with that primary key instead of an INSERT. Alice's title, slug, authorId, postDate, and UID land on Bob’s entry.
safeAttributes() on Entry includes id because the base element model exposes it, so the Collection::only() filter does not strip it.
Impact
A low-privileged author overwrites any other element (entries, categories, users that share the Entry element table inheritance) by predicting or enumerating element IDs. Content integrity on the entire install breaks. The attack requires only the ability to duplicate one entry Alice already owns.
Craft CMS is a content management system (CMS). Versions 5.0.0-RC1 and above, prior to 5.9.21 and versions 4.0.0-RC1 and above prior to 4.17.14 contain an authorization issue where a forced folder move can delete a conflicting destination folder without destination delete permission. Function craft\\controllers\\AssetsController::actionMoveFolder() supports moving an asset folder into a destination parent folder. If a folder with the same name already exists at the destination, the action can be called with force=true to overwrite the destination. This issue has been resolved in versions 5.9.21 and 4.17.14.
Summary
AssetsController::actionDeleteFolder() only requires the deleteAssets:<volume-uid> permission for the target folder. It never enforces deletePeerAssets:<volume-uid>, even though Assets::deleteFoldersByIds() cascades deletion to every descendant folder and every asset inside, regardless of who uploaded them. A low-privilege user who has been granted folder-management rights on a shared volume can therefore destroy assets uploaded by other users (peer assets), bypassing the per-asset peer-permission check that the sibling actionDeleteAsset endpoint correctly applies.
This is the same bug class that was just fixed in actionMoveFolder as GHSA-3w32-23wj-rxg3 (commit 05c2042, Apr 23 2026); the fix added requireVolumePermissionByFolder('deletePeerAssets', …) and savePeerAssets checks to the move endpoint but did not propagate to the delete-folder endpoint.
Details
src/controllers/AssetsController.php:552-569:
php public function actionDeleteFolder(): Response { $this->requireAcceptsJson(); $folderId = $this->request->getRequiredBodyParam('folderId');
$assets = Craft::$app->getAssets(); $folder = $assets->getFolderById($folderId);
if (!$folder) { throw new BadRequestHttpException('The folder cannot be found'); }
// Check if it's possible to delete objects in the target volume. $this->requireVolumePermissionByFolder('deleteAssets', $folder); // <-- only checks deleteAssets $assets->deleteFoldersByIds($folderId);
return $this->asSuccess(); }
requireVolumePermissionByFolder() (src/controllers/AssetsControllerTrait.php:75-88) only resolves to a single requirePermission('deleteAssets:<vol-uid>') call. The peer-equivalent helper (requirePeerVolumePermissionByAsset) is never invoked because there is no folder-level peer helper that iterates the folder's contents.
Assets::deleteFoldersByIds() (src/services/Assets.php:311-349) then enumerates the folder + every descendant folder, queries every asset under those IDs, and calls Craft::$app->getElements()->deleteElement($asset, true) directly:
php $assetQuery = Asset::find()->folderId($allFolderIds); $elementService = Craft::$app->getElements();
foreach (Db::each($assetQuery) as $asset) { $asset->keepFileOnDelete = !$deleteDir; $elementService->deleteElement($asset, true); }
This bypasses Asset::canDelete() (src/elements/Asset.php:1515-1536):
php public function canDelete(User $user): bool { if ($this->isFolder) { return false; } if (parent::canDelete($user)) { return true; } $volume = $this->getVolume(); if (Assets::isTempUploadFs($volume->getFs())) { return true; }
if ($this->uploaderId !== $user->id) { return $user->can("deletePeerAssets:$volume->uid"); // <-- never reached on cascade delete } return $user->can("deleteAssets:$volume->uid"); }
Compare to actionDeleteAsset (src/controllers/AssetsController.php:579-613), which correctly does:
php $this->requireVolumePermissionByAsset('deleteAssets', $asset); $this->requirePeerVolumePermissionByAsset('deletePeerAssets', $asset);
The fix that landed in 05c2042 for actionMoveFolder (src/controllers/AssetsController.php:733-765) added both savePeerAssets and deletePeerAssets requireVolumePermissionByFolder checks to mirror the per-asset pattern, but the same hardening was not applied to actionDeleteFolder or actionRenameFolder (which also calls deleteFoldersByIds indirectly through later logic).
The asymmetry between the two endpoints demonstrates the missing check.
Impact
- Integrity / availability of other users' assets on any volume where the attacker has deleteAssets but not deletePeerAssets: the attacker can permanently delete peer-owned files (and their parent folder structure) on the underlying filesystem, with no recovery via Craft's UI. - The Craft permission model explicitly distinguishes "delete your own assets" (deleteAssets) from "delete other users' assets" (deletePeerAssets) precisely so administrators can grant the former without the latter on shared volumes — this finding renders that distinction unenforceable for any user given folder-delete rights. - No information disclosure or remote code execution; impact is bounded to the affected volume's contents. - Does not require any non-default configuration: the affected endpoint is enabled by default and only requires that an administrator has split deleteAssets from deletePeerAssets (the documented, supported permission model).
Craft CMS is a content management system (CMS). Versions 5.0.0-RC1 through 5.9.20, and 4.0.0-RC1 through 4.17.13 contain an authorization issue in the AssetsController::actionReplaceFile that can delete a source asset without source delete permission by supplying both assetId and sourceAssetId. AssetsController::actionReplaceFile() supports replacing a target asset file using another existing asset as the source. The action loads: assetId -> $assetToReplace and sourceAssetId -> $sourceAsset, then enforces replace permissions using ($assetToReplace ?: $sourceAsset). When both IDs are provided, this expression resolves to the target asset so no permission check is performed against the source asset volume. When both assets are present, Craft copies the source file into the target and then deletes the source asset. There is no deletion check for for the source asset. An authenticated user who can replace files in one volume can delete assets in another volume where they do not have delete permission, as long as they can obtain a sourceAssetId, leading to broken content references and data loss. This issue has been fixed in versions 4.17.14 and 5.9.21.
Summary
The EntriesController::actionMoveToSection() endpoint checks only whether the current user can view the destination section, but it does not require permission to save entries into that section. A low-privileged authenticated control-panel user who can move an entry out of its current section can therefore move that entry into a different section where they have read access but no write access.
Details
The vulnerable route is implemented in EntriesController.php:465:
The destination check is only viewEntries:$section->uid . The source-entry gate is Entry::canMove(), which verifies whether the user can move the existing entry based on the source section:
This closes the exploit chain:
1. External source: authenticated CP request to entries/move-to-section. 2. Missing authorization check: destination section requires only viewEntries, not saveEntries. 3. Privileged sink: moveEntryToSection() rewrites sectionId and saves the entry into the unauthorized section.
Preconditions derived from the code:
1. The attacker is authenticated to the control panel. 2. Entry 345 is movable by the attacker from its current section. 3. The attacker can satisfy viewEntries on destination section 12. 4. The attacker does not have saveEntries:DESTINATIONUID, which is the missing check that makes the bypass possible.
Result:
1. The controller accepts the request because viewEntries:$section->uid passes. 2. Each source entry passes canMove() based on source-section permissions. 3. moveEntryToSection() updates the entry’s sectionId and saves it. 4. The entry is now located in a section where the attacker did not have write permission.
Impact
This breaks the intended section-level authorization model. A user with limited content permissions can inject or relocate content into a protected section, interfering with editorial boundaries, approval workflows, section-specific business logic, and content ownership expectations.
Summary
EntriesController::actionSaveEntry() performs entry-edit permission checks before request-controlled author changes are applied to the model. The subsequent author mutation path accepts attacker-supplied authors / author parameters and allows the change when the current user is one of the old authors. Because the controller does not re-run authorization after mutating the author list, a low-privileged user can reassign an entry’s authorship to another user without holding the dedicated peer-author-change permission.
Details The control flow begins in EntriesController.php:249. actionSaveEntry() loads the entry and enforces edit permissions before calling populateEntryModel():
php public function actionSaveEntry(bool $duplicate = false): ?Response { ... $entry = $this->editableEntry($this->request->getBodyParam('entryId'), $siteId); ... $this->enforceEditEntryPermissions($entry, $duplicate); ... $this->populateEntryModel($entry); ... $success = Craft::$app->getElements()->saveElement($entry); }
The attacker-controlled source is in EntriesController.php:588:
php $entry->setAttributesFromRequest(arrayfilter([ 'authorIds' => $this->request->getBodyParam('authors') ?? $this->request->getBodyParam('author') ?? $entry->getAuthorId() ?? static::currentUser()->id, ]));
Entry::setAttributesFromRequest() in Entry.php:1124 extracts the new author IDs and applies them if canChangeAuthor() returns true:
php if ( ($authorIds !== null || $authorId !== null) && $this->canChangeAuthor() ) { $this->oldAuthorIds = $oldAuthorIds; $this->setAuthorIds($authorIds); }
canChangeAuthor() at Entry.php:2789 allows the author change when the current user can view peer entries and is already one of the existing authors:
php return ( empty($authorIds) || inarray($user->id, $authorIds) || $user->can("changeAuthorForPeerEntries:$section->uid") );
After the author list is mutated, the controller does not re-check authorization.
This closes the exploit chain:
1. External source: authenticated request to entries/save-entry with attacker-controlled authors[]. 2. Trust boundary failure: authorization is checked on the pre-mutation entry state, not on the post-mutation author assignment. 3. Privileged sink: the author relationship is rewritten in persistent storage.
Preconditions derived from the source:
1. The attacker is authenticated and can edit entry 345. 2. The attacker is among the existing authors of entry 345, or otherwise satisfies canChangeAuthor() through the old author set. 3. The attacker has viewPeerEntries for the section. 4. User ID 1 exists and can be assigned as an author in that section.
Result:
1. enforceEditEntryPermissions() succeeds on the original entry state. 2. populateEntryModel() reads authors[]=1 from the request body. 3. setAttributesFromRequest() updates authorIds because canChangeAuthor() is evaluated against the old authorship state. 4. saveElement() persists the change and saveAuthors() rewrites the entry-author relation. 5. Entry 345 now appears authored by user 1.
Impact
This allows low-privileged users to falsify content ownership and alter the authorship of entries without having the dedicated author-management permission. The impact includes corrupted audit trails, misleading notifications, broken approval workflows, and unauthorized reassignment of content responsibility.
Craft CMS contains an external control of assumed-immutable web parameter vulnerability. This vulnerability could allow an unauthenticated client to introduce arbitrary values, such as PHP code, to a known local file location on the server. This vulnerability could be chained with CVE-2024-58136 as represented by CVE-2025-32432.
Impact
This is an additional fix for https://github.com/craftcms/cms/security/advisories/GHSA-4w8r-3xrw-v25g
This is a high-impact, low-complexity attack vector. To mitigate the issue, users running Craft installations before the fixed versions are encouraged to update to at least that version.
Details
https://craftcms.com/knowledge-base/craft-cms-cve-2025-32432
References
https://github.com/craftcms/cms/commit/e1c85441fa47eeb7c688c2053f25419bc0547b47
https://github.com/craftcms/cms/blob/3.x/CHANGELOG.md#3915---2025-04-10-critical https://github.com/craftcms/cms/blob/4.x/CHANGELOG.md#41415---2025-04-10-critical https://github.com/craftcms/cms/blob/5.x/CHANGELOG.md#5617---2025-04-10-critical
https://sensepost.com/blog/2025/investigating-an-in-the-wild-campaign-using-rce-in-craftcms/
Credits
Credit to Orange Cyberdefense for discovering a reporting this bug.
Impact
This is an RCE vulnerability that affects Craft 4 and 5 installs where your security key has already been compromised.
https://craftcms.com/knowledge-base/securing-craft#keep-your-secrets-secret
Anyone running an unpatched version of Craft with a compromised security key is affected.
Patches
This has been patched in Craft 5.5.8 and 4.13.8.
Workarounds
If you can't update to a patched version, then rotating your security key and ensuring its privacy will help to migitgate the issue.
References
https://github.com/craftcms/cms/commit/e59e22b30c9dd39e5e2c7fe02c147bcbd004e603
Impact You are affected if your php.ini configuration has registerargcargv enabled.
Patches Update to 3.9.14, 4.13.2, or 5.5.2.
Workarounds If you can't upgrade yet, and registerargcargv is enabled, you can disable it to mitigate the issue.
Summary XSS can be triggered by review volumes
PoC
1. Access setting tab 2. Create new assets 3. In assets name inject payload: "<script>alert(1337)</script> 4. Click Utilities tab 5. Choose all volumes, or volume trigger xss 6. Click Update asset indexes. 7. Wait to assets update success. 8. Progress complete. 9. Click on review button will trigger XSS
Root cause Function: index.php?p=admin/actions/asset-indexes/process-indexing-session&v=1680710595770 After loading completed, progess will load: "skippedEntries" and "missingEntries" These parameters is not yet filtered, I just tried "skippedEntries" but I think it will be work with "missingEntries"
My reponse: { "session": { "id": 10, "indexedVolumes": { "6": "\"<script>alert(1337)</script>" }, "totalEntries": 2235, "processedEntries": 2235, "cacheRemoteImages": true, "listEmptyFolders": false, "isCli": false, "actionRequired": true, "dateCreated": "Apr 5, 2023, 9:03:16 AM", "skippedEntries": [ "\"<script>alert(1337)</script>/assetpreviews/Image.php", "\"<script>alert(1337)</script>/assetpreviews/Pdf.php" ], "missingEntries": { "folders": [], "files": [] }, "processIfRootEmpty": false }, "skipDialog": false }
Resolved in https://github.com/craftcms/cms/commit/053d7119697e480ff81c5723bb9a33eaa49e0fc7
Summary The platform does not filter input and encode output in Quick Post validation error message, which can deliver an XSS payload.
Details Old CVE fixed the XSS in label HTML but didn’t fix it when clicking save.
PoC 1. Login at admin 2. Go to setting 3. Create a Section 4. On Entry page, click Edit label 5. Inject the XSS payload into the label and save 6. On the admin dashboard choose new widget -> Quick Post 7. In Quick Post, click save with blank slug; The XSS will be executed
"errors":{"title":["<script>alert('nono')</script> cannot be blank."],"slug":["Slug cannot be blank."]
Fixed in https://github.com/craftcms/cms/commit/9d0cd0bda7c8a830a3373f8c0f06943e519ac888
Craft CMS is a content management system (CMS). In versions 5.6.0 through 5.9.14, the actionSavePermissions() endpoint allows a user with only viewUsers permission to remove arbitrary users from all user groups. While saveUserGroups() enforces per-group authorization for additions, it performs no equivalent authorization check for removals, so submitting an empty groups value removes all existing group memberships. Version 5.9.15 contains a patch.
Craft CMS is a content management system (CMS). In versions on the 4.x branch through 4.17.8 and the 5.x branch through 5.9.14, the resource-js endpoint in Craft CMS allows unauthenticated requests to proxy remote JavaScript resources. When trustedHosts is not explicitly restricted (default configuration), the application trusts the client-supplied Host header. This allows an attacker to control the derived baseUrl, which is used in prefix validation inside actionResourceJs(). By supplying a malicious Host header, the attacker can make the server issue arbitrary HTTP requests, leading to Server-Side Request Forgery (SSRF). Versions 4.17.9 and 5.9.15 patch the issue.