Skip to content

QuicDraw (H3)

HTTP/3 Request Editor

    -----------
    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.

TOC

 Main Features

  • Implements the Quic-Fin-Sync on HTTP3 (over QUIC), for race-condition testing.
  • Supports fuzzing multiple requests with the FUZZ and wordlist (-w argument) mechanisms.
  • Custom HTTP headers functionality (-H argument).
    • Note: Custom headers are converted to lowercase since we have seen issues with some server implementations.
  • Supports SSLKEYLOGFILE (-l argument) 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.

Quick Start

Prerequisite:

  • python >=3.9
  • pip3

Install using pip

The easiest way to install QuicDraw is to run:

pip install quicdraw

Running (after pip install)

quicdraw -h

Install quicdraw-ui using pip (PyPi)

The easiest way to install QuicDraw-UI is to run:

pip install quicdraw[ui]
quicdraw-ui -h

Build and install locally by cloning the source (optional)

Build 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)

Usage

Print Help

quicdraw -h

Normal HTTP/3 (over QUIC) Requests

An HTTP/3 GET Request

quicdraw <https://http3_server.com/path>

An HTTP/3 POST Request

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"}'

Observability

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

Include response headers in the output -i

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> -i

Without -i, only the response status line and body are surfaced; the individual headers are still available at debug level via -v.

Verbose logging -v / -vv

Verbosity is cumulative — repeat the flag to increase detail:

  • -v sets 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 -d argument supplied), the request URL (:path) is logged (printed).
  • -vv additionally 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 internals

Note: -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).

Write the response body to a file or stdout -o OUTPUT

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.json

Stream the response body to stdout and pipe it to another tool:

quicdraw <https://http3_server.com/path> -o - | grep -i "token"

Notes:

  • -o writes the response body only. Combine it with -i/--include to prepend the response headers (curl-style).
  • When multiple requests/streams are sent (e.g. with -tr or -w), all response bodies are concatenated into the single output target.
  • -o/--output writes a single response target (file or stdout), whereas --output-dir writes each downloaded stream to its own file in a directory. They are independent options.

Log TLS Secrets to file -l SECRETS_LOG

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.log

To 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.

Test Race-Conditions in HTTP/3 applications

-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.

Racing example

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 12

Repeat 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 12

Repeat 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 -v

Fuzzing HTTP3 applications -w WORDLIST

Fuzzing 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.

Fuzzing Example (POST data)

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"}'

Path Fuzzing Example (GET)

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/wordlist

For 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

    -----------
    QuicDraw-UI: HTTP/3 Request Editor - A GUI for QuicDraw(H3): HTTP/3 Fuzzing and Racing (Client)
    -----------
               _         _
              (_)       | |                            __  ______
    __ _ _   _ _  ___ __| |_ __ __ ___      __        / / / /  _/
   / _` | | | | |/ __/ _` | '__/ _` \ \ /\ / / _____ / / / // /
  | (_| | |_| | | (_| (_| | | | (_| |\ V  V / /____// /_/ // /
   \__, |\__,_|_|\___\__,_|_|  \__,_| \_/\_/        \____/___/
      |_|    _______
         \  |QFS____| -------------------- HTTP/3
          \ |_//
            |_|

Install quicdraw-ui (using pip PyPi)

The easiest way to install QuicDraw-UI is to run:

pip install quicdraw[ui]
quicdraw-ui -h

Example 1: Simple HTTP/3 Request

Send a basic request to an HTTP/3 server:

quicdraw-ui https://www.cyberark.com

HTTP/3 Request Editor

HTTP/3 Request Editor

Advanced Tab

Advanced Tab

The 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:

Results Tab


Example 2: Fuzzing with a Wordlist

To fuzz an HTTP/3 endpoint, you need:

  1. A wordlist file (-w) - contains payloads to test (one per line)
  2. The FUZZ keyword in your data - gets replaced by each wordlist entry
quicdraw-ui https://www.cyberark.com -w path/to/wordlist -d '{"example_key":"FUZZ"}'

Fuzzing Example

The FUZZ keyword in {"example_key":"FUZZ"} will be replaced with each line from your wordlist file.


Command-Line Options

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.


Testing

QuicDraw includes a comprehensive pytest-based test suite covering unit, integration, and end-to-end scenarios.

Running Tests

Running Tests

Install Test Dependencies

# 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

# 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 Specific Test Categories

# 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 Specific Test Files or Functions

# 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" -v

Test Structure

tests/
├── 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)

Contributing

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

Limitations

  • The Quic-Fin-Sync is mostly effective in POST requests (using the -d argument).
    • 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 (FUZZ and --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 the FUZZ keyword in the URL (:path) will result in sending the URL (:path) as-is (including the FUZZ keyword).
  • 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)

Known issues

  • None

License

Copyright (c) 2025 CyberArk Software Ltd. All rights reserved This repository is licensed under the Apache-2.0 License - see LICENSE for more details.

Contact

Feel free to contact us via GitHub issues if you have any feature requests or project issues.

Contact Via LinkedIn

Maor Abutbul

About

QuicDraw(H3) 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.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages