Racing and Fuzzing HTTP/3: Open-sourcing QuicDraw(H3)

November 18, 2025 Maor Abutbul

Open-sourcing Quicdraw H3

This blog post provides a dive into HTTP/3’s evolution for security engineers, an overview of our research journey, and what led us to develop the open-source tool QuicDraw, which can be used for fuzzing and racing HTTP/3 applications. QuicDraw implements “Quic-Fin-Sync” our implementation of the last-byte-sync with the single packet attack on HTTP/3. We conclude by evaluating QuicDraw’s performance against a real-world target and comparing its results to other tools.

The research detailed in this blog is both responsible and ethical. QuicDraw(H3) is an open-source tool developed for authorized security testing and is intended for use by security professionals in accordance with applicable laws and ethical standards. We do not condone, support, or encourage any unauthorized or malicious use of the information or tooling presented in this blog.

Outline

The evolution of HTTP and head-of-line blocking
HTTP/3 for security engineers
The journey to make race conditions work in HTTP/3
Introducing QuicDraw
QuicDraw evaluation – can we race HTTP/3 servers?
Summary and actionable insights for Fuzzing and racing HTTP/3 with QuicDraw

HTTP/3 in 2025: does anyone use it?

The Hyper Text Transfer Protocol (HTTP) has been the backbone of web communication since the early days of the internet. Over the years, it has evolved to meet growing speed, security, and reliability demands. The latest iteration, HTTP/3, represents a significant leap forward in this evolution; it improves connection setup, speed, security, and privacy as compared to HTTP/1/2.

Since its standardization by the Internet Engineering Task Force (IETF), HTTP/3 has seen rapid internet-wide adoption, with widespread support across major web browsers.

Large internet companies and CDNs have already implemented HTTP/3 on their servers.

HTTP/3
Figure 1 – HTTP/3 support – all websites (W3Techs.com)

As seen in the image (W3Techs.com), 35% of all websites support HTTP/3.

In the next section, we dive into relevant HTTP features, a bit of history, and head-of-line blocking (HOL). If you are familiar with these topics, you can go directly to HTTP/3​ for Security Engineers.

The evolution of HTTP and head-of-line blocking

HTTP/1.1, introduced in 1997, significantly improved the original HTTP/1.0, allowing for persistent connections and reducing latency. In HTTP/1.1, each connection can only handle one request at a time. When a client sends multiple requests, they are queued up. The server must complete the response to the first request before it can start processing the next one. This sequential processing can lead to significant delays, especially if the first request takes a long time to complete.

HTTP/1.1 requires each response to be fully delivered before starting the next. As web applications became larger and more complex (bandwidth and storage), the limitations of HTTP/1.1 became apparent. This led to the development of HTTP/2, which introduced multiplexing (sending multiple requests simultaneously, on the same connection), header compression (reducing the size of headers), and server push (sending resources proactively) to enhance performance.

HTTP/2

HTTP/2 introduces multiplexing as one of its key features, allowing multiple requests and responses to be sent simultaneously over a single connection. HTTP/2 fixed the HTTP/1 head-of-line blocking problem, when clients had to wait for the first request in line to finish before the next one could go out.

Even though HTTP/2 eliminates application-layer (HTTP/1.1) HOL blocking (with multiplexing), HTTP/2 is still susceptible to TCP HOL blocking, since an HTTP/2 client can send multiple requests on a single (TCP) connection (see image below).

Figure 2 – TCP head-of-line blocking (HTTP/2)

In HTTP/2, if a packet at any point of a connection is lost (or delayed), the following packets are buffered, and the TCP “stack” will hold the data (including following requests on the same connection), leading to TCP HOL blocking.

Solving head-of-line blocking was also one of the primary motivations behind not just HTTP/2 but also HTTP/3. HTTP/2 failed to completely address HOL blocking, which is why we needed HTTP/3.

In the next section, we dive into relevant HTTP/3 features, how it solves head-of-line blocking, and HTTP/3 discovery. If you are familiar with these topics, you can go directly to The journey to make race conditions work in HTTP/3.

HTTP/3 for security engineers

HTTP/3 is the latest version of the HTTP protocol, designed to address the shortcomings of its predecessors by leveraging the QUIC (Quick UDP Internet Connections) protocol.

HTTP/3 runs on top of a protocol originally developed by Google, and the connectionless UDP protocol (no guarantee of delivery or order) rather than TCP. Often dubbed “TCP 2.0,” QUIC retains TCP’s reliability, congestion control, and flow control despite using UDP.

This fundamental shift delivers several key benefits:

  • Faster connection establishment: QUIC reduces the time required to establish a connection. Unlike TCP, which requires multiple round-trips (TCP handshake and TLS handshake) to establish a connection, QUIC can do it with just one round-trip, significantly reducing latency.
  • Always encrypted: QUIC integrates Transport Layer Security (TLS 1.3) directly into the protocol, providing encryption for all QUIC traffic, including metadata that is clear in HTTP/2 over TCP. This enhances security and accelerates the connection process by consolidating the handshake steps of TCP and TLS.
  • Multiplexing without TCP head-of-line blocking: One of the significant improvements in HTTP/2 was multiplexing, which allows multiple requests and responses to be sent over a single connection. However, HTTP/2 still suffers from head-of-line blocking at the TCP layer. By utilizing UDP, QUIC solves TCP HOL blocking (see the figure ).

Quic Solve Hol Blocking

Figure 3 – QUIC solve TCP head-of-line blocking

As can be seen in the image above, in UDP (hence QUIC) packets are not “blocked” (buffered) at the UDP layer. Therefore, HTTP/3, using QUIC, eliminates the TCP HOL blocking issue, allowing for more efficient data transfer.

  • Connection migration: QUIC supports connection migration, which allows a connection to continue seamlessly even if the client’s IP address changes, such as when switching from Wi-Fi to a mobile network.
  • Header compression: QPACK is the header compression mechanism used in HTTP/3, designed to replace the HPACK compression used in HTTP/2. This is because HPACK relies on the ordered and reliable delivery of data provided by TCP, which is not available in UDP. It aims to reduce head-of-line blocking and enhance performance by efficiently compressing HTTP headers.

Since QUIC is so different from TCP, it also means we cannot just run HTTP/2 on top of it, which is why HTTP/3 was created. (Ref: Head-of-Line Blocking in QUIC and HTTP/3: The Details )

In the following sections, we dive into some technical aspects of HTTP/3, including how a client discovers that HTTP/3 is supported on a server and how developers can add HTTP/3 to their websites.

Client’s HTTP/3 support discovery

When HTTP/3 is enabled, the server advertises support to clients, allowing them to attempt HTTP/3 connections with the server.

HTTP/3 support is revealed after establishing a “lower” HTTP connection (HTTP/2 or HTTP/1) with the server’s response, which includes an Alt-Svc header. This header indicates that the same service is available over a different protocol or location.

Alt-Svc header

Figure 4 – The Alt-Svc:h3 header in a response indicates the server supports HTTP/3.

As seen in the image, the server (youtube.com in this case) includes an Alt-Svc header, the value h3=”443” indicating that the server supports HTTP/3 (over QUIC) on UDP port: 443.

Note: server support can also be published via DNS (RFC 9460).

Properly implemented clients always fall back to HTTPS (HTTP/1 or HTTP/2) when they cannot establish an HTTP/3 connection. Clients can also use their cached prior knowledge of HTTP/3 support to save unnecessary round trips in the future. Because of this fallback, enabling or disabling HTTP/3 in a server should not disrupt the client’s ability to connect to the server.

HTTP/3 server deployment

Many popular web servers, including Nginx and OpenResty, have added support for HTTP/3, making it easier for developers to take advantage of its benefits. Enabling HTTP/3 on your web server typically involves updating your server software to a version that supports QUIC. Negotiating HTTP versions happens seamlessly, requiring no changes to website code.

And if you are using a CDN, it is likely supported by default or can be easily activated. (see HTTP/3 Support | GCP or HTTP/3 Support | AWS CloudFront).

In the next section, we share how our research journey led us to develop QuicDraw.

The journey to make race conditions work in HTTP/3

We will start with a short recap on race conditions.

Race conditions, a common vulnerability, arise when websites process concurrent requests without proper safeguards. This can cause different threads to access and modify the same data simultaneously, leading to conflicts and unpredictable application behavior.

During a race condition attack, an attacker sends precisely timed requests to deliberately trigger these conflicts, exploiting the resulting inconsistencies for malicious gain. (PortSwigger Research)

The objective: make race conditions work in HTTP/3

Following the massive internet adoption, inspired by PortSwigger’s research on race conditions and our previous research on Keycloak, in which we found a race-condition issue, we decided to explore the route to make race conditions work in HTTP/3.

Why reinvent the wheel? (Yet another tool)

As mentioned above, almost all web browsers support HTTP/3. Nevertheless, at the time of publishing this article, there are no popular HTTP/3 security tools. Moreover, none of the industry-standard interception proxies (MitmProxy, ZAP, Burp) supports HTTP/3 (as a client).

The only (mature) tool we found that supports HTTP/3 is curl. However, in curl, HTTP/3 is not built on the standard executable. To use curl (with HTTP/3 support), one needs to build curl with HTTP/3 support or use any other curl executable built with HTTP/3 support.

Additionally, many libraries that implement the QUIC protocol are available. In an attempt to bring order to this area, we created this list, Awesome-HTTP3 (a curated list of HTTP/3 and QUIC-related implementations, articles, security blogs, and tools). You are more than welcome to contribute.

The path to QuicDraw implementation

“Since servers only process a request once they regard it as complete, maybe by withholding a tiny fragment from each request we could pre-send the bulk of the data, then ‘complete’ ~100 requests with a single QUIC packet.” (Inspired by PortSwigger Research)

We have established our objective: enable race conditions in HTTP/3. In the next sections, we dive into our attempts.

QUIC on the racetrack – the naive approach: fragmentation

In this section, we share our thought process and the story of our first attempt (fragmentation):

So far, we know that HTTP/3 is “just” HTTP/2 over QUIC, and QUIC is an effort to implement the good parts of TCP. “Call it TCP/2. One more time.” (as stated in ngtcp2 QUIC protocol implementation used by curl).

We also know that QUIC runs over UDP, which runs over IP

Cool, and? IP has fragmentation

So, let’s just fragment the HTTP/3 traffic, and this way, we have “racing” on HTTP/3.
(something similar to this Flatt Security blogpost)

Cool, we found a solution.

“Let’s go!”
Unfortunately, that’s not the case…

QUIC RFC (9000)

Figure 5 – QUIC RFC (9000), “UDP datagrams MUST NOT be fragmented at the IP layer.”

As can be seen in Figure 5, according to the QUIC RFC (9000), “UDP datagrams MUST NOT be fragmented at the IP layer,” so this route is not feasible.

Even if we could use fragmentation, each request has its own stream.

So, fragmenting one stream should not interfere with other streams…but streams can be multiplexed.

In the next section, we will dive into our second attempt: using multiplexing.

QUIC on the racetrack – multiplexing

Since IP fragmentation is not an option, we decided to try another route to enable race-condition testing in HTTP/3 — splitting the requests on multiple QUIC packets and then sending the HEADERS and DATA frames, but this time holding the last byte of each request.

Finally, send the last byte of each “partial” stream with the FIN flag (indicating no more data to be sent on each stream), and that’s it (see Figure 6).

QUIC implementation

Figure 6 – QuicDraw last-byte-sync network perspective

Upon receiving these packets, the server (QUIC implementation) will release the requests (in bulk) and process them “together,” leading to race-condition potential on the server. So, with this concept, we have race-condition testing (or last-byte-sync) on HTTP/3.

Quic-Fin-Sync implementation – the algorithm

After drawing all our conclusions, this is our final algorithm pseudocode:

For each Request
    queue Request-Headers
    queue Request-Data[:-1] # request data without the last-byte

Transmit # release queue – send together.
wait 1000 ms

For each Request
    queue RequestData[-1:] & end_stream=True # the last byte of the request (data) and QUIC FIN

Transmit # release queue – send together.

Figure: Pseudocode of QuicDraw Quic-Fin-Sync algorithm.

Note: The 1000 ms delay is used to let all the first bulk packets arrive at the server before the last-bytes packet arrives.

QuicDraw traffic diagram

The following flow diagram is based on traffic inspected while using QuicDraw.

QuicDraw Traffic Diagram

Figure 7 – QuicDraw (HTTP3) Quic-Fin-Sync traffic diagram

In Figure 7, we can see that:

  • First, we perform a QUIC handshake.
  • Then we send our requests (via streams) for headers and data frames (without the last-byte, and with the FIN flag not set).
    • The server’s underlying QUIC implementation can send us QUIC-ACK (in this timeslot).
  • We wait for several milliseconds (to ensure all of the traffic arrives at the server).
  • Then, send a single QUIC packet that includes the last-byte of each request with the QUIC FIN flag set.

As seen in Figure 7 , the server can respond to each request the moment it completes processing it, as opposed to HTTP1/1, where responses are ordered (based on the receiving order – FIFO). This behavior represents the head-of-line blocking elimination in action.

Now that the theory is covered, in the next section, we will introduce QuicDraw.

Introducing QuicDraw

Racing HTTP/3 servers
Figure 8 – QuicDraw is our open-source security research tool for fuzzing and racing HTTP/3 servers.

QuicDraw(H3) is our open-source ethical security research tool designed for HTTP/3 servers.

In our context, racing means exploiting race conditions, and fuzzing means enumerating or sending multiple different URLs or POST-data payloads.

QuicDraw’s Quic-Fin-Sync feature enables effective race-condition testing on HTTP/3 over QUIC.

QuicDraw main features

  • Implemented Quic-Fin-Sync on HTTP/3 (over QUIC)
  • Supports fuzzing (sending multiple requests) with the FUZZ (keyword) and wordlist mechanism
  • Based on aioquic (http3_client)
  • Includes SSL-Key-Log-File support (used to decrypt the traffic for inspection via packet analyzers such as Wireshark)
  • Send custom HTTP headers functionality (-H argument).

In the next section, we evaluate QuicDraw’s performance against other tools.

QuicDraw evaluation – can we race HTTP/3 servers?

To evaluate QuicDraw performance against other tools, we used the following setup (see the figure 9 below):

  • We set up a Keycloak (version 23) system with a known race condition on an AWS EC2 machine.
  • We used AWS CloudFront because it gives us a real-world HTTP/3 server with a relatively easy setup process.

QuicDraw Evaluation
Figure 9 – Our test setup: using QuicDraw on Keycloak behind CloudFront (configured to serve HTTP/3)

QuicDraw performance vs other tools

We used the above test setup and ran five different tools from the same machine (using the same internet connectivity).

In our tests, we used curl. Although curl is not built specifically for race conditions testing, it can support HTTP/3 (special build), and it is highly optimized; nevertheless, we use it as a benchmark indicating a “baseline” to which we can compare.

We’ll start with some background for the test:

  • In Keycloak, an admin can create an API Token for developers to use on the system.
  • On creation, an admin can limit the number of clients that can be created by the developer (API Token).
  • The developer uses this API Token to create clients via the Keycloak API.
  • When using the API Token, the token’s “remaining count” is updated.

API Token
Figure 10 – Keycloak – admin sets the number of clients that the token can create.

For our tests, we set the count limit to one on all tokens, meaning each number below zero causes a race condition on the server side.

Below, we detail each tool and the essential parameters used for each tool’s execution.

Tool no. 1: racing using curl loop over HTTP/2

For this test, we used the static version of curl and commands similar to the following:

for i in {1..220};
do
/static-curl/curl -k -d ‘{“clientId”:”curl\_client\_’$i'”}’ -H ‘Authorization:…’ “https://keycloak23.cloudfront.net/path/” –http2 &
done

Tool no. 2: racing using curl loop over HTTP/3

For this test, we used the static version of curl (with HTTP/3 support) and commands similar to the following:

for i in {1..220};
do
/static-curl/curl -k -d ‘{“clientId”:”curl\_client\_’$i'”}’ -H ‘Authorization:…’ https://keycloak23.cloudfront.net/path/ –http3-only &
done

Tool no. 3: racing using QuicDraw (v0.8.0) over HTTP/3

For this test, we used commands similar to the following:

quicdraw_v08 “https://keycloak23.cloudfront.net/path/” -l /ssl_key_log_file.log -d ‘{“clientId”:”QuicDraw\_POC\_v08\_\_\_”}’ -H ‘Authorization: bearer …’

In this version (v0.8), we set the script to issue 220 requests.

In our specific setup, 117 requests were sent within a single (Quic-Fin-Sync) QUIC packet. (see Figure 11 below)

QuicDraw (v0.8.0) over HTTP/3

Figure 11 – QuicDraw in action (evaluation) with 117 (DATA) streams sent on a single QUIC packet

On (QUIC) stream limit (MAX_STREAMS)

In QUIC, set the maximum number of streams that can be “online” (sent with no response) between the client and the server via the MAX_STREAMS frame.

In our client (QuicDraw), we sent the stream limit (MAX_STREAMS frame) to 128 (see figure 12 below)

QUIC Stream Limit

Figure 12 – QuicDraw sets the stream limit (MAX_STREAMS frame) to 128.

Tool no. 4: racing using Turbo Intruder (HTTP/2)

As of the writing of this paper, Burp Suite does not support HTTP/3.

For this test, we set the for loop within Burp Turbo Intruder (a slightly modified version of race-single-packet-attack.py script to modify the payload sent in each request) to 110 requests. Since larger values (220) got our burp to hang and not respond, we decided to run this test with a lower value (110).

Racing using Turbo-Intruder
Figure 13 – Traffic inspected using Turbo Intruder,30 streams sent over a single-packet.

As seen in the figure 13 above, 30 requests are sent in a single-packet (this limit is also mentioned in PortSwigger’s paper).

Tool no. 5: racing using Burp Intruder (HTTP/2)

We set the number of payloads to 220 requests for this test. According to our tests, Burp Intruder does not use the single-packet mechanism.

In the next section, we dive into our racing (tests) results.

Evaluation results – QuicDraw racing the Keycloak

Below are the results of our testing:

QuicDraw Racing the Keycloak

Figure 14 – Results racing vs our test setup using QuicDraw, curl, Burp Intruder, and Turbo Intruder.

As seen in our test results (Figure 14), we set a count limit to one on all tokens. This means each number below zero caused a race condition on the server side. Moreover, the lower the number, the more effective the tool at exploiting the race condition on the server side.

We used the above command/tool to evaluate its performance, testing each tool three times with different (Keycloak initial access) tokens.

Key insights from our tests:

  • Using tool no. 1, curl (HTTP/2) in a for loop gave us the following results: (-22, -2, -13).
    • Not using the single-packet mechanism.
  • Using tool no. 2, curl (HTTP/3) in a for loop gave us the following results: (-1, 0, -6).
    • Not using the single-packet mechanism.
  • Using tool no. 3, QuicDraw (v0.8) gave us the following results: (-66, -38, -79).
    • Using our implementation of Quic-Fin-Sync on HTTP/3.
  • Using tool no. 4, Burp Turbo-Intruder (slightly modified) race-single-packet-attack.py (HTTP/2) gave us the following results: (-5, -4, -1).
    • Using the single-packet mechanism behind the scenes.
  • Using tool no. 5, Burp Intruder’s (HTTP/2) gave us the following results: (-6, -6, -6).
    • This one is used as a benchmark without using the single-packet mechanism.

We are aware that this setup cannot be taken as scientific results. Moreover, some of the tools (Burp Intruder and curl) are not explicitly designed to exploit race conditions, making these results “not fair.” (Yes, we were also surprised by the efficiency and results returned with curl).

However, we do suggest treating the Burp Intruder and curl results as a benchmark, stating “the best result without a specialized tool” We also think that our tool’s results (QuicDraw(H3)), which performed significantly better than the non-specialized tools tested, suggest it is very effective in triggering race conditions.

As seen above, when using Turbo Intruder, 30 requests are sent in a single-packet. While using QuicDraw, 117 requests were sent within a single (Quic-Fin-Sync) packet. This, together with the fact that QuicDraw outperformed Turbo Intruder (by a factor of “x6”) suggests that when HTTP/3 is enabled, you should try using QuicDraw to fuzz and exploit race conditions.

QuicDraw usage and demos

In the following section, we mention how to use QuicDraw for racing (exploiting race conditions) and fuzzing (multiple different URLs or POST data payloads) HTTP/3 servers. For complete documentation, consult the QuicDraw(H3) repository.

Racing HTTP/3 applications

To use the same request multiple times (using the Quic-Fin-Sync), use the -tr/–total-requests argument.

quicdraw -tr TOTAL_REQUESTS
# example
quicdraw https://www.cyberark.com/ -tr 7

Demo: QuicDraw racing demo

Fuzzing HTTP/3 applications

Fuzzing in QuicDraw is based on a simple concept, just like other web fuzzers (Ffuf, Wfuzz). Go over the data section (-d) and replace any reference to the FUZZ keyword with the value given in the wordlist (-w) as the payload.

quicdraw -w WORDLIST -d DATA_with_FUZZ_keyword
quicdraw <https://http3_server.com/path> -w path/to/wordlist -d ‘{“jsonkey”:”FUZZ”}’

Known limitations

The MAX_STREAMS parameter is a limit set by the QUIC implementations (client and server). It can limit our or other “racing” implementations.

Summary and actionable insights for Fuzzing and racing HTTP/3 with QuicDraw

HTTP/3 has seen rapid internet-wide adoption, and all major web browsers support it. It improves connection setup, speed, security, and privacy compared to HTTP/1/2.

HTTP/3 support is (usually) advertised only after establishing a “lower” HTTP connection (HTTP/2 or HTTP/1), via the Alt-Svc response header from the server.

Head-of-line blocking elimination brings potential improvements to (HTTP/3) servers’ performance but does not provide immunity for a last-byte-sync-like attacks.

Organizations deploying web applications should (among other security testing) evaluate whether race conditions are avoided (using transactions when relevant or other means) within their applications.

By using QuicDraw, our open-source tool, you can fuzz and “race” web servers supporting HTTP/3. In our test case, we were able to send more than 110 streams in one packet, and our tool was able to exploit a race condition more than 40 times

Further research and contributions

Since HTTP/3 and QUIC are relatively new protocols — and there are many “independent” implementations — we suspect that more research that is needed in this area.

In theory, any protocol supporting multiplexing could be vulnerable to last-byte-sync-like attacks. We think this area should be further researched.

We welcome researchers, developers, and engineers to contribute to QuicDraw or peruse other QUIC and HTTP/3 research.

Maor Abutbul is a security researcher at CyberArk Labs.

Previous Article
Why access management needs a challenger mindset
Why access management needs a challenger mindset

Cybersecurity never stands still. Every login, session, and connection shifts the balance between freedom a...

Next Article
How AI is reshaping identity governance for CISOs and CIOs
How AI is reshaping identity governance for CISOs and CIOs

2025 has been a defining year for identity security, marked by a rapid increase in the volume, variety, and...