Medium severity5.5NVD Advisory· Published Feb 8, 2016· Updated May 6, 2026
CVE-2016-2048
CVE-2016-2048
Description
Django 1.9.x before 1.9.2, when ModelAdmin.save_as is set to True, allows remote authenticated users to bypass intended access restrictions and create ModelAdmin objects via the "Save as New" option when editing objects and leveraging the "change" permission.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | >= 1.9, < 1.9.2 | 1.9.2 |
Patches
1adbca5e4db42[1.9.x] Fixed incorrect permissions check for admin's "Save as new".
3 files changed · +46 −6
django/contrib/admin/options.py+6 −4 modified@@ -1343,6 +1343,10 @@ def changeform_view(self, request, object_id=None, form_url='', extra_context=No model = self.model opts = model._meta + + if request.method == 'POST' and '_saveasnew' in request.POST: + object_id = None + add = object_id is None if add: @@ -1360,10 +1364,6 @@ def changeform_view(self, request, object_id=None, form_url='', extra_context=No raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % { 'name': force_text(opts.verbose_name), 'key': escape(object_id)}) - if request.method == 'POST' and "_saveasnew" in request.POST: - object_id = None - obj = None - ModelForm = self.get_form(request, obj) if request.method == 'POST': form = ModelForm(request.POST, request.FILES, instance=obj) @@ -1426,6 +1426,8 @@ def changeform_view(self, request, object_id=None, form_url='', extra_context=No if request.method == 'POST' and not form_validated and "_saveasnew" in request.POST: context['show_save'] = False context['show_save_and_continue'] = False + # Use the change template instead of the add template. + add = False context.update(extra_context or {})
docs/releases/1.9.2.txt+11 −2 modified@@ -4,8 +4,17 @@ Django 1.9.2 release notes *Under development* -Django 1.9.2 fixes several bugs in 1.9.1 and makes a small backwards -incompatible change that hopefully doesn't affect any users. +Django 1.9.2 fixes a security regression in 1.9 and several bugs in 1.9.1. It +also makes a small backwards incompatible change that hopefully doesn't affect +any users. + +Security issue: User with "change" but not "add" permission can create objects for ``ModelAdmin``’s with ``save_as=True`` +========================================================================================================================= + +If a ``ModelAdmin`` uses ``save_as=True`` (not the default), the admin +provides an option when editing objects to "Save as new". A regression in +Django 1.9 prevented that form submission from raising a "Permission Denied" +error for users without the "add" permission. Backwards incompatible change: ``.py-tpl`` files rewritten in project/app templates ===================================================================================
tests/admin_views/tests.py+29 −0 modified@@ -1712,6 +1712,35 @@ def test_change_view(self): self.assertContains(response, 'login-form') self.client.get(reverse('admin:logout')) + def test_change_view_save_as_new(self): + """ + 'Save as new' should raise PermissionDenied for users without the 'add' + permission. + """ + change_dict_save_as_new = { + '_saveasnew': 'Save as new', + 'title': 'Ikke fordømt', + 'content': '<p>edited article</p>', + 'date_0': '2008-03-18', 'date_1': '10:54:39', + 'section': self.s1.pk, + } + article_change_url = reverse('admin:admin_views_article_change', args=(self.a1.pk,)) + + # Add user can perform "Save as new". + article_count = Article.objects.count() + self.client.force_login(self.adduser) + post = self.client.post(article_change_url, change_dict_save_as_new) + self.assertRedirects(post, self.index_url) + self.assertEqual(Article.objects.count(), article_count + 1) + self.client.logout() + + # Change user cannot perform "Save as new" (no 'add' permission). + article_count = Article.objects.count() + self.client.force_login(self.changeuser) + post = self.client.post(article_change_url, change_dict_save_as_new) + self.assertEqual(post.status_code, 403) + self.assertEqual(Article.objects.count(), article_count) + def test_delete_view(self): """Delete view should restrict access and actually delete items.""" delete_dict = {'post': 'yes'}
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-46x4-9jmv-jc8pghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2016-2048ghsaADVISORY
- github.com/django/django/commit/adbca5e4db42542575734b8e5d26961c8ada7265ghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/django/PYSEC-2016-14.yamlghsaWEB
- web.archive.org/web/20210123075529/http://www.securityfocus.com/bid/82329ghsaWEB
- web.archive.org/web/20211204051406/http://www.securitytracker.com/id/1034894ghsaWEB
- www.djangoproject.com/weblog/2016/feb/01/releases-192-and-189ghsaWEB
- www.securityfocus.com/bid/82329nvd
- www.securitytracker.com/id/1034894nvd
- www.djangoproject.com/weblog/2016/feb/01/releases-192-and-189/nvd
News mentions
0No linked articles in our index yet.