CVE-2023-49081 (published 2023-11-30T07:15Z)

aiohttp is an asynchronous HTTP client/server framework for asyncio and Python. Improper validation made it possible for an attacker to modify the HTTP request (e.g. to insert a new header) or create a new HTTP request if the attacker controls the HTTP version. The vulnerability only occurs if the attacker can control the HTTP version of the request. This issue has been patched in version 3.9.0.

CVSS v3 base score: 5.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N)

Weakness information:
cwe_id: NVD-CWE-Other
cwe_name: Other
description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/aio-libs/aiohttp
commit_details: repo_url: https://github.com/aio-libs/aiohttp
author: Sam Bull
author_date: 2023-11-13 22:13:06+00:00
msg: Disallow arbitrary sequence types in version (#7835)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
repository: repo_url: https://github.com/aio-libs/aiohttp
repo_name: aio-libs/aiohttp
description: Asynchronous HTTP client/server framework for asyncio and Python
repo_language: Python
owner: aio-libs
file_changes: filename: 7835.bugfix
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+Fixed arbitrary sequence types being allowed to inject headers via version parameter -- by :user:`Dreamsorcerer`

programming_language: SQL
filename: client_reqrep.py
change_type: ModificationType.MODIFY
diff: @@ -644,8 +644,8 @@ class ClientRequest:
             self.headers[hdrs.CONNECTION] = connection
 
         # status + headers
-        status_line = "{0} {1} HTTP/{2[0]}.{2[1]}".format(
-            self.method, path, self.version
+        status_line = "{0} {1} HTTP/{v.major}.{v.minor}".format(
+            self.method, path, v=self.version
         )
         await writer.write_headers(status_line, self.headers)
 

programming_language: Python
filename: test_client_request.py
change_type: ModificationType.MODIFY
diff: @@ -20,6 +20,7 @@ from aiohttp.client_reqrep import (
     Fingerprint,
     _gen_default_accept_encoding,
 )
+from aiohttp.http import HttpVersion
 from aiohttp.test_utils import make_mocked_coro
 
 
@@ -590,18 +591,18 @@ async def test_connection_header(loop: Any, conn: Any) -> None:
     req.headers.clear()
 
     req.keep_alive.return_value = True
-    req.version = (1, 1)
+    req.version = HttpVersion(1, 1)
     req.headers.clear()
     await req.send(conn)
     assert req.headers.get("CONNECTION") is None
 
-    req.version = (1, 0)
+    req.version = HttpVersion(1, 0)
     req.headers.clear()
     await req.send(conn)
     assert req.headers.get("CONNECTION") == "keep-alive"
 
     req.keep_alive.return_value = False
-    req.version = (1, 1)
+    req.version = HttpVersion(1, 1)
     req.headers.clear()
     await req.send(conn)
     assert req.headers.get("CONNECTION") == "close"
@@ -1112,6 +1113,19 @@ async def test_close(loop: Any, buf: Any, conn: Any) -> None:
     resp.close()
 
 
+async def test_bad_version(loop: Any, conn: Any) -> None:
+    req = ClientRequest(
+        "GET",
+        URL("http://python.org"),
+        loop=loop,
+        headers={"Connection": "Close"},
+        version=("1", "1\r\nInjected-Header: not allowed"),
+    )
+
+    with pytest.raises(AttributeError):
+        await req.send(conn)
+
+
 async def test_custom_response_class(loop: Any, conn: Any) -> None:
     class CustomResponse(ClientResponse):
         def read(self, decode=False):

programming_language: Python