Skip to content

Commit

Permalink
00354-cve-2020-26116-http-request-method-crlf-injection-in-httplib.patch
Browse files Browse the repository at this point in the history
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 <31338382+amiremohamadi@users.noreply.github.com>
  • Loading branch information
amiremohamadi authored and encukou committed Sep 30, 2020
1 parent fcc4a58 commit 2da5f0e
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 0 deletions.
16 changes: 16 additions & 0 deletions Lib/httplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@
# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$")
# We are more lenient for assumed real world compatibility purposes.

# These characters are not allowed within HTTP method names
# to prevent http header injection.
_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]')

# We always set the Content-Length header for these methods because some
# servers will otherwise respond with a 411
_METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
Expand Down Expand Up @@ -935,6 +939,8 @@ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
else:
raise CannotSendRequest()

self._validate_method(method)

# Save the method for use later in the response phase
self._method = method

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

def _validate_method(self, method):
"""Validate a method name for putrequest."""
# prevent http header injection
match = _contains_disallowed_method_pchar_re.search(method)
if match:
raise ValueError(
"method can't contain control characters. %r "
"(found at least %r)"
% (method, match.group()))

def _validate_path(self, url):
"""Validate a url for putrequest."""
# Prevent CVE-2019-9740.
Expand Down
23 changes: 23 additions & 0 deletions Lib/test/test_httplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,28 @@ def test_invalid_headers(self):
conn.putheader(name, value)


class HttpMethodTests(TestCase):
def test_invalid_method_names(self):
methods = (
'GET\r',
'POST\n',
'PUT\n\r',
'POST\nValue',
'POST\nHOST:abc',
'GET\nrHost:abc\n',
'POST\rRemainder:\r',
'GET\rHOST:\n',
'\nPUT'
)

for method in methods:
with self.assertRaisesRegexp(
ValueError, "method can't contain control characters"):
conn = httplib.HTTPConnection('example.com')
conn.sock = FakeSocket(None)
conn.request(method=method, url="/")


class BasicTest(TestCase):
def test_status_lines(self):
# Test HTTP status lines
Expand Down Expand Up @@ -1010,6 +1032,7 @@ def create_connection(address, timeout=None, source_address=None):
@test_support.reap_threads
def test_main(verbose=None):
test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
HttpMethodTests,
HTTPTest, HTTPSTest, SourceAddressTest,
TunnelTests)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Prevent http header injection by rejecting control characters in
http.client.putrequest(...).

0 comments on commit 2da5f0e

Please sign in to comment.