Files
Jiaying Song c5c647ba6a python3-aiohttp: fix CVE-2023-49081/CVE-2024-30251/CVE-2024-52304/CVE-2023-49082/CVE-2024-27306
CVE-2023-49081:
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.

References:
https://nvd.nist.gov/vuln/detail/CVE-2023-49081

Upstream patches:
1e86b777e6

CVE-2024-30251:
aiohttp is an asynchronous HTTP client/server framework for asyncio and Python.
In affected versions an attacker can send a specially crafted POST
(multipart/form-data) request. When the aiohttp server processes it, the server
will enter an infinite loop and be unable to process any further requests. An
attacker can stop the application from serving requests after sending a single
request. This issue has been addressed in version 3.9.4. Users are advised to
upgrade. Users unable to upgrade may manually apply a patch to their systems.
Please see the linked GHSA for instructions.

References:
https://nvd.nist.gov/vuln/detail/CVE-2024-30251

Upstream patches:
cebe526b9c
7eecdff163
f21c6f2ca5

CVE-2024-52304:
aiohttp is an asynchronous HTTP client/server framework for asyncio and Python.
Prior to version 3.10.11, the Python parser parses newlines in chunk extensions
incorrectly which can lead to request smuggling vulnerabilities under certain
conditions. If a pure Python version of aiohttp is installed (i.e. without the
usual C extensions) or `AIOHTTP_NO_EXTENSIONS` is enabled, then an attacker may
be able to execute a request smuggling attack to bypass certain firewalls or
proxy protections. Version 3.10.11 fixes the issue.

References:
https://nvd.nist.gov/vuln/detail/CVE-2024-52304

Upstream patches:
259edc3690

CVE-2023-49082:
aiohttp is an asynchronous HTTP client/server framework for asyncio and Python.
Improper validation makes it possible for an attacker to modify the HTTP
request (e.g. insert a new header) or even create a new HTTP request if the
attacker controls the HTTP method. The vulnerability occurs only if the
attacker can control the HTTP method (GET, POST etc.) of the request. If the
attacker can control the HTTP version of the request it will be able to modify
the request (request smuggling). This issue has been patched in version 3.9.0.

References:
https://nvd.nist.gov/vuln/detail/CVE-2023-49082

Upstream patches:
a43bc17798

CVE-2024-27306:
aiohttp is an asynchronous HTTP client/server framework for asyncio and Python.
A XSS vulnerability exists on index pages for static file handling. This
vulnerability is fixed in 3.9.4. We have always recommended using a reverse
proxy server (e.g. nginx) for serving static files. Users following the
recommendation are unaffected. Other users can disable `show_index` if unable
to upgrade.

References:
https://nvd.nist.gov/vuln/detail/CVE-2024-27306

Upstream patches:
28335525d1

Signed-off-by: Jiaying Song <jiaying.song.cn@windriver.com>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
2024-12-08 15:04:29 -05:00

106 lines
3.8 KiB
Diff

From a2200dc43d9fe0ee19b9185b30749c204a4dfd45 Mon Sep 17 00:00:00 2001
From: Sam Bull <git@sambull.org>
Date: Wed, 8 Nov 2023 19:25:05 +0000
Subject: [PATCH] Add HTTP method validation (#6533) (#7806)
(cherry picked from commit 75fca0b00b4297d0a30c51ae97a65428336eb2c1)
Upstream-Status: Backport
[https://github.com/aio-libs/aiohttp/pull/7806/commits/a43bc1779892e7014b7723c59d08fb37a000955e]
CVE: CVE-2023-49082
Co-authored-by: Andrew Svetlov <andrew.svetlov@gmail.com>
Signed-off-by: Jiaying Song <jiaying.song.cn@windriver.com>
---
CHANGES/6533.feature | 1 +
aiohttp/client_reqrep.py | 9 ++++++++-
tests/test_client_request.py | 5 +++++
tests/test_web_request.py | 9 +++++++--
4 files changed, 21 insertions(+), 3 deletions(-)
create mode 100644 CHANGES/6533.feature
diff --git a/CHANGES/6533.feature b/CHANGES/6533.feature
new file mode 100644
index 0000000..36bcbeb
--- /dev/null
+++ b/CHANGES/6533.feature
@@ -0,0 +1 @@
+Add HTTP method validation.
diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py
index d3cd77e..a8135b2 100644
--- a/aiohttp/client_reqrep.py
+++ b/aiohttp/client_reqrep.py
@@ -78,6 +78,7 @@ if TYPE_CHECKING: # pragma: no cover
from .tracing import Trace
+_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]")
json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json")
@@ -266,10 +267,16 @@ class ClientRequest:
proxy_headers: Optional[LooseHeaders] = None,
traces: Optional[List["Trace"]] = None,
):
-
if loop is None:
loop = asyncio.get_event_loop()
+ match = _CONTAINS_CONTROL_CHAR_RE.search(method)
+ if match:
+ raise ValueError(
+ f"Method cannot contain non-token characters {method!r} "
+ "(found at least {match.group()!r})"
+ )
+
assert isinstance(url, URL), url
assert isinstance(proxy, (URL, type(None))), proxy
# FIXME: session is None in tests only, need to fix tests
diff --git a/tests/test_client_request.py b/tests/test_client_request.py
index 009f1a0..d0f208b 100644
--- a/tests/test_client_request.py
+++ b/tests/test_client_request.py
@@ -89,6 +89,11 @@ def test_method3(make_request) -> None:
assert req.method == "HEAD"
+def test_method_invalid(make_request) -> None:
+ with pytest.raises(ValueError, match="Method cannot contain non-token characters"):
+ make_request("METHOD WITH\nWHITESPACES", "http://python.org/")
+
+
def test_version_1_0(make_request) -> None:
req = make_request("get", "http://python.org/", version="1.0")
assert req.version == (1, 0)
diff --git a/tests/test_web_request.py b/tests/test_web_request.py
index c6aeaf8..2bb0cd5 100644
--- a/tests/test_web_request.py
+++ b/tests/test_web_request.py
@@ -43,7 +43,10 @@ def test_base_ctor() -> None:
assert "GET" == req.method
assert HttpVersion(1, 1) == req.version
- assert req.host == socket.getfqdn()
+ # MacOS may return CamelCased host name, need .lower()
+ # FQDN can be wider than host, e.g.
+ # 'fv-az397-495' in 'fv-az397-495.internal.cloudapp.net'
+ assert req.host.lower() in socket.getfqdn().lower()
assert "/path/to?a=1&b=2" == req.path_qs
assert "/path/to" == req.path
assert "a=1&b=2" == req.query_string
@@ -66,7 +69,9 @@ def test_ctor() -> None:
assert "GET" == req.method
assert HttpVersion(1, 1) == req.version
# MacOS may return CamelCased host name, need .lower()
- assert req.host.lower() == socket.getfqdn().lower()
+ # FQDN can be wider than host, e.g.
+ # 'fv-az397-495' in 'fv-az397-495.internal.cloudapp.net'
+ assert req.host.lower() in socket.getfqdn().lower()
assert "/path/to?a=1&b=2" == req.path_qs
assert "/path/to" == req.path
assert "a=1&b=2" == req.query_string
--
2.25.1