# From a 500 error to Django admin takeover

IDOR on Django user API leaked admin data; mass-assignment via PATCH set is_superuser/is_staff, granting Django admin takeover.

- Payout: $3,000
- Severity: critical
- Program: -
- Credited: Shashank
- Disclosed: 2020-11-03
- Write-up quality: 6/10
- Original: https://blog.shashank.co/2020/11/from-500-error-to-django-admin-takeover.html
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-shashank-co-2020-11-from-500-error-to-django-admin-takeover-html
- Tags: domain:web impact:ato impact:pii-leak impact:privesc kind:bounty platform:blog sev:critical tech:rest technique:recon vuln:access-control vuln:idor vuln:privilege-escalation

**Target:** Django REST user API /api/panel/v1/users/{id}/ and /admin
**Root cause:** User detail endpoint lacked object-level authorization (IDOR) and the serializer accepted privileged fields (is_superuser, is_staff, lms_role) on PATCH, allowing mass-assignment of role attributes.
**Steps:**
1. FFUF subdomains; note /api-docs/ returns 500 while random paths return 404, indicating existence
2. Sign up, then load /api-docs/ in a second tab to view Swagger UI
3. Enumerate `GET /api/panel/v1/users/{id}/` for id 1-100, harvest PII from 200 responses
4. Search dev names via `GET /api/panel/v1/users/?page=1&page_size=1&search=NAME` to find superuser JSON
5. POST privileged JSON to own id returns 500; retry with `PATCH /api/panel/v1/users/{id}/` body `{"is_superuser":true,"is_staff":true,"lms_role":"super_admin"}` returns 200
6. Log in and access /admin as superuser
**How found:** Noticed /api-docs/ returned 500 vs 404 for nonexistent paths, implying the endpoint existed but was gated; after signup, refreshing that tab in an authenticated session revealed Swagger docs. Then spotted is_staff/is_superuser fields in user JSON and hypothesized they were writable.
**Exploitation:** Authenticated low-priv user enumerated sequential user IDs to leak PII, identified admin accounts via search, then PATCHed own user record with privileged role fields to become superuser and log into Django admin.
**Escalation:** Chained IDOR info leak (to discover role schema and admin identities) with mass-assignment privilege escalation to full Django admin takeover.
**Insight:** Differential status codes (500 vs 404) reveal hidden endpoints; re-requesting a gated path after authentication often bypasses front-end gating. When a user object exposes role fields, try PATCH (not just POST) to mass-assign them.
