CVE-2020-26137 is a CRLF (carriage-return/line-feed) injection vulnerability in urllib3, the HTTP client library that underpins requests, pip, botocore/boto3, and a huge share of the Python ecosystem's networking stack. The flaw lives in how urllib3 builds outbound HTTP requests: versions before 1.25.9 did not strip or validate carriage-return and newline characters when the HTTP request method (and, depending on call path, header values) were passed through to the underlying socket write. An application that allowed attacker-influenced data to reach that method or header parameter could have \r\n sequences smuggled into the raw request line — enough to inject additional headers, split a single request into two, or otherwise manipulate what the receiving server, proxy, or cache believed it was processing. For a query like "urllib3 crlf injection headers," this is the CVE that answer refers to, and it's a good case study in how a single, quietly-patched library bug can ripple across thousands of downstream projects.
Affected Versions and Components
- Component:
urllib3(Python HTTP client library) - Affected versions: urllib3 releases prior to 1.25.9
- Fixed version: 1.25.9 and all subsequent releases (the fix has carried forward through the 1.26.x line and the 2.x major rewrite)
- Root cause: insufficient validation/sanitization of the HTTP method parameter (and related header-construction paths) before it was written onto the wire, allowing embedded CR/LF characters to alter the structure of the HTTP request
The practical exposure isn't "does my app use urllib3" — nearly every Python service does, directly or transitively — it's "does any code path let external input flow into the HTTP method or a header value passed to urllib3's request functions." That's a narrower but not rare pattern: proxy-style tools, API gateways, testing harnesses, and internal HTTP clients that dynamically construct requests from user-supplied configuration or parameters are the realistic targets. Because urllib3 is vendored or pinned inside requests, pip, and numerous SDKs, the practical blast radius of "which packages needed a bump" was large even though the number of applications with an actually reachable, exploitable code path was smaller.
CVSS, EPSS, and KEV Context
NVD rates CVE-2020-26137 as Medium severity under CVSS v3.1, reflecting a network-exploitable, low-complexity issue that requires no privileges or user interaction but has a limited confidentiality and integrity impact and no direct availability impact — consistent with a header/request-smuggling primitive rather than a remote code execution bug. The real-world severity of any individual instance depends heavily on what sits downstream (a caching proxy or a security control parsing the smuggled data can turn a "medium" library bug into a serious application-level issue via HTTP request smuggling or cache poisoning).
This CVE has not been added to CISA's Known Exploited Vulnerabilities (KEV) catalog, and it does not carry a high EPSS (Exploit Prediction Scoring System) probability — there's no evidence of widespread active exploitation in the wild. That said, "low EPSS" is a population-level signal, not a guarantee for any specific deployment. Because the vulnerable code path requires an unusual application pattern (untrusted input reaching the HTTP method/header construction), opportunistic mass scanning for it is less common than for, say, a directly internet-facing RCE — but targeted abuse against a specific vulnerable integration remains plausible.
Timeline
- Discovery/report: The issue was identified in urllib3's request-construction logic and reported through responsible disclosure channels to the maintainers.
- Fix released: The urllib3 maintainers shipped a patch in version 1.25.9, sanitizing the method and header inputs to reject embedded CR/LF sequences. A GitHub Security Advisory (GHSA-5phf-pp7p-vc2r) documents the fix and the vulnerable code path.
- CVE publication: CVE-2020-26137 was subsequently published in the NVD, formalizing the tracking identifier for the already-patched issue.
- Downstream propagation: Following the fix, the broader Python packaging ecosystem — including
pip(which vendors urllib3),requests, and numerous SDKs pinning urllib3 — needed to bump their bundled/declared minimum version over the following release cycles, which is the typical long tail for a widely-vendored dependency fix.
The lag between "patch available" and "every downstream consumer has picked it up" is the part that matters most for defenders: a fix landing upstream in mid-2020 does not mean every application built on urllib3 was actually running a patched version months, or even years, later — especially in environments with infrequent dependency refresh cycles or vendored/frozen copies of the library.
Remediation Steps
- Upgrade urllib3. Move to urllib3 1.25.9 or later — in practice, standardize on a currently maintained line (1.26.x or 2.x) rather than the minimum patched version, since those releases include additional hardening and are still receiving security updates.
- Check transitive pins. Because urllib3 is frequently pulled in indirectly through
requests,botocore,boto3,pip, and other packages, run a full dependency tree resolution (pip list --outdated,pipdeptree, or your package manager's equivalent) rather than only checking yourrequirements.txt/pyproject.tomltop-level entries. - Audit for vendored copies. Some tools and CI images vendor or freeze a specific urllib3 version inside a virtual environment, container base image, or bundled binary (pip itself historically vendored urllib3). Confirm those embedded copies are also current.
- Review dynamic request construction. Independent of the library fix, treat this as a reminder to never build HTTP methods or header values directly from unsanitized user input. Apply allow-lists for HTTP methods and strict validation/escaping for any header value derived from external data, regardless of what the underlying HTTP client does.
- Re-scan after remediation. Confirm the fix took effect by checking the installed version in the actual runtime environment (container image, Lambda layer, virtualenv) — not just the manifest file — since manifests and lockfiles can drift from what's actually deployed.
- Watch for future advisories on the same subsystem. Request-construction and header-handling code is a recurring source of HTTP smuggling-class bugs across many languages' HTTP libraries; keep dependency monitoring active rather than treating this as a one-time fix.
How Safeguard Helps
CVE-2020-26137 is a textbook example of why software supply chain visibility has to go deeper than a project's direct dependency list. urllib3 rarely shows up as a first-class, hand-picked dependency — it arrives quietly through requests, cloud SDKs, packaging tools, and dozens of other libraries, which means the question "are we affected?" can't be answered by reading a requirements.txt file.
Safeguard is built for exactly this problem. Our platform continuously generates and maintains accurate SBOMs across your codebases, containers, and build artifacts, resolving the full transitive dependency graph — not just declared top-level packages — so a vulnerable urllib3 version buried three layers deep inside a vendored SDK gets surfaced instead of missed. When a CVE like this one is disclosed, Safeguard correlates it against every artifact in your inventory in real time, telling you precisely which services, images, and repositories are exposed and which are already running a patched version.
Because remediation often stalls on "which fix actually matters here," Safeguard prioritizes findings using real exploitability context — CVSS, EPSS trends, and KEV status — so teams aren't treating every medium-severity library bug as a fire drill, while still catching the cases where a low-profile CRLF injection sits behind a code path that genuinely matters for your architecture. Policy gates in CI/CD let you block builds that would introduce a known-vulnerable urllib3 version going forward, and continuous runtime monitoring closes the gap between "the manifest says we're patched" and "the running container actually is." For dependencies as pervasive as urllib3, that combination of deep transitive visibility, exploitability-aware prioritization, and enforcement at the point of build is what turns a sprawling, hard-to-trace advisory into a short, well-scoped remediation task.