CVE-2021-23336 Overview
CVE-2021-23336 is a Web Cache Poisoning vulnerability affecting Python's urllib.parse.parse_qsl and urllib.parse.parse_qs functions. The vulnerability enables attackers to exploit a parameter cloaking technique by using semicolons (;) as query parameter separators, creating a discrepancy between how proxy servers and backend Python applications interpret HTTP requests. This inconsistency allows malicious requests to be cached as legitimate ones, potentially serving poisoned content to unsuspecting users.
Critical Impact
Attackers can exploit this vulnerability to poison web caches, causing cached malicious responses to be served to users. This can lead to widespread impact including denial of service, data integrity issues, and potential exploitation of downstream users relying on cached content.
Affected Products
- Python CPython versions before 3.6.13, 3.7.0-3.7.9, 3.8.0-3.8.7, and 3.9.0-3.9.1
- Django (multiple versions affected)
- Fedora 32, 33, and 34
- Debian Linux 9.0
- NetApp Cloud Backup, Inventory Collect Tool, ONTAP Select Deploy Administration Utility, and SnapCenter
- Oracle Communications Offline Mediation Controller, Communications Pricing Design Center, Enterprise Manager Ops Center, and ZFS Storage Appliance
Discovery Timeline
- February 15, 2021 - CVE-2021-23336 published to NVD
- December 17, 2025 - Last updated in NVD database
Technical Details for CVE-2021-23336
Vulnerability Analysis
This vulnerability exploits the inconsistent handling of query string parameter separators between web proxies and Python applications. The core issue lies in how Python's urllib.parse module historically accepted both ampersands (&) and semicolons (;) as valid parameter separators in query strings, while most proxy servers and caching systems only recognize ampersands as separators.
When a malicious request contains parameters separated by semicolons, the proxy server treats the entire segment (including the semicolon and subsequent parameters) as a single parameter value. However, the Python backend correctly parses the semicolon-separated parameters individually. This parsing discrepancy enables the attacker to "cloak" malicious parameters that influence the server's response without affecting the cache key generated by the proxy.
The attack requires network access and some user interaction, making successful exploitation dependent on specific environmental conditions. However, once cache poisoning is achieved, the impact can be significant as poisoned responses are served to all users requesting the same cached resource.
Root Cause
The root cause is the dual acceptance of semicolons and ampersands as parameter separators in urllib.parse.parse_qsl() and urllib.parse.parse_qs() functions. This behavior, while technically valid according to older HTTP specifications, creates an inconsistency with modern web infrastructure where semicolons are not typically recognized as separators by proxies and caches.
Attack Vector
The attack vector is network-based, requiring the attacker to craft malicious HTTP requests that exploit the parsing discrepancy. The attacker constructs a URL where semicolons separate hidden parameters that influence the server's response. When this request passes through a caching proxy, the proxy generates a cache key that doesn't account for the semicolon-separated parameters, while the Python application processes them normally.
For example, a request like /?param=value;injected=malicious would be cached by the proxy under a key that treats value;injected=malicious as the entire value of param. However, Python's parse_qs() function would parse this as two separate parameters: param=value and injected=malicious. If the injected parameter influences the response content, the attacker can poison the cache with a malicious response that gets served to other users.
Detection Methods for CVE-2021-23336
Indicators of Compromise
- Unusual query strings containing semicolons in web server access logs
- Cache hit responses that don't match expected content for the requested URL
- Anomalous parameter patterns in HTTP requests targeting Python-based web applications
- Discrepancies between proxy logs and application logs for the same request
Detection Strategies
- Monitor web server and proxy logs for query strings containing semicolons that may indicate parameter cloaking attempts
- Implement application-layer logging to compare parsed query parameters with raw query strings
- Deploy web application firewalls (WAF) with rules to detect and alert on semicolon usage in query parameters
- Review cache behavior by comparing cached content hashes with expected responses
Monitoring Recommendations
- Enable verbose logging on caching proxies to capture full query strings including potential semicolon separators
- Implement integrity checking for cached responses to detect unexpected content modifications
- Set up alerts for increased rates of requests containing semicolons in query parameters
- Monitor for anomalous cache hit ratios that may indicate poisoning activity
How to Mitigate CVE-2021-23336
Immediate Actions Required
- Upgrade Python to patched versions: 3.6.13+, 3.7.10+, 3.8.8+, or 3.9.2+
- Review and update Django installations to patched versions that address this vulnerability
- Audit applications using urllib.parse.parse_qs() and urllib.parse.parse_qsl() for potential exposure
- Configure web application firewalls to reject or sanitize requests containing semicolons in query parameters
Patch Information
The vulnerability has been addressed through the GitHub CPython Pull Request, which changes the default behavior of the affected functions to no longer accept semicolons as parameter separators. Security advisories have been issued by multiple vendors including Oracle Security Advisory, NetApp Security Advisory, and Gentoo GLSA 202104-04. Debian and Fedora have also released updated packages addressing this vulnerability.
Workarounds
- Configure the separator parameter explicitly in parse_qs() and parse_qsl() calls to use only ampersand (&) as the separator
- Implement request preprocessing to normalize query strings by removing or encoding semicolons before they reach Python parsing functions
- Deploy proxy configurations that explicitly reject requests containing semicolons in query parameters
- Use application-level input validation to sanitize query strings before processing
# Python configuration to explicitly set separator
# In your application code, replace:
# urllib.parse.parse_qs(query_string)
# With:
# urllib.parse.parse_qs(query_string, separator='&')
# For Django applications, upgrade to patched version:
pip install --upgrade Django>=2.2.19
pip install --upgrade Django>=3.0.13
pip install --upgrade Django>=3.1.7
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


