JakeChampion / JakeChampion/trafficserver

[10.2][http3] QPACK static-table lookup has no bounds check — remote OOB read

Open
#64 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

10.2 area:http2-3 audit severity:critical verified
Dominant language
C++
Stars
0
Forks
0
Avg merge
8h 2m
Merged PRs (30d)
21

Description

Branch: 10.2.x (10.2.1, commit 31f1f2f3b) · Severity: critical · ✓ adversarially verified
Location: src/proxy/http3/QPACK.cc:1222 (and encoder-insert caller at :1156)

What's wrong

QPACK::StaticTable::lookup(uint16_t index, ...) dereferences STATIC_HEADER_FIELDS[index] with no range check. STATIC_HEADER_FIELDS has only 99 entries, but index is taken from the wire (a QPACK integer up to 0xFFFF) on every HTTP/3 header decode that sets the static (T) bit — _decode_indexed_header_field and _decode_literal_header_field_with_name_ref, plus the encoder-stream Insert With Name Reference handler. A crafted HEADERS field with a static index ≥ 99 reads past the array; the garbage Header returned supplies a wild name/value pointer and length that flow into _attach_headerstd::string_view{name, name_len}, giving an out-of-bounds read of attacker-influenced size (crash or header-memory disclosure).

Additionally the encoder-stream handler (:1156) calls StaticTable::lookup(index, ...) unconditionally, ignoring the is_static flag it just parsed, so dynamic-table name references also hit the unchecked static path.

Evidence
1220: QPACK::StaticTable::lookup(uint16_t index, const char **name, ...) {
1222:   const Header &header = STATIC_HEADER_FIELDS[index];   // no bound check
...
1156:   StaticTable::lookup(index, &name, &name_len, &dummy, &dummy_len);  // is_static ignored
Fix

Bounds-check index against countof(STATIC_HEADER_FIELDS) and return MatchType::NONE (the callers already reject non-EXACT); honor is_static on the encoder insert by resolving dynamic references against the dynamic table, aborting the decode on an invalid index. PR attached.

Verification

Independently re-traced on the 10.2.1 tree by a second reviewer instructed to refute it; confirmed. STATIC_HEADER_FIELDS[] has exactly 99 entries; lookup() always returns EXACT with no range check, so the if (result.match_type != EXACT) return -1 guards never reject an out-of-range index; the index originates from an xpack_decode_integer bounded only to 16 bits, directly attacker-reachable via a crafted HTTP/3 field section.


From an automated multi-lens audit of the 10.2.x branch. Full report on branch claude/codebase-audit-review-9nw7vz (CODEBASE_AUDIT_10.2.md).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/proxy/http3/QPACK.cc at StaticTable::lookup around line 1222, then trace the callers around lines 1156 and 1222. Verify invalid static indexes are rejected and that encoder-stream references honor the parsed static flag. Done means malformed HTTP/3 inputs no longer permit the unchecked static lookup or out-of-bounds header read.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend-api-design, networking, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.