Skip to content
Permalink
Browse files Browse the repository at this point in the history
IMPORTANT security vulnerability CWE-93 CRLF injection
Force %xx quote of space, CR, LF characters in uri.

Special thanks to Recar https://github.com/Ciyfly for discrete notification.

https://cwe.mitre.org/data/definitions/93.html
  • Loading branch information
temoto committed May 20, 2020
1 parent 9413ffc commit a1457cc
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 1 deletion.
3 changes: 3 additions & 0 deletions python2/httplib2/__init__.py
Expand Up @@ -1985,6 +1985,9 @@ def request(
headers["user-agent"] = "Python-httplib2/%s (gzip)" % __version__

uri = iri2uri(uri)
# Prevent CWE-75 space injection to manipulate request via part of uri.
# Prevent CWE-93 CRLF injection to modify headers via part of uri.
uri = uri.replace(" ", "%20").replace("\r", "%0D").replace("\n", "%0A")

(scheme, authority, request_uri, defrag_uri) = urlnorm(uri)

Expand Down
3 changes: 3 additions & 0 deletions python3/httplib2/__init__.py
Expand Up @@ -1790,6 +1790,9 @@ def request(
headers["user-agent"] = "Python-httplib2/%s (gzip)" % __version__

uri = iri2uri(uri)
# Prevent CWE-75 space injection to manipulate request via part of uri.
# Prevent CWE-93 CRLF injection to modify headers via part of uri.
uri = uri.replace(" ", "%20").replace("\r", "%0D").replace("\n", "%0A")

(scheme, authority, request_uri, defrag_uri) = urlnorm(uri)

Expand Down
2 changes: 1 addition & 1 deletion tests/__init__.py
Expand Up @@ -75,7 +75,7 @@ def _fill(self, target=1, more=None, untilend=False):
chunk = b""
else:
chunk = self._sock.recv(8 << 10)
# print('!!! recv', chunk)
# print("!!! recv", chunk)
if not chunk:
self._end = True
if untilend:
Expand Down
30 changes: 30 additions & 0 deletions tests/test_http.py
Expand Up @@ -703,3 +703,33 @@ def test_custom_redirect_codes():
response, content = http.request(uri, "GET")
assert response.status == 301
assert response.previous is None


def test_cwe93_inject_crlf():
# https://cwe.mitre.org/data/definitions/93.html
# GET /?q= HTTP/1.1 <- injected "HTTP/1.1" from attacker
# injected: attack
# ignore-http: HTTP/1.1 <- nominal "HTTP/1.1" from library
# Host: localhost:57285
http = httplib2.Http()
with tests.server_reflect() as uri:
danger_url = urllib.parse.urljoin(
uri, "?q= HTTP/1.1\r\ninjected: attack\r\nignore-http:"
)
response, content = http.request(danger_url, "GET")
assert response.status == 200
req = tests.HttpRequest.from_bytes(content)
assert req.headers.get("injected") is None


def test_inject_space():
# Injecting space into request line is precursor to CWE-93 and possibly other injections
http = httplib2.Http()
with tests.server_reflect() as uri:
# "\r\nignore-http:" suffix is nuance for current server implementation
# please only pay attention to space after "?q="
danger_url = urllib.parse.urljoin(uri, "?q= HTTP/1.1\r\nignore-http:")
response, content = http.request(danger_url, "GET")
assert response.status == 200
req = tests.HttpRequest.from_bytes(content)
assert req.uri == "/?q=%20HTTP/1.1%0D%0Aignore-http:"

0 comments on commit a1457cc

Please sign in to comment.