GHSA-X5M4-G2CQ-52PQ

Vulnerability from github – Published: 2026-07-02 20:03 – Updated: 2026-07-02 20:03
VLAI
Summary
Craft CMS's mass assignment via id in newAttributes during bulk duplicate overwrites existing elements
Details

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:

$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:

$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():

$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.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "craftcms/cms"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "5.7.0"
            },
            {
              "fixed": "5.9.21"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-50281"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-915"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-02T20:03:36Z",
    "nvd_published_at": "2026-07-02T17:17:00Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nThere 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\u2019s attributes into Bob\u2019s existing entry row.\n\n## Details\n\n`ElementsController::beforeAction()` (`src/controllers/ElementsController.php:119-124`) pulls the request body into `$this-\u003e_attributes` and rejects requests that ship an `id` or `canonicalId` key at the top level:\n\n```php\n$this-\u003e_attributes = $this-\u003erequest-\u003egetBodyParams();\n\n// No funny business\nif (isset($this-\u003e_attributes[\u0027id\u0027]) || isset($this-\u003e_attributes[\u0027canonicalId\u0027])) {\n    throw new BadRequestHttpException(\u0027Changing an element\u2019s ID is not allowed.\u0027);\n}\n```\n\nThe 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:\n\n```php\n$elementInfo = $this-\u003erequest-\u003egetRequiredBodyParam(\u0027elements\u0027);\n$newAttributes = $this-\u003erequest-\u003egetRequiredBodyParam(\u0027newAttributes\u0027);\n...\n$safeNewAttributes = Collection::make($newAttributes)\n    -\u003eonly($element-\u003esafeAttributes())\n    -\u003eall();\n...\n$newElement = $elementsService-\u003eduplicateElement(\n    $element,\n    $safeNewAttributes + $element::baseBulkDuplicateAttributes(),\n    false,\n    checkAuthorization: true,\n);\n```\n\n`Elements::duplicateElement()` (`src/services/Elements.php:1814-1840`) clones the source element, sets `id` to null, and then hands the attacker\u0027s array to `Craft::configure()`:\n\n```php\n$mainClone = clone $element;\n$mainClone-\u003eid = null;\n$mainClone-\u003euid = StringHelper::UUID();\n...\nCraft::configure($mainClone, ArrayHelper::merge(\n    $newAttributes,\n    $siteAttributes[$mainClone-\u003esiteId] ?? [],\n));\n```\n\n`Craft::configure()` overwrites the reset `id` with any numeric value inside `$newAttributes`. Yii\u0027s `saveElement()` then performs an UPDATE against the row with that primary key instead of an INSERT. Alice\u0027s title, slug, authorId, postDate, and UID land on Bob\u2019s entry.\n\n`safeAttributes()` on `Entry` includes `id` because the base element model exposes it, so the `Collection::only()` filter does not strip it.\n\n## Impact\n\nA 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.",
  "id": "GHSA-x5m4-g2cq-52pq",
  "modified": "2026-07-02T20:03:36Z",
  "published": "2026-07-02T20:03:36Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/craftcms/cms/security/advisories/GHSA-x5m4-g2cq-52pq"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-50281"
    },
    {
      "type": "WEB",
      "url": "https://github.com/craftcms/cms/commit/8f6587c25050bbb6e080d59c71f6bb8932fc8600"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/craftcms/cms"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Craft CMS\u0027s mass assignment via id in newAttributes during bulk duplicate overwrites existing elements"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…