Post by Eric Rescorla
Well, in every existing proposal, it proposes to give them control over the
vast majority of
bytes on the socket, with some uncontrollable, but predictable framing. In
order for this to
be exploitable, you primarily need a proxy which is willing to ignore
enough to allow the attacker to resynchronize.
I can't agree with you on this Eric, because your study relies on broken
components that act as you want depending on the test. For instance, there
is the following paragraph concerning the POST-based handshake which is
Consider an intermediary examining packets exchanged
between the browser and the attacker's server. As above,
the client requests WebSockets and the server agrees. At
this point, the client can send any traffic it wants on the
channel. Unfortunately, the intermediary does not know about
WebSockets, so the initial WebSockets handshake just looks
like a standard HTTP request/response pair, with the request
being terminated, as usual, by an empty line. Thus, the client
program can inject new data which looks like an HTTP request
and the proxy may treat it as such. So, for instance, he might
inject the following sequence of bytes:
It assumes that an intermediary can accept a new HTTP request on
a non-keep-alive response. This does not work, this is not even
remotely looking like HTTP. The intermediary needs a content-length,
a transfer-encoding or a method or status code which implies an
empty body to be able to accept a second request. Here we have a
POST and a 200. Without either a content-length or a tranfer-encoding,
it is the last response and the response goes till the end of the
This is not even implementation-specific, such a compontent cannot
work without that.
It's important to keep the basic semantics of the HTTP protocol in
mind when addressing the handshake issues, because I once again feel
like we're trying to invent imaginary components for every type of
handshake that has to be dismissed. We could as well invent an imaginary
proxy that badly fails on the CONNECT handshake.
Post by Eric Rescorla
It's true that we don't have a demonstrated exploit against such a proxy and
what's required in order to mount such an exploit is probably somewhat
specific. However, I'm unaware of any convincing security analysis that
that the framing is a serious obstacle.
Please keep in mind that both the handshake and the framing are important.
Without a proper handshake, we could easily trick an existing proxy into
caching the first response as a wrong one, or perform cross-protocol
connections. The framing is important to prevent the situations where
intermediaries could have been mistaken by the handshake from confusing
raw data with some protocol they speak.
It has been said several times, but designing a keep-alive compatible
proxy really is tough work and requires to respect some key points of
HTTP, particularly the messaging. In order to be deployed, the component
does not have to be safe, but it has to work for the most common situations.
This means that we're sure to find proxies that don't work with 101 responses,
that do not correclty handle multiple content-length, that accept any raw data
in header names etc... (eg: apache has all these flaws but it's one of the
most commonly deployed reverse-proxies). But the component must flawlessly
work on the most common traffic. Basically, handling a POST with a 200 and
supporting correct messaging (ie respecting a missing or a correctly-formed
content-length header) is essential.
This means that if the framing ensures that :
- initial data sent from the client to the server cannot be parsed as a
valid HTTP request
- initial data sent from the server to the client cannot be parsed as a
valid HTTP response
then we have something that cannot fool these components. For instance, we
could very well have a POST and a 200 both containing a Content-length: 1 gig
and have a safe channel for us for the first gig of data. This would be stupid
because many proxies will not necessarily let the client-to-server data pass
once the server responds. But still it shows why HTTP messaging is important.
We could also ensure that an exchanged hello frame right after the handshake
looks like an un parsable HTTP request/response or a parsable end of HTTP
request/response to intermediaries. One such easy solutions would be to
prevent any CR or LF byte from being sent in the whole framing. In this case,
whatever the handshake, it's really the framing that's protecting you.
That's why I'm saying that both handshake and framing are important and
that we must not ignore reality. It's nice to invent imaginary components
to see how an issue or another could lead to an exploitable situation, but
at one point, it is important to weigh the degree of imagination when drawing
to a conclusion.
I'm not dismissing your work nor am I against the use of the CONNECT method,
indeed I was one of the first to ask for it several months ago. I'm just
trying to ensure that we focus on real world tests with both handshake and
framing trying to do the right thing and being usable by intermediaries as
For example, in your case, the whole security of the CONNECT-based method
relies on the fact that "websocket.invalid" cannot be resolved. Have you
imagined that future malware will try to add their own IP address in front
of "websocket.invalid" in every etc/hosts so that they can try to divert
most of the leaking requests ?
***@pcw:~# echo "127.0.0.1 websocket.invalid" >> /etc/hosts
***@pcw:~# ping websocket.invalid
PING websocket.invalid (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.022 ms
It would even be enough to couple that with DNS poisoning attacks, because
surely some proxies will try to resolve it via the DNS :
***@pcw:~$ host -a websocket.invalid
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52408
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;websocket.invalid. IN ANY
;; ANSWER SECTION:
websocket.invalid. 3600 IN CNAME www.attacker.com.
I'd rather have something which works by protocol construction than by
pure luck because hopefully nobody resolves websocket.invalid.