Listen to this Post
The vulnerability arises from a complete absence of object‑level permission validation within the page duplication workflow. When a staff user initiates a duplicate operation via the admin interface, the system invokes `PageAdmin.has_add_permission` as the sole gatekeeper. This built‑in check only verifies that the user possesses the generic `cms.add_page` permission or can add a sub‑page under a given parent – it does not, however, interrogate whether that user has any legitimate relationship (view, change, or ownership) to the source page being copied.
The root of the problem lies in cms/admin/forms.py, where `DuplicatePageForm.source` is defined as a `ModelChoiceField` with a queryset of Page.objects.all(). This unfiltered queryset exposes every page across all sites and tenants to the dropdown, even though the widget is hidden from the UI. When the form initializes, `AddPageForm.__init__` detects the hidden source widget and returns early, bypassing the usual logic that would narrow the queryset to the user’s permitted site or subtree. Consequently, the source parameter is never scoped to the current user’s domain.
During POST submission, the form’s `clean()` method validates only the new page’s URL uniqueness; the `source` value is completely omitted from any permission check. Crucially, cms/admin/pageadmin.py’s `duplicate()` method seeds the source from the URL parameter only on GET requests. On POST, the source ID is taken verbatim from the request body, allowing an attacker to supply any arbitrary page ID. The subsequent `AddPageForm.save()` calls source.copy(..., permissions=False), which clones every placeholder and all plugins from the victim page into a new page belonging to the attacker’s site. Because `permissions=False` explicitly drops the source’s view restrictions, the newly created copy becomes publicly readable without any access controls.
This effectively breaks multi‑tenancy and site isolation. A staff user who is restricted via `CMS_PERMISSION` to their own site or subtree can exfiltrate restricted content, including pages from other tenants, without ever needing view or change permissions on the original. The read‑back is trivial: after duplication, the attacker’s copy is unrestricted, `user_can_view_page()` returns True, and the content is accessible even to anonymous visitors directly from the front‑end. No further permissions are required on the new page. The flaw bypasses the intended privilege boundary and enables a low‑privileged editor to leak sensitive information across the entire CMS installation.
DailyCVE Form:
Platform: Django CMS
Version: Up to 3.11.2
Vulnerability: Privilege Bypass
Severity: Critical
date: 2023-08-14
Prediction: Patched Aug 2023
What Undercode Say:
Identify if the target CMS runs an affected version (e.g., 3.11.2) curl -s -I https://target.example.com/admin/ | grep "Server:" PoC: Use an attacker-owned page content ID (e.g., 123) as the URL parameter, and supply the victim page ID (e.g., 456) in the POST body. curl -X POST "https://target.example.com/admin/cms/pagecontent/123/duplicate/" \ -H "Cookie: sessionid=attacker_session_id" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "csrfmiddlewaretoken=...&=exfil&slug=exfil&language=en&source=456"
Exploit: (Educational Purposes!)
- Log in as a staff user who holds `cms.add_page` but lacks view/change rights on the target (secret) page with ID
SECRET_ID. - Obtain a page content ID (
MY_OWN_ID) that the attacker can already see (e.g., a page they created). - Craft a POST request to `/admin/cms/pagecontent/
/duplicate/` with the payload source=<SECRET_ID>. - The server creates a new unrestricted page under the attacker’s site, containing a verbatim copy of all plugins and content from
SECRET_ID. - Preview or visit the new page’s front‑end URL to read the exfiltrated content – no further authentication is required because `permissions=False` stripped all view restrictions.
Protection: from this CVE
- Apply the official patch immediately (upgrade to django CMS 3.11.3 or 3.10.7+, where `clean_source()` enforces
user_can_view_page). - If patching is not possible, revoke the `cms.add_page` permission from all untrusted or delegated staff users.
- Completely disable the duplicate action in the admin by overriding `get_urls()` and removing the `duplicate` endpoint until the fix is deployed.
Impact:
- A staff member with only page‑creation privileges can clone any page in the entire CMS instance, including restricted, confidential, or cross‑tenant pages.
- The duplicated content becomes publicly accessible, leading to full data leakage of sensitive information, business secrets, or private user data.
- Multi‑site and tenant isolation are completely broken, allowing horizontal privilege escalation across organisational boundaries.
- The attack requires no additional permissions and can be executed with a single HTTP request, making it highly practical and low‑effort for malicious insiders or compromised staff accounts.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

