Introduction
HTTP/2, the successor to HTTP/1.1, brings multiplexed streams, binary framing, and header compression. While these features improve performance, they also create new attack surfaces. A downgrade attack forces a client or server to fall back to an older, less-secure protocol version (typically HTTP/1.1) or to an insecure transport (clear-text h2c). The attacker then leverages legacy vulnerabilities that are no longer present in a proper HTTP/2 implementation.
Understanding downgrade vectors is crucial for red-teamers who need to bypass modern defenses, and for blue-team defenders who must ensure that their services cannot be forced into unsafe states. Real-world incidents-such as the 2022 H2C-Stripping campaign that compromised enterprise APIs-demonstrate the impact.
Prerequisites
- Familiarity with the HTTP/2 protocol overview and frame structure (e.g., DATA, HEADERS, SETTINGS, PRIORITY).
- Knowledge of TLS version negotiation, cipher suite selection, and classic downgrade mitigation (e.g., TLS_FALLBACK_SCSV).
- Basic command-line proficiency with
curl,nghttp, and packet-capture tools (Wireshark, tcpdump).
Core Concepts
Downgrade attacks exploit the fact that HTTP/2 can be negotiated in two distinct ways:
- ALPN (Application-Layer Protocol Negotiation) during the TLS handshake. The client advertises supported protocols (e.g.,
h2,http/1.1) and the server selects one. - Upgrade header for clear-text connections (RFC 7540 §3.2). A client can start with HTTP/1.1 and request an upgrade to
h2cvia theUpgrade: h2cheader.
If an attacker can tamper with these negotiation steps, they can force the connection to stay in HTTP/1.1 or in clear-text, opening the door to legacy attacks such as request smuggling, header injection, or even plain-text credential exposure.
Key mechanisms that mitigate downgrade in TLS-like TLS_FALLBACK_SCSV and the Supported Versions extension-have analogues in HTTP/2, but many deployments forget to enforce them, especially when mixed HTTP/1.1/HTTP/2 services coexist behind load balancers or CDNs.
ALPN and HTTP/2 upgrade handshake
During a TLS handshake, the client includes an ALPN extension listing its supported protocols, for example:
Client Hello: Extension: application_layer_protocol_negotiation (len=12) ALPN Protocol: h2 ALPN Protocol: http/1.1
The server replies with a ALPN selection. If the server chooses http/1.1 while the client also supports h2, a downgrade has occurred. An attacker positioned as a man-in-the-middle (MITM) can strip the h2 entry from the client’s list or replace the server’s selection.
Typical mitigation steps include:
- Enforcing that the server never advertises
http/1.1when it also supportsh2(strict ALPN). - Validating the
ALPNextension on the client side and aborting the connection if the selected protocol is not the highest-priority one. - Deploying HSTS and HPKP (where still supported) to prevent MITM injection of downgrade payloads.
Clear-text h2c (HTTP/2 over TCP) downgrade paths
HTTP/2 can be used without TLS via the h2c scheme. The RFC defines two upgrade mechanisms:
- HTTP/1.1 Upgrade - The client sends an
Upgrade: h2cheader together withHTTP2-Settings(base64url-encoded SETTINGS frame). - Prior Knowledge - The client directly initiates an HTTP/2 connection on a clear-text port (usually 80) and omits the HTTP/1.1 pre-amble.
Many web servers (e.g., Apache, nginx) default to HTTP/1.1 on port 80 and only enable h2c via explicit configuration. Attackers can force a downgrade by stripping the Upgrade header or by interfering with the HTTP2-Settings value, causing the server to fall back to HTTP/1.1.
Because HTTP/1.1 does not provide binary framing, classic parsing bugs become available again. This is especially dangerous for APIs that only validate inputs against the HTTP/2 grammar.
TLS downgrade manipulation (e.g., SSL-strip style attacks)
SSL-strip attacks work by converting HTTPS links to HTTP, then forwarding the traffic unencrypted to the victim. A similar approach can be applied to HTTP/2:
- The attacker intercepts the TLS handshake, removes the
h2entry from theALPNextension, and forwards a modifiedServer Hellothat selectshttp/1.1. - When the client follows redirects (e.g., from
https://example.comtohttp://example.com) the attacker downgrades each subsequent request. - Because HTTP/2 uses a single TCP connection for many streams, forcing a downgrade early can affect all later streams, amplifying the attack surface.
Mitigation techniques include:
- Enforcing
Strict-Transport-Security (HSTS)with thepreloadflag. - Deploying
TLS_FALLBACK_SCSVand ensuring the server rejects any handshake that omits the highest-supported protocol. - Using a reverse-proxy that validates the
ALPNnegotiation and aborts if a downgrade is detected.
Server misconfiguration that forces HTTP/1.1 fallback
Even without an active attacker, misconfigurations can unintentionally downgrade clients:
- Mixed virtual hosts: One vhost enables
h2while another only supportshttp/1.1. If the SNI value is missing or incorrect, the server may default to the older vhost. - Load balancer termination: A load balancer terminates TLS, negotiates HTTP/2 with the client, but forwards the request to the backend over HTTP/1.1. If the backend does not enforce protocol validation, an attacker can manipulate the upstream connection.
- Incorrect
Upgradeheader handling: Some frameworks automatically strip unknown headers, inadvertently removingUpgrade: h2cand causing a fallback.
Administrators should audit configuration files (e.g., nginx.conf, httpd.conf), verify that Protocols directives list h2 before http/1.1, and test with tools like nghttp -v to confirm the negotiated protocol.
Crafting downgrade payloads with curl, nghttp, and Burp Suite
Below are practical ways to force a downgrade for testing purposes.
Using curl
# Force HTTP/1.1 by removing ALPN entries (curl 7.68+ supports --no-alpn)
curl --http1.1 https://example.com -v
# Attempt HTTP/2 but strip ALPN via proxy injection (mitmproxy example)
mitmproxy --mode transparent --set http2=False
The first command tells curl to ignore HTTP/2 entirely. The second demonstrates how a transparent proxy can be configured to drop the h2 token.
Using nghttp (nghttp2 client)
# Normal HTTP/2 request (ALPN negotiation)
nghttp -v https://example.com
# Explicitly disable ALPN to force HTTP/1.1
nghttp -n -v https://example.com
# Clear-text h2c upgrade attempt (requires server support)
nghttp -u https://example.com -H "Upgrade: h2c" -H "HTTP2-Settings: AAEAAEAAAAIAAAABAAEAAEAA" -v
The -n flag disables ALPN, causing the client to fall back.
Using Burp Suite
Burp can be used to edit the ALPN extension in the TLS handshake via the Extensions → HTTP/2 → ALPN Manipulation module (available in Burp Pro 2023.6+). Steps:
- Open the Proxy → Options → HTTP/2 Settings tab.
- Enable Strip "h2" from client ALPN list.
- Intercept a request, forward it, and observe the server’s
ALPNselection in the HTTP history tab.
Burp’s Repeater can then be used to craft a raw Upgrade: h2c header and observe the server’s response.
Impact: triggering legacy vulnerabilities (e.g., request smuggling, header injection)
When a connection is forced into HTTP/1.1, attackers can reuse known attacks that were mitigated in HTTP/2:
- HTTP Request Smuggling: By sending mismatched
Content-LengthandTransfer-Encodingheaders, an attacker can split a single request into two, confusing downstream proxies. - Header Injection: HTTP/2 compresses headers using HPACK; many implementations reject duplicate pseudo-headers. In HTTP/1.1, duplicate headers are allowed, enabling injection attacks (e.g., CRLF injection).
- Cache Poisoning: Legacy caching layers may treat an HTTP/2 request differently than an HTTP/1.1 one. Downgrading can cause a request to bypass cache validation logic.
Because HTTP/2’s binary framing eliminates many parsing ambiguities, a downgrade re-introduces the textual parsing bugs that attackers have exploited for years. The net effect is a higher probability of successful exploitation with lower effort.
Automation with Python/Scapy and Burp extensions
For red-team engagements, automating downgrade attempts speeds up discovery. Below is a minimal Python script that uses scapy to manipulate the TLS ClientHello packet, stripping the h2 ALPN identifier.
from scapy.all import *
from scapy.layers.tls.all import TLSClientHello, TLSHandshake, TLS_Ext_ApplicationLayerProtocolNegotiation as TLS_ALPN
# Build a basic ClientHello with http/1.1 only
client_hello = TLSClientHello( version=0x0303, cipher_suites=[0x1301, 0x1302, 0x1303], # TLS_AES_128_GCM_SHA256, etc. extensions=[ TLS_ALPN(protocols=[b"http/1.1"]), # No "h2" entry → forced downgrade ]
)
handshake = TLSHandshake() / client_hello
packet = IP(dst="target.example.com")/TCP(dport=443, sport=RandShort(), flags="S")
# Send SYN, receive SYN-ACK, then send the crafted handshake
send(packet)
# (In a real script you would capture the SYN-ACK and continue the TCP three-way handshake.)
print("ClientHello with stripped ALPN sent")
For Burp, the open-source HTTP2 Downgrade Helper extension (available on the PortSwigger BApp Store) automatically toggles ALPN entries and logs the server’s response. The extension also provides a Repeater tab that can replay crafted Upgrade: h2c requests.
Defensive detection and mitigation strategies
Defenders need visibility into protocol negotiation and the ability to reject unexpected downgrades.
Detection
- Log the
ALPNselection on both client-side (via browser extensions or custom agents) and server-side (e.g.,error_login nginx with$ssl_alpn_protocolvariable). - Deploy network-based IDS/IPS signatures that flag TLS handshakes missing the highest-priority protocol (Snort rule example below).
- Use Passive TLS Scanners (sslyze, testssl.sh) to verify that servers always select
h2when offered.
alert tcp $HOME_NET any -> $EXTERNAL_NET 443 (msg:"TLS ALPN downgrade to HTTP/1.1"; tls_alpn; content:"http/1.1"; nocase; tls_alpn; content:"h2"; distance:0; within:2; classtype:policy-violation; sid:2024001; rev:1;)
Mitigation
- Enforce strict ALPN ordering: configure servers to refuse HTTP/1.1 when
h2is advertised (nginxhttp2directive, ApacheProtocols h2 http/1.1). - Disable clear-text h2c unless absolutely required. If needed, bind it to a dedicated port with strict firewall rules.
- Apply HSTS preload and
Expect-CTheaders to harden TLS. - Upgrade backend communication: ensure that internal services also speak HTTP/2 over TLS, removing the need for HTTP/1.1 bridges.
- Patch known parsing bugs in any legacy HTTP/1.1 components (e.g., old mod_proxy, outdated reverse proxies).
Common Mistakes
- Assuming HTTP/2 = TLS: Developers often think that using HTTPS automatically guarantees HTTP/2, ignoring the ALPN negotiation step.
- Leaving
Upgrade: h2cenabled on public ports: This opens a clear-text path that can be abused by network-adjacent attackers. - Testing only with browsers: Modern browsers hide ALPN details; use command-line tools to verify the actual protocol.
- Neglecting proxy-to-backend downgrade: Even if the edge terminates TLS with HTTP/2, a misconfigured reverse proxy may forward the request as HTTP/1.1, re-introducing vulnerabilities.
Real-World Impact
In 2023, a major cloud provider experienced a misconfiguration where their load balancer stripped the h2 token for certain SNI values. Attackers leveraged this to downgrade API traffic, then performed request smuggling against downstream micro-services, leading to credential leakage and a brief data breach affecting 12 k users.
My experience in penetration testing shows that many enterprises assume “HTTP/2 is safe” and skip testing for downgrade. The reality is that a single mis-configured reverse proxy can nullify all the benefits of HTTP/2. As HTTP/2 adoption climbs (currently >70 % of top-10 sites), the attack surface expands, making downgrade awareness a priority for any modern web security program.
Practice Exercises
- ALPN stripping with mitmproxy: Set up mitmproxy in transparent mode, configure it to drop the
h2entry, and verify withnghttp -vthat the server falls back to HTTP/1.1. - h2c upgrade abuse: Deploy a local nginx with
listen 8080;andhttp2enabled. Usecurl --http1.1 -H "Upgrade: h2c" -H "HTTP2-Settings: AAEAAEAAAAIAAAABAAEAAEAA" https://example.comand observe the server’s response code. - Automated downgrade scanner: Extend the Python/Scapy script above to iterate over a list of domains, log any that negotiate HTTP/1.1 despite offering
h2. - Detection rule test: Deploy the Snort rule provided earlier, generate traffic with and without ALPN downgrade, and confirm the alerts fire correctly.
Further Reading
- RFC 7540 - HTTP/2 Specification (focus on sections 3.2 and 9.2 for upgrade mechanisms).
- RFC 8446 - TLS 1.3, especially the
Supported VersionsandALPNextensions. - “The H2C-Stripping Attack” - Black Hat USA 2022 presentation slides.
- OWASP Cheat Sheet: Transport Layer Protection.
- “HTTP Request Smuggling” - PortSwigger Web Security Academy module.
Summary
HTTP/2 downgrade attacks exploit the negotiation phase-ALPN or clear-text Upgrade-to force legacy HTTP/1.1 or clear-text h2c. By manipulating these handshakes, attackers can resurrect classic vulnerabilities such as request smuggling and header injection. Defensive teams must log protocol selections, enforce strict ALPN ordering, disable unnecessary h2c endpoints, and monitor for downgrade signatures. Automation with Python/Scapy or Burp extensions streamlines testing, while real-world incidents underscore the critical need for proactive mitigation.