Classroom Glossary Public page

Lab 11: QUIC/HTTP3 NSM Coverage Gap Analysis

702 words

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

  1. Generate HTTP/3 traffic using curl --http3 to a QUIC-capable server.
  2. Identify QUIC packet types (Initial/Handshake/1-RTT) in Wireshark.
  3. Confirm what Zeek ssl.log and Suricata alerts see vs. don't see.
  4. 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 = Initial
  • 0x02 = Handshake
  • 0x03 = 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_versions extension
  • 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:

  1. Part A confirmation: tshark count output; confirm HTTP/3 traffic present.
  2. Part B table: filled in for 4-5 QUIC frame types observed; SNI and TLS version from Initial ClientHello; ECH presence noted.
  3. Zeek output: paste conn.log entries for QUIC connections (service field); paste ssl.log if present; note what is missing vs. a TCP connection.
  4. Suricata output: paste fast.log from the two QUIC detection rules; note if alert quic syntax was available or if fallback was used.
  5. Coverage gap report (Part D): three sections (available, encrypted, compensating controls) with at least 5 items per section.
  6. 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