From e1d787f1b36d13b95187f8f425425ae1b98da188 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Mon, 24 May 2021 09:55:14 +0200 Subject: [PATCH] Fixed CVE-2021-33571 -- Prevented leading zeros in IPv4 addresses. validate_ipv4_address() was affected only on Python < 3.9.5, see [1]. URLValidator() uses a regular expressions and it was affected on all Python versions. [1] https://bugs.python.org/issue36384 --- django/core/validators.py | 15 ++++++++++++++- docs/releases/2.2.24.txt | 13 +++++++++++++ docs/releases/3.1.12.txt | 13 +++++++++++++ docs/releases/3.2.4.txt | 13 +++++++++++++ tests/validators/invalid_urls.txt | 8 ++++++++ tests/validators/tests.py | 20 ++++++++++++++++++++ tests/validators/valid_urls.txt | 6 ++++++ 7 files changed, 87 insertions(+), 1 deletion(-) diff --git a/django/core/validators.py b/django/core/validators.py index f9abec602c..731ccf2d46 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -66,7 +66,7 @@ class URLValidator(RegexValidator): ul = '\u00a1-\uffff' # Unicode letters range (must not be a raw string). # IP patterns - ipv4_re = r'(?:25[0-5]|2[0-4]\d|[0-1]?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}' + ipv4_re = r'(?:0|25[0-5]|2[0-4]\d|1\d?\d?|[1-9]\d?)(?:\.(?:0|25[0-5]|2[0-4]\d|1\d?\d?|[1-9]\d?)){3}' ipv6_re = r'\[[0-9a-f:.]+\]' # (simple regex, validated later) # Host patterns @@ -276,6 +276,19 @@ def validate_ipv4_address(value): ipaddress.IPv4Address(value) except ValueError: raise ValidationError(_('Enter a valid IPv4 address.'), code='invalid', params={'value': value}) + else: + # Leading zeros are forbidden to avoid ambiguity with the octal + # notation. This restriction is included in Python 3.9.5+. + # TODO: Remove when dropping support for PY39. + if any( + octet != '0' and octet[0] == '0' + for octet in value.split('.') + ): + raise ValidationError( + _('Enter a valid IPv4 address.'), + code='invalid', + params={'value': value}, + ) def validate_ipv6_address(value): diff --git a/docs/releases/2.2.24.txt b/docs/releases/2.2.24.txt index 9bcf7037c4..1064fc53a0 100644 --- a/docs/releases/2.2.24.txt +++ b/docs/releases/2.2.24.txt @@ -17,3 +17,16 @@ the existence but also the file contents would have been exposed. As a mitigation, path sanitation is now applied and only files within the template root directories can be loaded. + +CVE-2021-33571: Possible indeterminate SSRF, RFI, and LFI attacks since validators accepted leading zeros in IPv4 addresses +=========================================================================================================================== + +:class:`~django.core.validators.URLValidator`, +:func:`~django.core.validators.validate_ipv4_address`, and +:func:`~django.core.validators.validate_ipv46_address` didn't prohibit leading +zeros in octal literals. If you used such values you could suffer from +indeterminate SSRF, RFI, and LFI attacks. + +:func:`~django.core.validators.validate_ipv4_address` and +:func:`~django.core.validators.validate_ipv46_address` validators were not +affected on Python 3.9.5+. diff --git a/docs/releases/3.1.12.txt b/docs/releases/3.1.12.txt index 7d8ee8447e..0700f40849 100644 --- a/docs/releases/3.1.12.txt +++ b/docs/releases/3.1.12.txt @@ -17,3 +17,16 @@ the existence but also the file contents would have been exposed. As a mitigation, path sanitation is now applied and only files within the template root directories can be loaded. + +CVE-2021-33571: Possible indeterminate SSRF, RFI, and LFI attacks since validators accepted leading zeros in IPv4 addresses +=========================================================================================================================== + +:class:`~django.core.validators.URLValidator`, +:func:`~django.core.validators.validate_ipv4_address`, and +:func:`~django.core.validators.validate_ipv46_address` didn't prohibit leading +zeros in octal literals. If you used such values you could suffer from +indeterminate SSRF, RFI, and LFI attacks. + +:func:`~django.core.validators.validate_ipv4_address` and +:func:`~django.core.validators.validate_ipv46_address` validators were not +affected on Python 3.9.5+. diff --git a/docs/releases/3.2.4.txt b/docs/releases/3.2.4.txt index 7c1b195d00..496ea6ec3f 100644 --- a/docs/releases/3.2.4.txt +++ b/docs/releases/3.2.4.txt @@ -18,6 +18,19 @@ the existence but also the file contents would have been exposed. As a mitigation, path sanitation is now applied and only files within the template root directories can be loaded. +CVE-2021-33571: Possible indeterminate SSRF, RFI, and LFI attacks since validators accepted leading zeros in IPv4 addresses +=========================================================================================================================== + +:class:`~django.core.validators.URLValidator`, +:func:`~django.core.validators.validate_ipv4_address`, and +:func:`~django.core.validators.validate_ipv46_address` didn't prohibit leading +zeros in octal literals. If you used such values you could suffer from +indeterminate SSRF, RFI, and LFI attacks. + +:func:`~django.core.validators.validate_ipv4_address` and +:func:`~django.core.validators.validate_ipv46_address` validators were not +affected on Python 3.9.5+. + Bugfixes ======== diff --git a/tests/validators/invalid_urls.txt b/tests/validators/invalid_urls.txt index 3a92bbb9b4..86a080bf33 100644 --- a/tests/validators/invalid_urls.txt +++ b/tests/validators/invalid_urls.txt @@ -46,6 +46,14 @@ http://1.1.1.1.1 http://123.123.123 http://3628126748 http://123 +http://000.000.000.000 +http://016.016.016.016 +http://192.168.000.001 +http://01.2.3.4 +http://01.2.3.4 +http://1.02.3.4 +http://1.2.03.4 +http://1.2.3.04 http://.www.foo.bar/ http://.www.foo.bar./ http://[::1:2::3]:8080/ diff --git a/tests/validators/tests.py b/tests/validators/tests.py index 09d5c40ff5..e39d0e3a1c 100644 --- a/tests/validators/tests.py +++ b/tests/validators/tests.py @@ -136,6 +136,16 @@ TEST_DATA = [ (validate_ipv4_address, '1.1.1.1\n', ValidationError), (validate_ipv4_address, '٧.2٥.3٣.243', ValidationError), + # Leading zeros are forbidden to avoid ambiguity with the octal notation. + (validate_ipv4_address, '000.000.000.000', ValidationError), + (validate_ipv4_address, '016.016.016.016', ValidationError), + (validate_ipv4_address, '192.168.000.001', ValidationError), + (validate_ipv4_address, '01.2.3.4', ValidationError), + (validate_ipv4_address, '01.2.3.4', ValidationError), + (validate_ipv4_address, '1.02.3.4', ValidationError), + (validate_ipv4_address, '1.2.03.4', ValidationError), + (validate_ipv4_address, '1.2.3.04', ValidationError), + # validate_ipv6_address uses django.utils.ipv6, which # is tested in much greater detail in its own testcase (validate_ipv6_address, 'fe80::1', None), @@ -161,6 +171,16 @@ TEST_DATA = [ (validate_ipv46_address, '::zzz', ValidationError), (validate_ipv46_address, '12345::', ValidationError), + # Leading zeros are forbidden to avoid ambiguity with the octal notation. + (validate_ipv46_address, '000.000.000.000', ValidationError), + (validate_ipv46_address, '016.016.016.016', ValidationError), + (validate_ipv46_address, '192.168.000.001', ValidationError), + (validate_ipv46_address, '01.2.3.4', ValidationError), + (validate_ipv46_address, '01.2.3.4', ValidationError), + (validate_ipv46_address, '1.02.3.4', ValidationError), + (validate_ipv46_address, '1.2.03.4', ValidationError), + (validate_ipv46_address, '1.2.3.04', ValidationError), + (validate_comma_separated_integer_list, '1', None), (validate_comma_separated_integer_list, '12', None), (validate_comma_separated_integer_list, '1,2', None), diff --git a/tests/validators/valid_urls.txt b/tests/validators/valid_urls.txt index 3f8bf839b0..53fde9df7f 100644 --- a/tests/validators/valid_urls.txt +++ b/tests/validators/valid_urls.txt @@ -71,6 +71,12 @@ http://0.0.0.0/ http://255.255.255.255 http://224.0.0.0 http://224.1.1.1 +http://111.112.113.114/ +http://88.88.88.88/ +http://11.12.13.14/ +http://10.20.30.40/ +http://1.2.3.4/ +http://127.0.01.09.home.lan http://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com http://example.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com http://example.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa