From 0aea113f712083fd3213f4b5ab1f3faabe4e8205 Mon Sep 17 00:00:00 2001 From: shopyou Date: Tue, 19 Mar 2024 10:23:24 +0800 Subject: [PATCH 1/2] Add patches to fix CVE-2021-45115,CVE-2022-34265 --- 001-fix-CVE-2022-34265-for-release-3.2.patch | 128 ++++++++++++++ 002-fix-CVE-2021-45115-for-release-3.2.patch | 177 +++++++++++++++++++ python-django.spec | 8 +- 3 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 001-fix-CVE-2022-34265-for-release-3.2.patch create mode 100644 002-fix-CVE-2021-45115-for-release-3.2.patch diff --git a/001-fix-CVE-2022-34265-for-release-3.2.patch b/001-fix-CVE-2022-34265-for-release-3.2.patch new file mode 100644 index 0000000..a54d393 --- /dev/null +++ b/001-fix-CVE-2022-34265-for-release-3.2.patch @@ -0,0 +1,128 @@ +From a9010fe5555e6086a9d9ae50069579400ef0685e Mon Sep 17 00:00:00 2001 +From: Mariusz Felisiak +Date: Wed, 22 Jun 2022 12:44:04 +0200 +Subject: [PATCH] [3.2.x] Fixed CVE-2022-34265 -- Protected + Trunc(kind)/Extract(lookup_name) against SQL injection. + +Thanks Takuto Yoshikai (Aeye Security Lab) for the report. +--- + django/db/backends/base/operations.py | 3 ++ + django/db/models/functions/datetime.py | 4 +++ + docs/releases/3.2.14.txt | 11 ++++++ + .../datetime/test_extract_trunc.py | 34 +++++++++++++++++++ + 4 files changed, 52 insertions(+) + +diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py +index 0fcc607bcfb06..cdcd9885ba27d 100644 +--- a/django/db/backends/base/operations.py ++++ b/django/db/backends/base/operations.py +@@ -9,6 +9,7 @@ + from django.db.backends import utils + from django.utils import timezone + from django.utils.encoding import force_str ++from django.utils.regex_helper import _lazy_re_compile + + + class BaseDatabaseOperations: +@@ -53,6 +54,8 @@ class BaseDatabaseOperations: + # Prefix for EXPLAIN queries, or None EXPLAIN isn't supported. + explain_prefix = None + ++ extract_trunc_lookup_pattern = _lazy_re_compile(r"[\w\-_()]+") ++ + def __init__(self, connection): + self.connection = connection + self._cache = None +diff --git a/django/db/models/functions/datetime.py b/django/db/models/functions/datetime.py +index 90e6f41be0572..47651d281f194 100644 +--- a/django/db/models/functions/datetime.py ++++ b/django/db/models/functions/datetime.py +@@ -41,6 +41,8 @@ def __init__(self, expression, lookup_name=None, tzinfo=None, **extra): + super().__init__(expression, **extra) + + def as_sql(self, compiler, connection): ++ if not connection.ops.extract_trunc_lookup_pattern.fullmatch(self.lookup_name): ++ raise ValueError("Invalid lookup_name: %s" % self.lookup_name) + sql, params = compiler.compile(self.lhs) + lhs_output_field = self.lhs.output_field + if isinstance(lhs_output_field, DateTimeField): +@@ -192,6 +194,8 @@ def __init__(self, expression, output_field=None, tzinfo=None, is_dst=None, **ex + super().__init__(expression, output_field=output_field, **extra) + + def as_sql(self, compiler, connection): ++ if not connection.ops.extract_trunc_lookup_pattern.fullmatch(self.kind): ++ raise ValueError("Invalid kind: %s" % self.kind) + inner_sql, inner_params = compiler.compile(self.lhs) + tzname = None + if isinstance(self.lhs.output_field, DateTimeField): +diff --git a/docs/releases/3.2.14.txt b/docs/releases/3.2.14.txt +index bde1777376087..f5719798c1793 100644 +--- a/docs/releases/3.2.14.txt ++++ b/docs/releases/3.2.14.txt +@@ -5,3 +5,14 @@ Django 3.2.14 release notes + *July 4, 2022* + + Django 3.2.14 fixes a security issue with severity "high" in 3.2.13. ++ ++CVE-2022-34265: Potential SQL injection via ``Trunc(kind)`` and ``Extract(lookup_name)`` arguments ++================================================================================================== ++ ++:class:`Trunc() ` and ++:class:`Extract() ` database functions were ++subject to SQL injection if untrusted data was used as a ++``kind``/``lookup_name`` value. ++ ++Applications that constrain the lookup name and kind choice to a known safe ++list are unaffected. +diff --git a/tests/db_functions/datetime/test_extract_trunc.py b/tests/db_functions/datetime/test_extract_trunc.py +index 258600127f930..27ed3ae63ee5a 100644 +--- a/tests/db_functions/datetime/test_extract_trunc.py ++++ b/tests/db_functions/datetime/test_extract_trunc.py +@@ -177,6 +177,23 @@ def test_extract_year_lessthan_lookup(self): + self.assertEqual(qs.count(), 1) + self.assertGreaterEqual(str(qs.query).lower().count('extract'), 2) + ++ def test_extract_lookup_name_sql_injection(self): ++ start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321) ++ end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123) ++ if settings.USE_TZ: ++ start_datetime = timezone.make_aware(start_datetime) ++ end_datetime = timezone.make_aware(end_datetime) ++ self.create_model(start_datetime, end_datetime) ++ self.create_model(end_datetime, start_datetime) ++ ++ msg = "Invalid lookup_name: " ++ with self.assertRaisesMessage(ValueError, msg): ++ DTModel.objects.filter( ++ start_datetime__year=Extract( ++ "start_datetime", "day' FROM start_datetime)) OR 1=1;--" ++ ) ++ ).exists() ++ + def test_extract_func(self): + start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321) + end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123) +@@ -620,6 +637,23 @@ def test_extract_second_func(self): + ) + self.assertEqual(DTModel.objects.filter(start_datetime__second=ExtractSecond('start_datetime')).count(), 2) + ++ def test_trunc_lookup_name_sql_injection(self): ++ start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321) ++ end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123) ++ if settings.USE_TZ: ++ start_datetime = timezone.make_aware(start_datetime) ++ end_datetime = timezone.make_aware(end_datetime) ++ self.create_model(start_datetime, end_datetime) ++ self.create_model(end_datetime, start_datetime) ++ msg = "Invalid kind: " ++ with self.assertRaisesMessage(ValueError, msg): ++ DTModel.objects.filter( ++ start_datetime__date=Trunc( ++ "start_datetime", ++ "year', start_datetime)) OR 1=1;--", ++ ) ++ ).exists() ++ + def test_trunc_func(self): + start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321) + end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123) diff --git a/002-fix-CVE-2021-45115-for-release-3.2.patch b/002-fix-CVE-2021-45115-for-release-3.2.patch new file mode 100644 index 0000000..2de69d3 --- /dev/null +++ b/002-fix-CVE-2021-45115-for-release-3.2.patch @@ -0,0 +1,177 @@ +From a8b32fe13bcaed1c0b772fdc53de84abc224fb20 Mon Sep 17 00:00:00 2001 +From: Florian Apolloner +Date: Mon, 27 Dec 2021 14:48:03 +0100 +Subject: [PATCH] [3.2.x] Fixed CVE-2021-45115 -- Prevented DoS vector in + UserAttributeSimilarityValidator. + +Thanks Chris Bailey for the report. + +Co-authored-by: Adam Johnson +--- + django/contrib/auth/password_validation.py | 40 ++++++++++++++++++++-- + docs/releases/2.2.26.txt | 14 +++++++- + docs/releases/3.2.11.txt | 14 +++++++- + docs/topics/auth/passwords.txt | 14 +++++--- + tests/auth_tests/test_validators.py | 11 +++--- + 5 files changed, 78 insertions(+), 15 deletions(-) + +diff --git a/django/contrib/auth/password_validation.py b/django/contrib/auth/password_validation.py +index 845f4d86d5b23..7beb4bdc0ff28 100644 +--- a/django/contrib/auth/password_validation.py ++++ b/django/contrib/auth/password_validation.py +@@ -115,6 +115,36 @@ def get_help_text(self): + ) % {'min_length': self.min_length} + + ++def exceeds_maximum_length_ratio(password, max_similarity, value): ++ """ ++ Test that value is within a reasonable range of password. ++ ++ The following ratio calculations are based on testing SequenceMatcher like ++ this: ++ ++ for i in range(0,6): ++ print(10**i, SequenceMatcher(a='A', b='A'*(10**i)).quick_ratio()) ++ ++ which yields: ++ ++ 1 1.0 ++ 10 0.18181818181818182 ++ 100 0.019801980198019802 ++ 1000 0.001998001998001998 ++ 10000 0.00019998000199980003 ++ 100000 1.999980000199998e-05 ++ ++ This means a length_ratio of 10 should never yield a similarity higher than ++ 0.2, for 100 this is down to 0.02 and for 1000 it is 0.002. This can be ++ calculated via 2 / length_ratio. As a result we avoid the potentially ++ expensive sequence matching. ++ """ ++ pwd_len = len(password) ++ length_bound_similarity = max_similarity / 2 * pwd_len ++ value_len = len(value) ++ return pwd_len >= 10 * value_len and value_len < length_bound_similarity ++ ++ + class UserAttributeSimilarityValidator: + """ + Validate whether the password is sufficiently different from the user's +@@ -130,19 +160,25 @@ class UserAttributeSimilarityValidator: + + def __init__(self, user_attributes=DEFAULT_USER_ATTRIBUTES, max_similarity=0.7): + self.user_attributes = user_attributes ++ if max_similarity < 0.1: ++ raise ValueError('max_similarity must be at least 0.1') + self.max_similarity = max_similarity + + def validate(self, password, user=None): + if not user: + return + ++ password = password.lower() + for attribute_name in self.user_attributes: + value = getattr(user, attribute_name, None) + if not value or not isinstance(value, str): + continue +- value_parts = re.split(r'\W+', value) + [value] ++ value_lower = value.lower() ++ value_parts = re.split(r'\W+', value_lower) + [value_lower] + for value_part in value_parts: +- if SequenceMatcher(a=password.lower(), b=value_part.lower()).quick_ratio() >= self.max_similarity: ++ if exceeds_maximum_length_ratio(password, self.max_similarity, value_part): ++ continue ++ if SequenceMatcher(a=password, b=value_part).quick_ratio() >= self.max_similarity: + try: + verbose_name = str(user._meta.get_field(attribute_name).verbose_name) + except FieldDoesNotExist: +diff --git a/docs/releases/2.2.26.txt b/docs/releases/2.2.26.txt +index 12e9923a19f52..3444c491db45a 100644 +--- a/docs/releases/2.2.26.txt ++++ b/docs/releases/2.2.26.txt +@@ -7,4 +7,16 @@ Django 2.2.26 release notes + Django 2.2.26 fixes one security issue with severity "medium" and two security + issues with severity "low" in 2.2.25. + +-... ++CVE-2021-45115: Denial-of-service possibility in ``UserAttributeSimilarityValidator`` ++===================================================================================== ++ ++:class:`.UserAttributeSimilarityValidator` incurred significant overhead ++evaluating submitted password that were artificially large in relative to the ++comparison values. On the assumption that access to user registration was ++unrestricted this provided a potential vector for a denial-of-service attack. ++ ++In order to mitigate this issue, relatively long values are now ignored by ++``UserAttributeSimilarityValidator``. ++ ++This issue has severity "medium" according to the :ref:`Django security policy ++`. +diff --git a/docs/releases/3.2.11.txt b/docs/releases/3.2.11.txt +index b88f0f79ff8ad..621139033c2a6 100644 +--- a/docs/releases/3.2.11.txt ++++ b/docs/releases/3.2.11.txt +@@ -7,4 +7,16 @@ Django 3.2.11 release notes + Django 3.2.11 fixes one security issue with severity "medium" and two security + issues with severity "low" in 3.2.10. + +-... ++CVE-2021-45115: Denial-of-service possibility in ``UserAttributeSimilarityValidator`` ++===================================================================================== ++ ++:class:`.UserAttributeSimilarityValidator` incurred significant overhead ++evaluating submitted password that were artificially large in relative to the ++comparison values. On the assumption that access to user registration was ++unrestricted this provided a potential vector for a denial-of-service attack. ++ ++In order to mitigate this issue, relatively long values are now ignored by ++``UserAttributeSimilarityValidator``. ++ ++This issue has severity "medium" according to the :ref:`Django security policy ++`. +diff --git a/docs/topics/auth/passwords.txt b/docs/topics/auth/passwords.txt +index 52c90d574b426..8fc4ba6ed41a5 100644 +--- a/docs/topics/auth/passwords.txt ++++ b/docs/topics/auth/passwords.txt +@@ -539,10 +539,16 @@ Django includes four validators: + is used: ``'username', 'first_name', 'last_name', 'email'``. + Attributes that don't exist are ignored. + +- The minimum similarity of a rejected password can be set on a scale of 0 to +- 1 with the ``max_similarity`` parameter. A setting of 0 rejects all +- passwords, whereas a setting of 1 rejects only passwords that are identical +- to an attribute's value. ++ The maximum allowed similarity of passwords can be set on a scale of 0.1 ++ to 1.0 with the ``max_similarity`` parameter. This is compared to the ++ result of :meth:`difflib.SequenceMatcher.quick_ratio`. A value of 0.1 ++ rejects passwords unless they are substantially different from the ++ ``user_attributes``, whereas a value of 1.0 rejects only passwords that are ++ identical to an attribute's value. ++ ++ .. versionchanged:: 2.2.26 ++ ++ The ``max_similarity`` parameter was limited to a minimum value of 0.1. + + .. class:: CommonPasswordValidator(password_list_path=DEFAULT_PASSWORD_LIST_PATH) + +diff --git a/tests/auth_tests/test_validators.py b/tests/auth_tests/test_validators.py +index 393fbdd39c8f1..f4aaf33052901 100644 +--- a/tests/auth_tests/test_validators.py ++++ b/tests/auth_tests/test_validators.py +@@ -150,13 +150,10 @@ def test_validate(self): + max_similarity=1, + ).validate(user.first_name, user=user) + self.assertEqual(cm.exception.messages, [expected_error % "first name"]) +- # max_similarity=0 rejects all passwords. +- with self.assertRaises(ValidationError) as cm: +- UserAttributeSimilarityValidator( +- user_attributes=['first_name'], +- max_similarity=0, +- ).validate('XXX', user=user) +- self.assertEqual(cm.exception.messages, [expected_error % "first name"]) ++ # Very low max_similarity is rejected. ++ msg = 'max_similarity must be at least 0.1' ++ with self.assertRaisesMessage(ValueError, msg): ++ UserAttributeSimilarityValidator(max_similarity=0.09) + # Passes validation. + self.assertIsNone( + UserAttributeSimilarityValidator(user_attributes=['first_name']).validate('testclient', user=user) diff --git a/python-django.spec b/python-django.spec index 3a9ae6f..722a271 100644 --- a/python-django.spec +++ b/python-django.spec @@ -4,7 +4,7 @@ Summary: A high-level Python Web framework Name: python-django Version: 3.2 -Release: 1%{?dist} +Release: 2%{?dist} License: BSD URL: https://www.djangoproject.com/ Source0: https://github.com/django/django/archive/refs/tags/%{version}.tar.gz @@ -16,6 +16,9 @@ Patch0004: 15168.patch Patch0005: b61f44c339830ea53663415f00cbd17e2fd5aa43.patch Patch0006: 36fa071d6ebd18a61c4d7f1b5c9d17106134bd44.patch +Patch1001: 001-fix-CVE-2022-34265-for-release-3.2.patch +Patch1002: 002-fix-CVE-2021-45115-for-release-3.2.patch + BuildArch: noarch @@ -127,6 +130,9 @@ python3 runtests.py --settings=test_sqlite --verbosity=2 --parallel 1 %changelog +* Tue Mar 19 2024 Shop You - 3.2-2 +- Add patches to fix CVE-2021-45115,CVE-2022-34265 + * Sat Oct 07 2023 Miaojun Dong - 3.2-1 - Downgrade version to 3.2 to meet openstack-dashboard-23.0.0 Requirement -- Gitee From 4cbf1d915f256bb54b3e6741ce72c11e60bb4847 Mon Sep 17 00:00:00 2001 From: shopyou Date: Tue, 19 Mar 2024 16:00:46 +0800 Subject: [PATCH 2/2] fix patch --- 001-fix-CVE-2022-34265-for-release-3.2.patch | 20 --------- 002-fix-CVE-2021-45115-for-release-3.2.patch | 47 +------------------- 2 files changed, 1 insertion(+), 66 deletions(-) diff --git a/001-fix-CVE-2022-34265-for-release-3.2.patch b/001-fix-CVE-2022-34265-for-release-3.2.patch index a54d393..57ec782 100644 --- a/001-fix-CVE-2022-34265-for-release-3.2.patch +++ b/001-fix-CVE-2022-34265-for-release-3.2.patch @@ -8,7 +8,6 @@ Thanks Takuto Yoshikai (Aeye Security Lab) for the report. --- django/db/backends/base/operations.py | 3 ++ django/db/models/functions/datetime.py | 4 +++ - docs/releases/3.2.14.txt | 11 ++++++ .../datetime/test_extract_trunc.py | 34 +++++++++++++++++++ 4 files changed, 52 insertions(+) @@ -55,25 +54,6 @@ index 90e6f41be0572..47651d281f194 100644 inner_sql, inner_params = compiler.compile(self.lhs) tzname = None if isinstance(self.lhs.output_field, DateTimeField): -diff --git a/docs/releases/3.2.14.txt b/docs/releases/3.2.14.txt -index bde1777376087..f5719798c1793 100644 ---- a/docs/releases/3.2.14.txt -+++ b/docs/releases/3.2.14.txt -@@ -5,3 +5,14 @@ Django 3.2.14 release notes - *July 4, 2022* - - Django 3.2.14 fixes a security issue with severity "high" in 3.2.13. -+ -+CVE-2022-34265: Potential SQL injection via ``Trunc(kind)`` and ``Extract(lookup_name)`` arguments -+================================================================================================== -+ -+:class:`Trunc() ` and -+:class:`Extract() ` database functions were -+subject to SQL injection if untrusted data was used as a -+``kind``/``lookup_name`` value. -+ -+Applications that constrain the lookup name and kind choice to a known safe -+list are unaffected. diff --git a/tests/db_functions/datetime/test_extract_trunc.py b/tests/db_functions/datetime/test_extract_trunc.py index 258600127f930..27ed3ae63ee5a 100644 --- a/tests/db_functions/datetime/test_extract_trunc.py diff --git a/002-fix-CVE-2021-45115-for-release-3.2.patch b/002-fix-CVE-2021-45115-for-release-3.2.patch index 2de69d3..20f3ad7 100644 --- a/002-fix-CVE-2021-45115-for-release-3.2.patch +++ b/002-fix-CVE-2021-45115-for-release-3.2.patch @@ -9,8 +9,6 @@ Thanks Chris Bailey for the report. Co-authored-by: Adam Johnson --- django/contrib/auth/password_validation.py | 40 ++++++++++++++++++++-- - docs/releases/2.2.26.txt | 14 +++++++- - docs/releases/3.2.11.txt | 14 +++++++- docs/topics/auth/passwords.txt | 14 +++++--- tests/auth_tests/test_validators.py | 11 +++--- 5 files changed, 78 insertions(+), 15 deletions(-) @@ -84,50 +82,7 @@ index 845f4d86d5b23..7beb4bdc0ff28 100644 try: verbose_name = str(user._meta.get_field(attribute_name).verbose_name) except FieldDoesNotExist: -diff --git a/docs/releases/2.2.26.txt b/docs/releases/2.2.26.txt -index 12e9923a19f52..3444c491db45a 100644 ---- a/docs/releases/2.2.26.txt -+++ b/docs/releases/2.2.26.txt -@@ -7,4 +7,16 @@ Django 2.2.26 release notes - Django 2.2.26 fixes one security issue with severity "medium" and two security - issues with severity "low" in 2.2.25. - --... -+CVE-2021-45115: Denial-of-service possibility in ``UserAttributeSimilarityValidator`` -+===================================================================================== -+ -+:class:`.UserAttributeSimilarityValidator` incurred significant overhead -+evaluating submitted password that were artificially large in relative to the -+comparison values. On the assumption that access to user registration was -+unrestricted this provided a potential vector for a denial-of-service attack. -+ -+In order to mitigate this issue, relatively long values are now ignored by -+``UserAttributeSimilarityValidator``. -+ -+This issue has severity "medium" according to the :ref:`Django security policy -+`. -diff --git a/docs/releases/3.2.11.txt b/docs/releases/3.2.11.txt -index b88f0f79ff8ad..621139033c2a6 100644 ---- a/docs/releases/3.2.11.txt -+++ b/docs/releases/3.2.11.txt -@@ -7,4 +7,16 @@ Django 3.2.11 release notes - Django 3.2.11 fixes one security issue with severity "medium" and two security - issues with severity "low" in 3.2.10. - --... -+CVE-2021-45115: Denial-of-service possibility in ``UserAttributeSimilarityValidator`` -+===================================================================================== -+ -+:class:`.UserAttributeSimilarityValidator` incurred significant overhead -+evaluating submitted password that were artificially large in relative to the -+comparison values. On the assumption that access to user registration was -+unrestricted this provided a potential vector for a denial-of-service attack. -+ -+In order to mitigate this issue, relatively long values are now ignored by -+``UserAttributeSimilarityValidator``. -+ -+This issue has severity "medium" according to the :ref:`Django security policy -+`. + diff --git a/docs/topics/auth/passwords.txt b/docs/topics/auth/passwords.txt index 52c90d574b426..8fc4ba6ed41a5 100644 --- a/docs/topics/auth/passwords.txt -- Gitee