Skip to content

Commit c4bd5b5

Browse files
committed
Fixed CVE-2018-16984 -- Fixed password hash disclosure to admin "view only" users.
Thanks Claude Paroz & Tim Graham for collaborating on the patch. # Conflicts: # tests/auth_tests/test_views.py
1 parent f86100a commit c4bd5b5

File tree

4 files changed

+44
-3
lines changed

4 files changed

+44
-3
lines changed

django/contrib/admin/helpers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ def contents(self):
202202
except (AttributeError, ValueError, ObjectDoesNotExist):
203203
result_repr = self.empty_value_display
204204
else:
205+
if field in self.form.fields:
206+
widget = self.form[field].field.widget
207+
# This isn't elegant but suffices for contrib.auth's
208+
# ReadOnlyPasswordHashWidget.
209+
if getattr(widget, 'read_only', False):
210+
return widget.render(field, value)
205211
if f is None:
206212
if getattr(attr, 'boolean', False):
207213
result_repr = _boolean_icon(value)

django/contrib/auth/forms.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
class ReadOnlyPasswordHashWidget(forms.Widget):
2424
template_name = 'auth/widgets/read_only_password_hash.html'
25+
read_only = True
2526

2627
def get_context(self, name, value, attrs):
2728
context = super().get_context(name, value, attrs)

docs/releases/2.1.2.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,17 @@ Django 2.1.2 release notes
44

55
*Expected October 1, 2018*
66

7-
Django 2.1.2 fixes several bugs in 2.1.1. Also, the latest string translations
8-
from Transifex are incorporated.
7+
Django 2.1.2 fixes a security issue and several bugs in 2.1.1. Also, the latest
8+
string translations from Transifex are incorporated.
9+
10+
CVE-2018-16984: Password hash disclosure to "view only" admin users
11+
===================================================================
12+
13+
If an admin user has the change permission to the user model, only part of the
14+
password hash is displayed in the change form. Admin users with the view (but
15+
not change) permission to the user model were displayed the entire hash. While
16+
it's typically infeasible to reverse a strong password hash, if your site uses
17+
weaker password hashing algorithms such as MD5 or SHA1, it could be a problem.
918

1019
Bugfixes
1120
========

tests/auth_tests/test_views.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
from django.contrib.auth.forms import (
1515
AuthenticationForm, PasswordChangeForm, SetPasswordForm,
1616
)
17-
from django.contrib.auth.models import User
17+
from django.contrib.auth.models import Permission, User
1818
from django.contrib.auth.views import (
1919
INTERNAL_RESET_SESSION_TOKEN, LoginView, logout_then_login,
2020
redirect_to_login,
2121
)
22+
from django.contrib.contenttypes.models import ContentType
2223
from django.contrib.sessions.middleware import SessionMiddleware
2324
from django.contrib.sites.requests import RequestSite
2425
from django.core import mail
@@ -1115,6 +1116,11 @@ def test_logout_redirect_url_named_setting(self):
11151116
self.assertRedirects(response, '/logout/', fetch_redirect_response=False)
11161117

11171118

1119+
def get_perm(Model, perm):
1120+
ct = ContentType.objects.get_for_model(Model)
1121+
return Permission.objects.get(content_type=ct, codename=perm)
1122+
1123+
11181124
# Redirect in test_user_change_password will fail if session auth hash
11191125
# isn't updated after password change (#21649)
11201126
@override_settings(ROOT_URLCONF='auth_tests.urls_admin')
@@ -1221,6 +1227,25 @@ def test_password_change_bad_url(self):
12211227
response = self.client.get(reverse('auth_test_admin:auth_user_password_change', args=('foobar',)))
12221228
self.assertEqual(response.status_code, 404)
12231229

1230+
def test_view_user_password_is_readonly(self):
1231+
u = User.objects.get(username='testclient')
1232+
u.is_superuser = False
1233+
u.save()
1234+
u.user_permissions.add(get_perm(User, 'view_user'))
1235+
response = self.client.get(reverse('auth_test_admin:auth_user_change', args=(u.pk,)),)
1236+
algo, salt, hash_string = (u.password.split('$'))
1237+
self.assertContains(response, '<div class="readonly">testclient</div>')
1238+
# ReadOnlyPasswordHashWidget is used to render the field.
1239+
self.assertContains(
1240+
response,
1241+
'<strong>algorithm</strong>: %s\n\n'
1242+
'<strong>salt</strong>: %s**********\n\n'
1243+
'<strong>hash</strong>: %s**************************\n\n' % (
1244+
algo, salt[:2], hash_string[:6],
1245+
),
1246+
html=True,
1247+
)
1248+
12241249

12251250
@override_settings(
12261251
AUTH_USER_MODEL='auth_tests.UUIDUser',

0 commit comments

Comments
 (0)