Skip to content

Commit 4190ca2

Browse files
amiremohamadihroncok
authored andcommitted
00354-cve-2020-26116-http-request-method-crlf-injection-in-httplib.patch
00354 # Reject control chars in HTTP method in httplib.putrequest to prevent HTTP header injection Backported from Python 3.5-3.10 (and adjusted for py2's single-module httplib): - https://bugs.python.org/issue39603 - python#18485 (3.10) - python#21946 (3.5) Co-authored-by: AMIR <[email protected]>
1 parent 46d8c85 commit 4190ca2

File tree

3 files changed

+41
-0
lines changed

3 files changed

+41
-0
lines changed

Lib/httplib.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@
257257
# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$")
258258
# We are more lenient for assumed real world compatibility purposes.
259259

260+
# These characters are not allowed within HTTP method names
261+
# to prevent http header injection.
262+
_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]')
263+
260264
# We always set the Content-Length header for these methods because some
261265
# servers will otherwise respond with a 411
262266
_METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
@@ -935,6 +939,8 @@ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
935939
else:
936940
raise CannotSendRequest()
937941

942+
self._validate_method(method)
943+
938944
# Save the method for use later in the response phase
939945
self._method = method
940946

@@ -1020,6 +1026,16 @@ def _encode_request(self, request):
10201026
# On Python 2, request is already encoded (default)
10211027
return request
10221028

1029+
def _validate_method(self, method):
1030+
"""Validate a method name for putrequest."""
1031+
# prevent http header injection
1032+
match = _contains_disallowed_method_pchar_re.search(method)
1033+
if match:
1034+
raise ValueError(
1035+
"method can't contain control characters. %r "
1036+
"(found at least %r)"
1037+
% (method, match.group()))
1038+
10231039
def _validate_path(self, url):
10241040
"""Validate a url for putrequest."""
10251041
# Prevent CVE-2019-9740.

Lib/test/test_httplib.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,28 @@ def test_invalid_headers(self):
385385
conn.putheader(name, value)
386386

387387

388+
class HttpMethodTests(TestCase):
389+
def test_invalid_method_names(self):
390+
methods = (
391+
'GET\r',
392+
'POST\n',
393+
'PUT\n\r',
394+
'POST\nValue',
395+
'POST\nHOST:abc',
396+
'GET\nrHost:abc\n',
397+
'POST\rRemainder:\r',
398+
'GET\rHOST:\n',
399+
'\nPUT'
400+
)
401+
402+
for method in methods:
403+
with self.assertRaisesRegexp(
404+
ValueError, "method can't contain control characters"):
405+
conn = httplib.HTTPConnection('example.com')
406+
conn.sock = FakeSocket(None)
407+
conn.request(method=method, url="/")
408+
409+
388410
class BasicTest(TestCase):
389411
def test_status_lines(self):
390412
# Test HTTP status lines
@@ -1010,6 +1032,7 @@ def create_connection(address, timeout=None, source_address=None):
10101032
@test_support.reap_threads
10111033
def test_main(verbose=None):
10121034
test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
1035+
HttpMethodTests,
10131036
HTTPTest, HTTPSTest, SourceAddressTest,
10141037
TunnelTests)
10151038

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Prevent http header injection by rejecting control characters in
2+
http.client.putrequest(...).

0 commit comments

Comments
 (0)