medium

CVE-2026-63003

PyPI · django-cms

Summary

django CMS: Broken access control in page *Duplicate* allows reading the content of any page (cross-site / restriction bypass)

Severity
medium
CVSS
6.5
EPSS
0.4% (p36)
CWE
CWE-639, CWE-862
Also known as
GHSA-6x92-6vx4-5fwr
Published
2026-08-20
Updated
2026-08-20

Advisory details

Impact

The only authorization gate on the duplicate flow is PageAdmin.has_add_permission, which checks user_can_add_page(user, site) / user_can_add_subpage(...) — i.e. “may this user create a page at all”. Nothing checks the user’s relationship to the page being copied:

This crosses a real privilege boundary: a staff user restricted (via CMS_PERMISSION) to their own site or subtree can exfiltrate the content of restricted pages and of pages belonging to other tenants.

Read-back is trivial (verified): the copy is created on the attacker’s site and, because copy(..., permissions=False) strips the source’s view restrictions, the new page is unrestricted. user_can_view_page() then returns True for it (unrestricted + PUBLIC_FOR), so the attacker — or even an anonymous visitor — can read the duplicated content directly from the front end. No further permission on the new page is required.

Proof of concept

  1. Log in as a staff user attacker who has add page permission but no view/change permission on a target (secret / other-site) page SECRET_ID.
  2. Send (the URL <id> only needs to be a PageContent the attacker can already see — e.g. one of their own pages; the victim id goes in the POST body):
POST /admin/cms/pagecontent/<MY_OWN_PAGECONTENT_ID>/duplicate/ HTTP/1.1
Cookie: sessionid=<attacker session>
Content-Type: application/x-www-form-urlencoded

csrfmiddlewaretoken=...&title=x&slug=x&language=en&source=<SECRET_ID>
  1. A new, unrestricted page is created under the attacker’s site containing a verbatim copy of the secret page’s plugins, which the attacker can now preview/edit/read.

Patches

Enforce an object-level permission check on source:

class DuplicatePageForm(AddPageForm):
    source = forms.ModelChoiceField(
        queryset=Page.objects.all(),
        required=True,
        widget=forms.HiddenInput(),
    )

    def clean_source(self):
        source = self.cleaned_data.get("source")
        if source and not user_can_view_page(self._user, source):
            raise ValidationError(_("You do not have permission to copy this page."))
        return source

(user_can_view_page is imported from cms.utils.page_permissions.)

Workarounds

Until patched, restrict access to the cms.add_page permission to fully-trusted staff, or disable the duplicate action for delegated/limited editors.

References

References

Related advisories

Is your project exposed to this? Stateward checks every dependency on every pull request and flags it only if your code actually reaches it.

Check my repo

Summarize with AI

ChatGPTClaudePerplexity

Sources: CISA KEV (public domain), OSV.dev & GitHub Advisory Database (CC-BY-4.0), FIRST EPSS, NVD/CWE (public domain). Served live from the Stateward advisory database.