Better don't be too QUIC(K)

A deep dive into the technical aspects of the QUIC protocol will show why it is relevant to care about it in regards to security for the upcoming years.

QUICforge – tl;dr 

QUICforge: Client-site Request Forgery in QUIC is a paper Yuri Gbur, security consultant and researcher at SEC Consult, and Florian Tschorsch, professor for Computer Science at Technische Universität (TU) Berlin, presented at the Network and Distributed Systems Security (NDSS) Symposium 2023.

It resulted from a master thesis that Yuri Gbur wrote in collaboration with SEC Consult. In the paper they explore client-site request forgery attacks in the QUIC protocol. This blogpost is aimed to give less-technical readers a short overview about the topic and why it is relevant to care about QUIC in regards to security for the upcoming years. 

Additionally, the second part of this blogpost provides a deep dive into the technical aspects of the research and gives some more background about QUIC that could not be covered in the paper and the presentation at NDSS 2023. 

What is QUIC? 

Protocol Stacks
Figure 1: Comparison of protocol stacks between HTTP/2 and HTTP/3.

QUIC is a modern approach for a transport protocol. Initially proposed by Google in 2013, it was re-designed and improved over the last years until it was finally released in 2021. It provides reliable data-transfer similar to the “legacy” transport control protocol (TCP).

Yet, it also integrates the encryption capabilities that are currently done in a separate protocol layer called transport layer security (TLS). Furthermore, it integrates multistreaming capabilities currently handled in the application layer (e.g., HTTP/2).

Figure 1 compares both protocol stacks of HTTP/2 and HTTP/3 which is using QUIC. This combination of features allows QUIC to set up connections faster, giving it an advantage for modern real-time networking demands while also increasing security and privacy. Furthermore, QUIC achieves to maintain connections even if clients change networks, giving it more flexibility in a dynamic environment. 

 

Why is this relevant? 

Because of the performance and security guarantees of QUIC, it gained a lot of traction in the industry. Already a major portion of Google’s traffic is done via QUIC. Multiple other well-known companies also started developing their own implementations, e.g., Microsoft, Facebook, CloudFlare, Mozilla, Apple and Akamai, just to name a few. Furthermore, the decision was made to use QUIC as the new transport layer protocol for the HTTP3 standard which was standardized in 2022. This makes QUIC the basis of a major portion of future web traffic, increasing its relevance and posing one of the most significant changes to the web’s underlying protocol stack since it was first conceived in 1989.  

The likelihood that you or your company will be confronted with or already use QUIC, is pretty high. On a first glance, all the security features of QUIC look compelling but it is paramount that these promises are challenged.   

Security Challenges with QUIC 

Using QUIC comes with some pitfalls in terms of network security. The most prominent one is that QUIC is built on top of UDP. Most firewalls handle TCP and UDP traffic separately and often differently. A pretty basic example would be a network that disallows the usage of web services by blocking all TCP traffic (the currently still used transport layer protocol for the web). However, if UDP traffic is not restricted, the existence of QUIC still allows clients to reach parts of the web, e.g., all Google websites and services. This issue was already presented at BlackHat by C. Pearce and C. Vincent in 2016.

Even if network admins are aware that QUIC is being used, the usage of the connection-less UDP protocol inherits further risks. All the common transport layer firewalls (e.g., iptables) can no longer track connection states, degrading them to less effective stateless firewalls. Allowing QUIC traffic for your webservers will likely make the network vulnerable to attacks like UDP hole punching, which utilizes the stateless nature of UDP to bypass inbound traffic restrictions. If you want to read more about this issue, please refer to the tech report paper we published in 2021.

Of course, there are applications for network protection that allow to evaluate the protocol layers above UDP and TCP. However, as QUIC makes security and privacy a first-class citizen and encrypts most of the protocol information, the insight into connections is very limited. Research by L. Decker in 2020 showed that many of the existing intrusion detection systems (IDS) and intrusion prevention systems (IPS) struggle with detecting malicious actions like command and control (C2) traffic. 

Methods to secure environments using QUIC will take time to mature. But besides securing environments around QUIC, it is always interesting to ask if is it possible to utilize the protocol itself to execute attacks? The QUICforge research was aimed to explore this topic further.  

Weaponizing QUIC 

With the QUICforge paper, we looked into client-side request forgery attacks. This means that the attacker is always the client of the QUIC connection. The QUIC server which the client aims to exploit is called the victim. The attacker tries to trick the victim into sending one or multiple datagrams to another target.  

It is almost impossible to entirely avoid the forging of requests in network protocols. At some point all network protocols send at least one packet on a yet unvalidated path. For an endpoint, it is impossible to tell if a source address is legit or spoofed. In the aspect of security, the primary question is what an attacker could do with the forged requests. From an attacker’s perspective, there are two primary goals of request forgery. The first goal is to bypass network restrictions as the server can reach other (internal) hosts which are not directly reachable from the attacker. The second goal is to leverage resources of the victim (e.g. bandwidth).  

We found three request forgery techniques in QUIC where packets can be sent to an arbitrary target by spoofing the source IP address and/or port. With one of the techniques, we showed the possibility of protocol impersonation and with the other two, we uncovered amplification vectors that were verified in real-world implementations of QUIC. 

Protocol Impersonation 

With one of the techniques, we showed that it is possible to craft payloads that are interpreted by the target as another UDP-based protocol than QUIC. As a proof of concept, we demonstrated how to send a valid DNS request for an arbitrary host name. This could potentially be used to poison internal DNS caches as the capability of forging DNS requests removes a lot of uncertainty with timing of these attacks.  

While there are some limitations for the possible payload, it should be assumed that various other UDP-based protocols can be impersonated as well. As the attack is based solely on the protocol itself and not based on a bug, every QUIC server is vulnerable to it and there is no built-in fix for the issue. Until addressed in a future QUIC version, it should be assumed that this attack is valid. Administrators must consider additional network security controls to limit the exploitability.  

Traffic Amplification 

Likely, the more known attack through request forgery is traffic amplification. This vulnerability is exploitable, if the victim sends significantly more bytes to the target than the attacker needs to send to the victim to forge the request. This can be utilized for (D)DoS attacks, as an attacker can profit from the bandwidth of the server. 

Theoretically, the QUIC specification considers these types of attacks and limits the amount of bytes sent on any unvalidated path to three times the amount of received bytes. However, we identified pitfalls and edge cases for implementing the various features of QUIC where this limit might be surpassed. To uncover if these issues actually occur in the real world, we built an evaluation setup with 13 open-source implementations of QUIC servers (please refer to the Section 2.3 for more detailed information about the test setup). The evaluation showed that almost all implementations varied in the assessed parameters showing a general discrepancy between specification and implementations. Five of 13 implementations missed the identified pitfalls and exerted high amplification factors. We contacted the vendors of all affected servers in a responsible disclosure and the issues were fixed or the features causing the issues can be turned off. 

Yet, our experiments highlight the importance of challenging the security claims by the QUIC specification and test the implementations against these claims.

While being promising on paper, the change to the novel QUIC protocol will also cause novel security challenges which network administrators and blue teams have to be aware of.

While being promising on paper, the change to the novel QUIC protocol will also cause novel security challenges which network administrators and blue teams have to be aware of.
Yuri Gbur, Security Consultant and Researcher at SEC Consult
Figure 2: QUIC Handshake

QUICforge – A technical view 

In this section, we want to provide an insight into the technical aspects of our research about request forgery in QUIC. The first part provides a sophisticated introduction into the QUIC protocol while the second part covers our attack setup and tooling. We strongly advise also reading the original paper as this section is only complementary and does not cover all aspects of our research. 

A Deeper Look into QUIC

QUIC aims to provide transport layer stream abstraction similar to TCP while making privacy and security a first-class citizen. Therefore, as much as possible of the protocol data is encrypted including most state and control information. Furthermore, QUIC enables multiplexed transport of individual streams via one connection, introducing additional complexity to the protocol design.

Terminology and Notation

In order to distinguish the different protocol layers, we utilize the following terminology, as it is defined in RFC 9000: 

  • Endpoint: An entity that speaks QUIC. The endpoint initiating the connection is called client, the endpoint accepting a connection is called server. 
  • QUIC packet: A complete, processable unit of QUIC that can be encapsulated in a UDP datagram. Multiple QUIC packets can be encapsulated in one UDP datagram. 
  • Frame: A unit of structured protocol information. One QUIC packet can contain multiple frames up to the UDP datagram size limitations. 
  • Address: internet protocol (IP) address and UDP port number pair. For the conducted experiments only IP version 4 (IPv4) addresses are used. 
  • Connection ID (CID): QUIC specific endpoint identifier. Depending on their use, they are called source connection ID (SCID) or destination connection ID (DCID). 
  • Stream: Channel of ordered and reliable byte transfer within a QUIC connection. A QUIC connection can carry multiple uni- and bidirectional streams. 

Connections and Streams 

QUIC is a connection-oriented protocol, i.e., the client and server have a shared state and data is transferred reliably and in order. To identify connections, QUIC introduces CIDs as endpoint identifiers to allow for network-path changes of the UDP port and IP address. CIDs are variable in length up to 20 bytes in the current version and must not contain any information that can be used to correlate them with other CIDs. The length of a CID is communicated in the handshake or over NEW_CONNECTION_ID frames during the connection. Regular packets have a short header format containing only the DCID. Thus, the endpoint needs to remember the corresponding lengths. A peer can store multiple CIDs for future use for a connection. All CIDs used during one connection have to be different from each other and have a unique sequence number associated with them. The maximum number of CIDs is advertised in the handshake parameters. Furthermore, peers have the possibility to retire a CID via the RETIRE_CONNECTION_ID frame.

If packets cannot be matched with a CID and if they do not open a new connection, they are discarded, or they may trigger a version negotiation if the received packet is large enough (see next chapter 2.1.3). Packets that can be matched but are out of order and cannot be decrypted yet, may be buffered or dropped.  

One connection can have multiple uni- or bidirectional non-blocking streams. Streams are identified by a 62-bit stream ID which are assigned incrementally and are therefore unique within a connection. The two least significant bits (LSBs) indicate whether the stream is client or server initiated and whether it is uni- or bidirectional. As stream IDs are assigned sequentially, choosing a higher ID automatically “opens” all streams with a lower ID. The maximum number of streams is either exchanged during the handshake or via MAX_STREAM frames during a connection. Application data is transferred in STREAM frames which are limited to flow control and buffer limitations.  

Flow control for QUIC is performed on two layers: individually for each stream and for the whole connection. The initial flow limits are exchanged in the handshake. Limits during an ongoing connection are advertised by the receiver via MAX_STREAM_DATA frames for individual streams as well as via MAX_DATA frames for the entire connection. Analogously STREAM_DATA_BLOCKED and DATA_BLOCKED frames are used if flow limits are reached. 

Handshake 

The QUIC handshake combines the transport layer handshake and the TLS cryptographic handshake. QUIC also supports the TLS 1.3 0-RTT handshake. However, we always refer to the 1-RTT handshake shown in the figure below. 

The QUIC handshake is comprised of initial and handshake packets. Both packet types use a long header format and contain a SCID and a DCID with the corresponding lengths. For the first message in a connection the client sets the DCID to an arbitrary value. The server can choose to use the suggested value or to replace it in the SCID field in its initial message. The headers for initial and handshake packets are similar except for a token field in the initial packet that is used in the retry mechanism. A retry can be initiated by the server upon receiving an initial packet from an endpoint that does contain no or an unknown token. In order to validate the path to the new endpoint, the server sends a retry packet containing a fresh token that has to be mirrored in a second initial packet from the client. The normal handshake flow without a retry does also perform path validation implicitly but by using retry packets, the server can avoid generating and sending the significantly larger cryptographic information to an unvalidated endpoint. However, while retries avoid these unwanted large data transfers, they add a RTT to the connection setup. 

The cryptographic TLS handshake is carried in CRYPTO frames transmitted in initial and handshake packets. The cryptographic messages are similar to conventional TLS messages: The client sends a client hello (CH) message transmitting supported cipher suites and some random data needed for the key generation (key share). The server answers with a server hello (SH) containing the selected cipher suite, server certificate, key share, signature and hashed transcript of the cryptographic handshake so far, called FIN. The final CRYPTO frame from the client contains a FIN of the whole handshake. For reliability all packets of the entire handshake (and all subsequent messages) are acknowledged with ACK frames. 

Besides the normal handshake flow in the figure below and the retry mechanism, QUIC further supports a version negotiation mechanism. All long header packets always contain the used QUIC version encoded in a 4-byte field. If a server receives an unknown version, it will answer with a version negotiation packet providing a list of supported versions. Version negotiation packets must always have the version identifier 0x00000000. On receipt of a version negotiation, a client can start a new handshake with a mutually supported version. 

 

Figure 3: Evaluation setup for client-side request forgery in QUIC.

Connection Migration 

One fairly unique feature of QUIC is connection migration. The usage of CIDs allows connections to survive endpoint address changes after a connection is established. Whether a server allows for connection migration, is communicated via the disable_active_migration transport parameter in the handshake. Currently this feature is limited to client migration. If a migrated endpoint is detected by the server, it has to perform a path validation to the new host. To validate the path, the server sends one or multiple PATH_CHALLENGE frames, containing an unpredictable payload, to the new endpoint address. A client receiving a challenge has to send a PATH_RESPONSE frame that echoes the received data to complete the path validation. 

Requirements for Request Forgery in QUIC 

Please refer to the first section of this blog post for a general introduction to request forgery or view the respective sections in the paper for a more comprehensive view, we only reiterate over the most important concepts in the following sections and focus on the technical aspects. 

We analyzed three attack modalities: 

  1. Connection Migration Request Forgery (CMRF) utilizing the connection migration functionality  
  2. Server Initial Request Forgery (SIRF)  
  3. Version Negotiation Request Forgery (VNRF) utilizing two variants of the handshake.  

All three modalities are based on spoofing the source IP address and/or source port. Therefore, an attacker needs the capability to modify the IP protocol fields as well as the UDP protocol fields. 

Another requirement for protocol impersonation is to modify the QUIC header. Optimizing the amplification factor further requires an attacker to send minimal QUIC packets (e.g. single ACKs). 

An additional challenge with looking at early implementations of protocols is interoperability. In the following section we describe our approach to addressing these requirements and challenges for our evaluation setup. 

Setup 

We used two virtual machines for client and server both running Ubuntu 20.04. For our tests, both VMs were placed in the same network. However, as long as the QUIC server is reachable from the client, the request forgery will still work for separate networks. The entire setup is shown in Figure 3. 

Youtube Screenshot
DNS Protocol Impersonation with Version Negotiation in QUIC.

Manipulating Traffic on the Client 

For the performed experience we decided to use a modular approach that allowed us to plug in different QUIC clients to overcome the mentioned interoperability issues. Finally, we ended up with lsquic and aioquic as QUIC clients. Lsquic gave us the broadest support of versions and command line options to configure it and aioquic, being written in python, gave us an easier option to alter the actual implementation than the C++-based lsquic.  

The modularity was achieved by intercepting the traffic from and to the client with netfilter (iptables) queues, modifying the raw packets and then forwarding them to the server. This gave us the power to interfere directly with multiple layers of the network stack (IP, UDP). Encryption could be ignored for our attack scenario, as we only modified unencrypted data of the packets. The interaction with the netfilter queue was done with the NetfilterQueue python packet and the modification of the packets was realized with ScaPy. 

Even though it theoretically would have been possible to also modify the QUIC fields via ScaPy (no QUIC dissector is available yet), it was much easier to deal with the variable length fields by directly editing the source code of the aioquic server. We patched the client implementation to have additional command line arguments to set the CIDs manually. Furthermore, we lifted the static limit of maximum 20 bytes to utilize the full potential of the version negotiation mechanism for protocol impersonation.  

The attack script and client setup instructions can be found in our GitHub repository Be aware that the current setup is very experimental. 

In its current state, the client script supports all three attack modalities. The spoofed IP address has to be set while the target port is optional. For connection migration it can be chosen if only one or if all pending packets should be spoofed. We started changing the VNRF mode to a modular payload approach for adding other protocols in the future. However, currently only DNS is supported. Lsquic is still required for some of the functionality but considered as “legacy” mode. The goal is to write a standalone tool directly integrating the aioquic libraries for all attack modalities. 

Analyzing Traffic 

We used Wireshark in version (v3.7.0) to have the (back then) most up to date dissection capabilities for QUIC. To have insight into both the traffic from attacker to victim as well as from victim to target, we ran Wireshark on the server. Not all servers supported exporting the TLS session keys for dissecting the captured traffic. Therefore, we exported them to the client and synced them to the server.  

We dockerized all server implementations for an easy deployment and released them here. All containers display the version or git commit hash when they are started. The individual Dockerfiles can also be found in the GitHub repository above.  

There are more implementations for QUIC than the 13 that were used in the setup. To have sufficient insight into the implementations, only open-source implementations were chosen from this list, which is curated by the QUIC working group of the Internet Engineering Task Force (IETF). From the available options, only those were chosen that supported QUIC version 1 (no draft versions) and were still actively maintained. A few implementations also had to be sorted out because of bugs that made our test impossible.  

The servers all have a large file in the root directory that can be requested. This allows for experimenting with CMRF as the connection is kept open for multiple seconds.  

The video below shows an exemplary usage of our setup. The neqo server is used as the victim and Google’s DNS server (8.8.8.8) is chosen as target. The attack script is used with VNRF and the DNS payload to forge a valid DNS request for tu-berlin.de. The traffic is captured and analyzed. First as QUIC traffic and then as DNS traffic, showing that the packet sent by the victim is valid in both cases. 

Conclusion 

The presented proof of concept shows clearly that protocol impersonation is possible with QUIC. It should be assumed that a creative attacker can impersonate multiple other UDP protocols as well. Unfortunately, this attack does not leverage any bugs in the implementations but solely the protocol specification itself. Therefore, mitigating the issue will require a new QUIC version with changes to the specification as proposed in the paper.  

Yet, bugs in QUIC should not be neglected. The research into traffic amplification showed that there is a gap between the existing QUIC implementations and the specification, leading to significant amplification vectors.  

We believe that there is a need for more research into the security of QUIC before the protocol can be adopted with a peace of mind. We hope that providing insight into our evaluation setup and sharing the tooling will inspire future research, helping QUIC to fulfill its security promises. 

 

This research has been conducted by Yuri Gbur and Florian Tschorsch (TU-Berlin) and published on behalf of the SEC Consult Vulnerability Lab

 

Are you interested in working at SEC Consult?

SEC Consult is always searching for talented security professionals to work in our team.