Project

General

Profile

Bug #8581

Updated by Jason Ish 5 days ago

Reported via Freshdesk: https://suricata.freshdesk.com/a/tickets/130 

 Freshdesk subject: IDS Evasion via HTTP/2 HPACK Varint Overflow in http2_parse_var_uint 
 Freshdesk created: 2026-05-19T14:08:43Z 
 Reporter-stated severity: Medium (IDS evasion) 

 The report says Suricata's HTTP/2 HPACK integer parser returns 0 instead of an error when an HPACK-encoded integer overflows u64, allowing headers to be decoded differently from the endpoint and potentially evading detection. 

 Original Freshdesk report: 

 <pre> 
 Summary 

 The function http2_parse_var_uint in rust/src/http2/parser.rs:459-481 silently 
 returns value=0 when an HPACK-encoded integer overflows u64 (encoded in ≥10 
 continuation bytes). Instead of returning an error, the parser returns 
 Ok((remaining_input, 
 0)), which propagates through the HTTP/2 header decoding pipeline and 
 causes Suricata to interpret headers differently from the actual HTTP/2 
 server. 

 This allows an attacker to craft HTTP/2 HEADERS frames that evade Suricata 
 detection rules by making the IDS see empty or incorrect header values 
 while the destination server processes the intended values. 
 Severity 

 Medium — IDS evasion. No crash or memory corruption, but allows malicious 
 HTTP/2 traffic to bypass detection rules. 
 Vulnerable Code 

 rust/src/http2/parser.rs:459-481: 

 rust 

 fn http2_parse_var_uint(input: &[u8], value: u64, max: u64) -> 
 IResult<&[u8], u64> { 
     // ... 
     if varia.len() > 9 || (varia.len() == 9 && finalv > 1) { 
         // this will overflow u64 
         return Ok((i3, 0));    // ← silently returns 0 instead of error 
     } 
     // ...} 

 Affected Downstream Functions 

 The value=0 propagates to: 

    1. *http2_parse_headers_block_indexed* (line 318): index 0 triggers 
    HTTP2HeaderDecodeIndex0 — the header is silently ignored by Suricata 
    while the server processes it normally. 
    2. *http2_parse_headers_block_string* (line 330): stringlen=0 results in 
    an empty string — Suricata sees an empty header value while the server sees 
    the real value. 
    3. *Literal headers with incremental indexing* (lines 384, 439, 454): 
    value 0 is interpreted as "new header entry" instead of the actual index, 
    causing incorrect HPACK dynamic table state divergence between Suricata and 
    the server. 

 Steps to Reproduce 

    1. Set up Suricata monitoring HTTP/2 traffic with a detection rule 
    matching a specific URI or Host header 
    2. From a client, send an HTTP/2 HEADERS frame containing an 
    HPACK-encoded header where the integer value uses ≥10 continuation bytes 
    (forcing the overflow path) 
    3. The destination server (which implements HPACK per RFC 7541) will 
    either: 
       - Decode the value correctly (if within its integer size), or 
       - Return a COMPRESSION_ERROR (per spec) 
    4. Suricata will silently decode the value as 0 and process the header 
    as empty/indexed-at-0 

 Example: encode a header index that, when properly decoded, points to :path: 
 /malicious but when decoded as 0 by Suricata, is ignored entirely. 
 Suricata's rule for /malicious does not fire. 
 Impact 

    1. *Detection bypass*: An attacker can hide malicious HTTP/2 request 
    headers (URI, Host, User-Agent, Cookie) from Suricata rules by encoding 
    them with overflowing HPACK integers. The destination server processes the 
    real headers while Suricata sees empty values. 
    2. *HPACK dynamic table desynchronization*: When the overflow occurs in 
    a literal-with-incremental-indexing header, Suricata's HPACK dynamic table 
    state diverges from the server's. All subsequent headers in the connection 
    may be decoded incorrectly by Suricata, amplifying the evasion window. 
    3. *Silent failure*: There is no log entry, alert, or anomaly event when 
    the overflow occurs. The parser returns success with corrupted data. 

 Suggested Fix 

 Return an error instead of silently accepting the overflow: 

 rust 

 if varia.len() > 9 || (varia.len() == 9 && finalv > 1) { 
     // this will overflow u64 
     return Err(nom::Err::Error(nom::error::make_error(input, 
 nom::error::ErrorKind::LengthValue)));} 

 This matches the error handling pattern already used in the checked_add branch 
 at lines 474-476 of the same function. 
 Environment 

    - Suricata main branch @ commit 367ca7f (post v8.0.1, May 15, 2026) 
    - Affected file: rust/src/http2/parser.rs 
    - Feature: HTTP/2 HPACK header decoding (enabled by default) 
 </pre> 

Back