The vulnerability exists in how aiohttp handles HTTP headers containing non-ASCII characters. Specifically, the Upgrade, Content-Encoding, and Transfer-Encoding headers were processed using the .lower() string method before validation. The .lower() method can transform certain unicode characters into different ASCII representations (e.g., the Kelvin sign 'K' becomes 'k').
An attacker could craft a request with a header like Upgrade: websocKet. A front-end proxy might not perform the same unicode normalization and see a header it doesn't understand, passing it to the backend. The vulnerable aiohttp application, however, would convert websocKet to websocket and might upgrade the connection. This discrepancy in parsing between the proxy and the backend application is what enables an HTTP Request Smuggling attack.
The patch addresses this by adding an .isascii() check on the header values before calling .lower(). This ensures that any non-ASCII characters, which are not compliant with the relevant RFCs for these headers, cause the header to be rejected or ignored, preventing the parsing discrepancy.
The vulnerable functions are those responsible for parsing these specific headers in the pure Python implementation (aiohttp/http_parser.py), which is used when C extensions are not available. The same fix was applied to the Cython implementation (aiohttp/_http_parser.pyx) for consistency and to prevent the vulnerability in case it's used. The key vulnerable functions are _is_supported_upgrade, HttpParser.parse_headers, and HttpParser._is_chunked_te in the Python module, and HttpParser.on_headers_complete in the Cython module.