-----------
QuicDraw(H3): HTTP/3 Fuzzing and Racing (Client)
-----------
_ _
(_) | | ______
__ _ _ _ _ ___ __| |_ __ __ ___ __ /\ /\___ /
/ _` | | | | |/ __/ _` | '__/ _` \ \ /\ / / / /_/ / |_ \
| (_| | |_| | | (_| (_| | | | (_| |\ V V / / __ / ___) |
\__, |\__,_|_|\___\__,_|_| \__,_| \_/\_/ \/ /_/ |____/
|_| _______
\ |QFS____| -------------------- HTTP/3
\ |_//
|_|
GitHub: https://github.com/cyberark/QuicDrawH3
License: Apache-2.0 License
Author: Maor Abutbul <CyberArk Labs>
-----------QuicDraw is a security research tool designed for fuzzing and racing
HTTP/3 servers. QuicDraw implements the Quic-Fin-Sync on HTTP/3 (over
QUIC), for race-condition testing.
The tool was originally published as part of CyberArk Labs' research: "Racing and Fuzzing HTTP/3: Open-sourcing QuicDraw(H3)"
The research, has been presented at: BlackAlps, Nullcon (Goa), and BlueHatIL26.
- QuicDraw
- QuicDraw-UI
- Contributing
- Limitations
- Known issues
- License
- Contact
- Implements the
Quic-Fin-Syncon HTTP3 (over QUIC), for race-condition testing. - Supports fuzzing multiple requests with the
FUZZand wordlist (-wargument) mechanisms. - Custom HTTP headers functionality (
-Hargument).- Note: Custom headers are converted to lowercase since we have seen issues with some server implementations.
- Supports SSLKEYLOGFILE (
-largument) for TLS decryption/inspection via packet analyzers such as Wireshark. - Based on aioquic (http3_client)
- aioquic is a library for the QUIC network protocol in Python.
- It features a minimal TLS 1.3 implementation, a QUIC stack, and an HTTP/3 stack.
Prerequisite:
- python >=3.9
- pip3
The easiest way to install QuicDraw is to run:
pip install quicdrawquicdraw -hThe easiest way to install QuicDraw-UI is to run:
pip install quicdraw[ui]
quicdraw-ui -hBuild from source
If there are no wheels for your system or if you wish to build QuicDraw from source.
Clone the repository:
(You may prefer to do this within a Virtual Environment)
git clone https://github.com/cyberark/quicdrawh3.git
python3 -m build
pip install .\dist\quicdraw-<VERSION>.whl
pip install .\dist\quicdraw-<VERSION>.whl[ui] # with the GUI (dependencies)quicdraw -hquicdraw <https://http3_server.com/path>HTTP POST requests are determined by using the -d argument followed by
the HTTP POST data to be sent.
quicdraw <https://http3_server.com/path> -d '{"key":"value"}'QuicDraw offers several ways to inspect requests and responses, from quick console output to files consumable by other tools:
| Flag | Purpose |
|---|---|
-i, --include |
Prepend the HTTP response headers to the output (curl-style) |
-v / -vv |
Increase logging verbosity (-v debug, -vv also QUIC) |
-o, --output |
Write the raw response body to a file or stdout |
-l, --secrets-log |
Log TLS secrets to a file, for use with Wireshark |
Use the -i/--include argument to print the HTTP response headers
alongside the response, mirroring curl -i. Each received response
header is printed to the console, and when combined with -o/--output
the headers are also prepended to the written body.
quicdraw <https://http3_server.com/path> -iWithout -i, only the response status line and body are surfaced; the
individual headers are still available at debug level via -v.
Verbosity is cumulative — repeat the flag to increase detail:
-vsets the QuicDraw logger to DEBUG. It logs (prints) the request data to be sent, the full response headers, and the response content, along with the tool version. In the case of GET requests (no-dargument supplied), the request URL (:path) is logged (printed).-vvadditionally raises the underlying QUIC logger to DEBUG, so you also see low-level QUIC/HTTP-3 protocol events.
quicdraw <https://http3_server.com/path> -v # debug logging
quicdraw <https://http3_server.com/path> -vv # debug + QUIC internalsNote: -v is intended for interactive inspection — it interleaves the
response content with log lines. For scripting or piping the response
body, prefer -o/--output (below).
Use the -o/--output argument to write the raw HTTP response body to a
file, mirroring curl's output behavior. Pass a filename to save the body,
or - to stream it to standard output (so it can be piped to other tools).
Unlike -v, which prints the response content interleaved with log lines,
-o writes only the response body, making it suitable for scripting and
piping.
Save the response body to a file:
quicdraw <https://http3_server.com/path> -o response.jsonStream the response body to stdout and pipe it to another tool:
quicdraw <https://http3_server.com/path> -o - | grep -i "token"Notes:
-owrites the response body only. Combine it with-i/--includeto prepend the response headers (curl-style).- When multiple requests/streams are sent (e.g. with
-tror-w), all response bodies are concatenated into the single output target. -o/--outputwrites a single response target (file or stdout), whereas--output-dirwrites each downloaded stream to its own file in a directory. They are independent options.
Use the -l/--secrets-log argument to log the TLS secrets to a file
(SSLKEYLOGFILE), for use with a packet analyzer such as Wireshark.
quicdraw <https://http3_server.com/path> -l /path/to/secrets.logTo inspect the traffic in Wireshark: Open Wireshark → Edit → Preferences
→ Protocols → TLS and set "(Pre)-Master-Secret log filename" to the full
path of secrets.log. Wireshark can then decrypt the captured QUIC/TLS
traffic.
-tr TOTAL_REQUESTS
To use the same request multiple times (using the Quic-Fin-Sync /
single-packet), use the -tr/--total-requests argument.
Note: If a WORDLIST (-w) argument is specified, this argument
(-tr TOTAL_REQUESTS) is overridden by the wordlist number of lines.
Repeat the same request 12 times (-tr 12) (using Quic-Fin-Sync)
quicdraw <https://http3_server.com/path> -d '{"key":"value"}' -H 'Authorization: bearer eyJ...' -tr 12Repeat the same request 12 times (-tr 12), use Quic-Fin-Sync and log (-l) TLS secrets
quicdraw <https://http3_server.com/path> -d '{"key":"value"}' -H 'Authorization: bearer eyJ...' -H 'content-type: application/json' -l /m2a/ssl_key_log_file.log -tr 12Repeat the same request 12 times (-tr 12), use Quic-Fin-Sync, log (-l) TLS secrets, and print verbose (-v) output including HTTP response content
quicdraw <https://http3_server.com/path> -d '{"key": "value"}' -H 'Authorization: bearer eyJ...' -H 'content-type: application/json' -l /m2a/ssl_key_log_file.log -tr 12 -vFuzzing in QuicDraw is based on a simple concept, 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.
To define fuzzing, use the wordlist (-w/--wordlist) argument with
the FUZZ keyword either in the DATA (-d) section (POST-data fuzzing)
or in the request URL path (path fuzzing, when no -d is supplied).
Note: If the payload (-d) does not include the FUZZ keyword, the
same data will be sent according to the number of lines in the
wordlist file.
Use Quic-Fin-Sync, go over the data section (-d), and replace any reference to the FUZZ keyword with the value given in the wordlist file (-w) as the payload
quicdraw <https://http3_server.com/path> -w path/to/wordlist -d '{"example_key":"FUZZ"}'When no -d (POST data) argument is supplied, the FUZZ keyword is
substituted in the request URL path instead. For each word in the
wordlist (-w), a GET request is sent with FUZZ replaced by that word
— useful for endpoint/directory discovery.
quicdraw <https://http3_server.com/FUZZ> -w path/to/wordlistFor example, with a wordlist containing admin, login, and api, the
tool requests /admin, /login, and /api respectively.
Note: Per the Limitations, fuzzing is applied once.
If -d is supplied, QuicDraw assumes POST-data fuzzing and sends the
URL path as-is (any FUZZ in the path is left untouched).
-----------
QuicDraw-UI: HTTP/3 Request Editor - A GUI for QuicDraw(H3): HTTP/3 Fuzzing and Racing (Client)
-----------
_ _
(_) | | __ ______
__ _ _ _ _ ___ __| |_ __ __ ___ __ / / / / _/
/ _` | | | | |/ __/ _` | '__/ _` \ \ /\ / / _____ / / / // /
| (_| | |_| | | (_| (_| | | | (_| |\ V V / /____// /_/ // /
\__, |\__,_|_|\___\__,_|_| \__,_| \_/\_/ \____/___/
|_| _______
\ |QFS____| -------------------- HTTP/3
\ |_//
|_|The easiest way to install QuicDraw-UI is to run:
pip install quicdraw[ui]
quicdraw-ui -hSend a basic request to an HTTP/3 server:
quicdraw-ui https://www.cyberark.comThe following options can be set by the advanced tab
Option Description
-l, --secrets-log TLS secrets file (for Wireshark)
-v, --verbose Verbose output
Results Tab:
To fuzz an HTTP/3 endpoint, you need:
- A wordlist file (
-w) - contains payloads to test (one per line) - The
FUZZkeyword in your data - gets replaced by each wordlist entry
quicdraw-ui https://www.cyberark.com -w path/to/wordlist -d '{"example_key":"FUZZ"}'The FUZZ keyword in {"example_key":"FUZZ"} will be replaced with
each line from your wordlist file.
QuicDraw-UI parameters are imported to the UI.
Option Description
-d, --data HTTP POST data (use FUZZ for wordlist
substitution)
-H, --header Custom header (repeatable)
-b, --cookie Custom cookie header
-w, --wordlist Fuzzing wordlist file
-tr, --total-requests Number of concurrent requests (race testing)
-l, --secrets-log TLS secrets file (for Wireshark)
-v, --verbose Verbose output
Note: "copy-as-curl compatible" meaning common curl arguments (-d,-H,-b) are supported by QuicDraw-UI.
QuicDraw includes a comprehensive pytest-based test suite covering unit, integration, and end-to-end scenarios.
Running Tests
# Install project with test dependencies
pip install -e ".[test]"
# Or install test dependencies separately
pip install pytest pytest-asyncio pytest-cov pytest-mock# Run all tests
pytest
# Run with verbose output
pytest -v
# Run tests with coverage report
pytest --cov=src/quicdraw --cov-report=html --cov-report=term-missing
# View coverage report (after running with --cov-report=html)
open htmlcov/index.html # macOS
xdg-open htmlcov/index.html # Linux# Run only unit tests (fast, no I/O)
pytest tests/unit/ -v
# Run only integration tests
pytest tests/integration/ -v
# Run only end-to-end tests
pytest tests/e2e/ -v
# Run tests with specific markers
pytest -m unit # Unit tests only
pytest -m integration # Integration tests only
pytest -m "not slow" # Skip slow tests# Run a specific test file
pytest tests/unit/test_validation.py -v
# Run a specific test function
pytest tests/unit/test_validation.py::test_non_negative_float_valid -v
# Run tests matching a pattern
pytest -k "validation" -vtests/
├── unit/ # Fast unit tests (no I/O dependencies)
├── integration/ # Integration tests with mocked I/O
├── e2e/ # End-to-end workflow tests
└── ui/ # GUI component tests (optional)We welcome contributions of all kinds to this repository. For instructions on how to get started and descriptions of our development workflows, please see our contributing guide
- The
Quic-Fin-Syncis mostly effective in POST requests (using the-dargument).- GET requests will benefit from the mechanism, but according to our tests, only a few requests "fit" on a single QUIC packet.
- The fuzzing mechanism (
FUZZand--wordlist/-w) only works in POST messages data or in the GET request URL (:path) argument. - Currently, the fuzzing mechanism only works once, meaning if the
data argument is supplied (
-d), we assume fuzzing on the POST data, supplying theFUZZkeyword in the URL (:path) will result in sending the URL (:path) as-is (including theFUZZkeyword). - We do not support multiple different domains in the current version. (For different paths, you can use the FUZZ keyword in the URL's path part)
- None
Copyright (c) 2025 CyberArk Software Ltd. All rights reserved This
repository is licensed under the Apache-2.0 License - see
LICENSE for more details.
Feel free to contact us via GitHub issues if you have any feature requests or project issues.




