Like a bad label maker on an internal mail chute, this only matters if you already let strangers write the label
CVE-2026-1502 is a CR/LF injection flaw in CPython's http.client CONNECT-tunneling path. Before the April 2026 fixes, HTTPConnection.set_tunnel() could accept tunnel host or tunnel header values containing carriage-return/line-feed characters, letting malformed data alter the CONNECT request sent to an upstream HTTP proxy. Upstream references show fixes landing in current branches including Python 3.13.13 and 3.14.4, with security backports also queued/applied for 3.10/3.11/3.12 branches; the unfixed development line was effectively pre-3.15.0.
The vendor's MEDIUM 5.7 is technically defensible in a lab, but it is too hot for enterprise patch triage. Real exploitation requires an application to use Python's proxy tunneling feature, pass attacker-influenced values into set_tunnel(), and route through a proxy where modified CONNECT headers actually change behavior. That's a narrow, post-design-mistake chain with tiny exposed population, no KEV listing, no public in-the-wild evidence, and a near-zero EPSS.
4 steps from start to impact.
Gain control of tunnel input
http.client.HTTPConnection.set_tunnel() as the tunnel host or custom tunnel headers. In practice this usually means a higher-level app already accepts untrusted proxy configuration, destination metadata, or header content and forwards it into Python's stdlib client. The "weapon" here is just the application's own use of http.client.- Application uses CPython's
http.clientdirectly or through a wrapper - Application calls
set_tunnel()for CONNECT proxying - Attacker can control or significantly influence tunnel host or tunnel header values
- Most enterprise Python apps never call
set_tunnel()directly - Many apps hardcode proxy settings rather than taking them from user input
- This prerequisite already implies a design bug or unsafe trust boundary in the calling application
set_tunnel() path is reachable with attacker-controlled input.Trigger malformed CONNECT request
python plus a local intercepting proxy, or by adapting the upstream issue/patch test cases from GitHub. The effective technique is header injection / request splitting into the proxy-facing CONNECT transaction.- Affected CPython build is running
- Attacker-supplied value reaches
set_tunnel()unsanitized - Application attempts a proxied outbound connection
- Many frameworks do not expose this exact stdlib path
- Some wrappers or app-side validation may strip control characters first
- Not every malformed CONNECT line meaningfully changes proxy behavior
set_tunnel( usage is more useful than perimeter scanning. Network IDS visibility is poor unless you inspect outbound proxy traffic.Abuse proxy-side interpretation
- Outbound traffic traverses an HTTP proxy that processes CONNECT headers
- That proxy interprets the injected CR/LF in a useful way
- The application or environment relies on proxy-side policy decisions
- Modern proxies often normalize or reject malformed CONNECT traffic
- Many enterprise egress stacks log or block oddball proxy requests
- Impact varies heavily by proxy implementation and local policy
Land limited integrity impact
- Prior steps succeeded
- The target environment depends on the proxied transaction for a sensitive workflow
- Impact is bounded to a narrow feature path
- No confidentiality or availability impact is claimed by the CNA vector
- No public exploitation campaigns or KEV inclusion found
The supporting signals.
| In-the-wild status | No public in-the-wild exploitation found in vendor advisory, NVD, or CISA KEV checks. Inference: this currently looks like a low-traction bug, not an active campaign item. |
|---|---|
| Public exploit / PoC | No standalone weaponized PoC repo surfaced in public search. However, the upstream issue, PR, and tests make reproduction straightforward for anyone comfortable with Python and a proxy. |
| EPSS | 0.00024 from the user-supplied intel, which is effectively noise-floor risk. That lines up with the weak real-world exposure story. |
| KEV status | Not listed in the CISA KEV catalog when checked. |
| CVSS / vendor baseline | Python CNA scored it 5.7 MEDIUM with CVSS:4.0/AV:N/AC:L/AT:P/PR:H/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N. The important part is the built-in friction: high privileges + user interaction + integrity-only impact. |
| Weakness class | NVD now maps this to CWE-93, CRLF injection. |
| Affected versions | Affected code sits in CPython's stdlib http.client tunnel handling. Public records indicate exposure on the active line before 3.15.0 and released branches before the April 2026 fixes, including pre-3.13.13 and pre-3.14.4. |
| Fixed versions / backports | Confirmed public fixed releases include Python 3.13.13 and 3.14.4 dated 2026-04-07. Upstream PR metadata also shows backport work for 3.12, 3.11, and 3.10 branches; FreeBSD and distro trackers subsequently carried the issue. |
| Exposure / scanning reality | There is no clean internet-wide fingerprint for "apps that call set_tunnel() with attacker-controlled data." Shodan/Censys/FOFA-style counts are mostly useless here because this is a library usage bug, not a directly bannered service vulnerability. |
| Disclosure / credits | Published 2026-04-10 via Python security announce and MITRE CNA record. Public credit in aggregators and references points to senseicat and Seth Larson. |
noisgate verdict.
The decisive downgrade factor is reachability: exploitation requires a very specific application behavior chain around HTTPConnection.set_tunnel() and attacker-controlled proxy-tunnel metadata. This is not an unauthenticated internet-edge bug in Python itself; it is a narrow misuse-sensitive feature flaw with limited blast radius and no active exploitation signal.
Why this verdict
- Attacker position is already constrained: this is not unauthenticated remote-to-RCE; the CNA vector itself says
PR:HandUI:P, and real abuse requires control over values fed intoset_tunnel(). - Exposure population collapses fast: only apps using Python's CONNECT proxy tunneling are in scope, and only a subset of those let untrusted input reach tunnel host or headers.
- Modern controls should catch adjacent mistakes: secure coding review, SAST, outbound proxy validation, and egress monitoring all reduce exploitability long before this becomes an estate-wide incident.
- Blast radius is narrow: impact is integrity manipulation of a proxied request path, not host takeover, credential dumping, or remote wormability.
- Threat intel is cold: no KEV, no public campaign reporting, no mass-scanning signal, and an EPSS of
0.00024all push this down.
Why not higher?
To justify MEDIUM or HIGH in a 10,000-host environment, you would want broader reachability, simpler attacker position, or active exploitation evidence. This flaw instead stacks multiple prerequisites: specific stdlib feature use, attacker-controlled input, CONNECT proxying, and favorable proxy-side parsing. That is too much compounding friction for a higher queue position.
Why not lower?
It is not pure non-issue territory because the bug is real, fixed upstream, and can alter request integrity in environments that depend on CONNECT proxies. If you run Python services that broker outbound traffic or let tenants influence proxy destinations or headers, there is a legitimate though narrow abuse case. That keeps it above IGNORE.
What to do — in priority order.
- Audit
set_tunnel()usage — Search application code and dependencies forHTTPConnection.set_tunnel(and wrappers aroundhttp.client; this identifies the tiny subset of hosts where the CVE is even reachable. For a LOW verdict there is no fixed mitigation deadline, so do this as backlog hygiene during normal engineering cycles. - Block control characters at app boundaries — Reject
\rand\nin proxy hostnames, CONNECT metadata, and any custom header values before they reach stdlib networking calls. This is the right temporary guard if you cannot patch immediately; for LOW, deploy during normal code-hardening work rather than emergency change windows. - Harden outbound proxy policy — Configure egress proxies to reject malformed CONNECT requests and unexpected custom CONNECT headers where possible. That cuts off the downstream interpreter-dependent impact and is useful regardless of runtime version.
- Log CONNECT anomalies — Turn on or retain outbound proxy logging for malformed CONNECT attempts, repeated 4xx responses, or unexpected header patterns from Python application tiers. This gives you the only realistic detection path if someone probes the edge cases.
- A WAF does little here because the dangerous traffic is usually server-side outbound CONNECT traffic, not inbound web requests at your perimeter.
- Host EDR will not reliably flag this; the exploit primitive is malformed proxy request construction inside a legitimate Python process, not malware execution.
- Perimeter asset scanners cannot tell you whether an app actually passes attacker-controlled values into
set_tunnel(), so version-only scanning overstates practical risk.
Crowdsourced verification payload.
Run this on the target host using the same Python interpreter your application uses, or from your software inventory pipeline against each discovered python binary. Invoke it as python3 check_cve_2026_1502.py or C:\Python313\python.exe check_cve_2026_1502.py; no admin rights are required, but you must run the specific interpreter instance you want to assess.
#!/usr/bin/env python3
# CVE-2026-1502 verifier for CPython http.client CONNECT tunnel CR/LF validation
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
import inspect
import re
import sys
STATUS_PATCHED = 0
STATUS_VULNERABLE = 1
STATUS_UNKNOWN = 2
def out(msg):
sys.stdout.write(msg + "\n")
def main():
try:
import http.client as hc
except Exception as e:
out(f"UNKNOWN: failed to import http.client: {e}")
return STATUS_UNKNOWN
ver = sys.version.split()[0]
try:
src = inspect.getsource(hc.HTTPConnection._tunnel)
except Exception as e:
out(f"UNKNOWN: could not inspect http.client.HTTPConnection._tunnel on Python {ver}: {e}")
return STATUS_UNKNOWN
# Heuristic 1: patched code added explicit tunnel-host validation and header name/value validation.
checks = [
"_contains_disallowed_url_pchar_re.search(self._tunnel_host)",
"_is_legal_header_name",
"_is_illegal_header_value",
"Invalid header name",
"Invalid header value",
"Tunnel host can't contain control characters",
]
found = [c for c in checks if c in src]
# Heuristic 2: behavior test. Vulnerable versions typically accept CR/LF into tunnel headers until send time.
behavior_result = None
try:
conn = hc.HTTPConnection("example.com")
conn.set_tunnel("example.org", headers={"X-Test": "ok\r\nInjected: yes"})
# If we got here without ValueError, that's a strong vulnerable signal.
behavior_result = "accepted_bad_header"
except ValueError:
behavior_result = "rejected_bad_header"
except TypeError:
behavior_result = "rejected_bad_header"
except Exception:
# Unexpected exception; source-based result will decide.
behavior_result = "indeterminate"
if len(found) >= 4 and behavior_result == "rejected_bad_header":
out(f"PATCHED: Python {ver} contains tunnel host/header validation consistent with CVE-2026-1502 fix")
return STATUS_PATCHED
if len(found) <= 1 and behavior_result == "accepted_bad_header":
out(f"VULNERABLE: Python {ver} accepted CR/LF in set_tunnel() header data and lacks fix markers")
return STATUS_VULNERABLE
# Mixed case: report what we saw so operators can decide.
out(
"UNKNOWN: inconclusive result for Python {} (fix markers found: {}; behavior: {})".format(
ver, len(found), behavior_result
)
)
return STATUS_UNKNOWN
if __name__ == "__main__":
sys.exit(main())
If you remember one thing.
set_tunnel( in services that broker outbound traffic; if you find real reachability, add CR/LF input rejection and patch those runtimes in the next normal maintenance cycle. For a LOW noisgate rating there is no noisgate mitigation SLA and no noisgate remediation SLA beyond backlog hygiene—treat it as routine runtime cleanup, not a stop-the-line event.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.