Module: 9 — QUIC, HTTP/3, and Encrypted Transport Security
Points: 20
Time estimate: 90 min lab + 2 hr independent
Deliverable: lab-11-report.md + lab11/ directory
Objectives
- Generate HTTP/3 traffic using
curl --http3to a QUIC-capable server. - Identify QUIC packet types (Initial/Handshake/1-RTT) in Wireshark.
- Confirm what Zeek
ssl.logand Suricata alerts see vs. don't see. - Write a coverage-gap report: available metadata, encrypted metadata, lost detection capability.
Setup
pip install requests
sudo apt-get install -y curl wireshark suricata zeek
# Verify curl supports HTTP/3
curl --version | grep -i quic
# Expected: Features: ... HTTP3 ... (or similar indication of QUIC support)
# If curl lacks HTTP3: install a curl build with nghttp3/ngtcp2 support
# Alternative: use cloudflared or quiche-client
mkdir lab11 && cd lab11
Part A: Generate HTTP/3 Traffic (20 min)
Generate HTTP/3 traffic to QUIC-capable endpoints while capturing with tcpdump:
# Start capture before generating traffic
sudo tcpdump -i any udp port 443 -w lab11/quic_capture.pcap &
TCPDUMP_PID=$!
# Generate HTTP/3 requests
# curl's --http3 flag requests HTTP/3 (falls back to HTTP/2 if QUIC unavailable)
curl --http3 -v https://cloudflare.com/cdn-cgi/trace -o /dev/null 2>&1 | tee lab11/curl_cloudflare.log
curl --http3 -v https://www.google.com/ -o /dev/null 2>&1 | tee lab11/curl_google.log
# Also generate comparison HTTP/1.1 + TLS traffic on TCP
curl --http1.1 -v https://www.google.com/ -o /dev/null 2>&1 | tee lab11/curl_http1.log
sleep 2 && sudo kill $TCPDUMP_PID
If curl HTTP/3 is not available on your system, use the following alternative to generate QUIC traffic via a test server:
# Alternative: use Python quic-client or install quiche
pip install aioquic
python3 -c "
import asyncio
from aioquic.asyncio.client import connect
from aioquic.h3.connection import H3_ALPN
async def run():
async with connect('cloudflare.com', 443,
alpn_protocols=H3_ALPN,
verify_peer=False) as quic:
print('QUIC connection established')
asyncio.run(run())
" 2>&1 | tee lab11/quic_python.log
Verify the capture has UDP/443 traffic:
tshark -r lab11/quic_capture.pcap -Y "udp.port==443" | wc -l
# Expected: > 0 packets
Part B: Wireshark QUIC Packet Type Identification (25 min)
Open lab11/quic_capture.pcap in Wireshark. Apply filter: quic
Task 1: Identify packet types.
QUIC Long Header packets carry a Packet Type field:
0x00= Initial0x02= Handshake0x03= 0-RTT
Short Header packets (1-RTT application data) do not contain a type byte.
For each QUIC connection in the capture, record:
| Frame # | Packet Type | Direction | What Wireshark shows in the QUIC dissector |
|---|---|---|---|
| (fill in) | Initial | Client → Server | CRYPTO frame with TLS ClientHello |
| (fill in) | Initial | Server → Client | CRYPTO frame with TLS ServerHello |
| (fill in) | Handshake | Server → Client | CRYPTO: Certificate + Finished |
| (fill in) | Handshake | Client → Server | CRYPTO: Finished |
| (fill in) | 1-RTT | Client → Server | Protected application data (cannot read payload) |
Task 2: Examine the Initial CRYPTO frame.
Click on the first Client Initial packet. Expand:
QUIC IETF → Payload → CRYPTO → TLS Handshake → Client Hello
Record:
- The SNI (Server Name Indication) visible in the ClientHello
- The TLS version in the
supported_versionsextension - Whether
encrypted_client_hello(ECH) extension is present
Part C: Zeek and Suricata Visibility Analysis (30 min)
Zeek Analysis
Run Zeek against the QUIC capture:
zeek -r lab11/quic_capture.pcap
ls -la
# Check: conn.log, ssl.log, dpd.log
Query the logs:
# What service does Zeek identify for UDP/443 connections?
zeek-cut id.orig_h id.orig_p id.resp_h id.resp_p service proto < conn.log
# What does ssl.log show for QUIC connections?
# (Zeek may or may not populate ssl.log for QUIC depending on version)
cat ssl.log 2>/dev/null || echo "No ssl.log generated"
# What does dpd.log show?
cat dpd.log 2>/dev/null || echo "No dpd.log generated"
Compare against the HTTP/1.1 capture: If you generated a separate TCP/443 capture (using the curl --http1.1 command), run Zeek against it and note what fields appear in ssl.log that do not appear for QUIC.
Suricata Analysis
Run Suricata against the QUIC capture:
suricata -r lab11/quic_capture.pcap -l /tmp/lab11-suricata/ --no-random-seed
# Check what fired
cat /tmp/lab11-suricata/fast.log
cat /tmp/lab11-suricata/eve.json | python3 -m json.tool | grep '"event_type"' | sort | uniq -c
Write and test the Suricata QUIC SNI detection rule from Week 9:
cat > lab11/quic_detect.rules << 'EOF'
# Detect QUIC connections (any SNI)
alert quic any any -> any any (
msg:"NET-301 QUIC Connection Observed";
classtype:misc-activity;
sid:9000010; rev:1;
)
# Detect QUIC connections to specific destination (customize with actual test SNI)
alert quic any any -> any any (
msg:"NET-301 QUIC to Cloudflare - coverage gap example";
quic.sni; content:"cloudflare.com";
classtype:misc-activity;
sid:9000011; rev:1;
)
EOF
suricata -r lab11/quic_capture.pcap -S lab11/quic_detect.rules \
-l /tmp/lab11-suricata-rules/ --no-random-seed
cat /tmp/lab11-suricata-rules/fast.log
Note: alert quic syntax requires Suricata 7.0+. On older Suricata, use:
alert udp any any -> any 443 (msg:"QUIC UDP/443"; sid:9000010;)
Part D: Coverage Gap Report (30 min)
Write the coverage gap analysis in lab-11-report.md (Part D section). Use the NSM visibility matrix from Week 9 as your framework.
Structure:
Available metadata (what NSM can see for QUIC traffic): List each field and its source:
- Example: "Connection duration, bytes transferred, packet counts -- available via Zeek
conn.log"
Encrypted metadata (what existed in TCP+TLS but is hidden in QUIC): List each field and why it is hidden:
- Example: "HTTP method and URL path -- HTTP/3 application data is inside 1-RTT QUIC packets; the TLS 1.3 session key is ephemeral and not available to a passive NSM sensor"
Detection capability lost vs. HTTP/1.1+TLS: For each item in the encrypted column, state what specific detection rule or Zeek analytic relied on that field and how it is affected.
Compensating controls: For each detection gap, propose one compensating control (endpoint telemetry, DNS monitoring, byte-count analysis, or other).
Lab Report
Create lab-11-report.md with:
- Part A confirmation: tshark count output; confirm HTTP/3 traffic present.
- Part B table: filled in for 4-5 QUIC frame types observed; SNI and TLS version from Initial ClientHello; ECH presence noted.
- Zeek output: paste
conn.logentries for QUIC connections (service field); pastessl.logif present; note what is missing vs. a TCP connection. - Suricata output: paste
fast.logfrom the two QUIC detection rules; note ifalert quicsyntax was available or if fallback was used. - Coverage gap report (Part D): three sections (available, encrypted, compensating controls) with at least 5 items per section.
- Design question: Your organization's security team wants to monitor all QUIC traffic for exfiltration. They propose blocking UDP/443 at the perimeter. Write a two-paragraph response: what detection capability does this recover, and what is the operational cost?
Grading
| Component | Points |
|---|---|
| Part A: QUIC traffic captured; UDP/443 packets confirmed | 3 |
| Part B: packet type table; Initial CRYPTO frame analyzed | 4 |
| Part C: Zeek output compared to HTTP/1.1; Suricata rule fired or failure explained | 5 |
| Part D: coverage gap report -- available/encrypted/compensating all present | 5 |
| Design question: balanced analysis of UDP/443 block | 3 |
| Total | 20 |