ElementsController::actionDuplicate() accepts a deleteProvisionalDraft request parameter. After successfully duplicating an element, if the source is a provisional draft and this flag is set, the controller calls deleteElement($element) directly, without performing a canDelete() authorization check. The duplicate operation itself is gated by canDuplicateAsDraft(), which, for Entry only, checks createEntries (no creatorId or deletePeerEntryDrafts requirement). A user with viewPeerEntryDrafts + createEntries (but no deletePeerEntryDrafts) can duplicate and delete another user’s provisional draft.
Impact
- Victim loses unsaved provisional draft (auto-saved work in progress).
- Attacker’s
createEntries (content creation) escalates to cross-user delete capability.
- A duplicate is created as the attacker’s own unpublished draft — the attacker gains the victim’s in-progress content.
Report ID: 1541
ElementsController::actionDuplicate()accepts adeleteProvisionalDraftrequest parameter. After successfully duplicating an element, if the source is a provisional draft and this flag is set, the controller callsdeleteElement($element)directly, without performing acanDelete()authorization check. The duplicate operation itself is gated bycanDuplicateAsDraft(), which, for Entry only, checkscreateEntries(nocreatorIdordeletePeerEntryDraftsrequirement). A user withviewPeerEntryDrafts + createEntries(but nodeletePeerEntryDrafts) can duplicate and delete another user’s provisional draft.Impact
createEntries(content creation) escalates to cross-user delete capability.Report ID: 1541