Project

General

Profile

Bug #8410

Updated by Jason Ish 6 days ago

request_frame_size is set whatever the direction 

 Setting request_frame_size when we should set response_frame_size leads to FN 

 Submitted report: 

 [Summary] 
 `HTTP2State::parse_frames()` is called for both ToServer and ToClient directions, but unconditionally writes remaining frame bytes to `self.request_frame_size` (line 1153 of `rust/src/http2/http2.rs`). When a large (>=65536 bytes) server-to-client DATA frame is partially delivered, the remaining byte count is stored in `request_frame_size` instead of `response_frame_size`. On the next client-to-server data delivery, `parse_ts()` consumes `request_frame_size` bytes and discards them, causing all subsequent client HTTP/2 requests to be silently dropped from parsing and inspection. 

 [Root Cause] 
 `HTTP2State` maintains two fields for tracking remaining bytes of partially-delivered large frames: 

 ```rust 
 // rust/src/http2/http2.rs:567-568 
 pub struct HTTP2State { 
     request_frame_size: u32,     // used by parse_ts() at line 1343 
     response_frame_size: u32,    // used by parse_tc() at line 1369 
     ... 
 } 
 ``` 

 `parse_ts()` (ToServer) checks and consumes `self.request_frame_size` at lines 1343-1352. `parse_tc()` (ToClient) checks and consumes `self.response_frame_size` at lines 1369-1378. Both call the shared `parse_frames()` method. 

 Inside `parse_frames()`, when a frame with `length >= HTTP2_MAX_HANDLED_FRAME_SIZE` (65536) arrives partially (the available data contains at least `HTTP2_MIN_HANDLED_FRAME_SIZE` (256) bytes but not the full frame), line 1153 executes: 

 ```rust 
 // rust/src/http2/http2.rs:1153 
 self.request_frame_size = head.length - (rem.len() as u32); 
 ``` 

 This always writes to `request_frame_size`, even when `parse_frames()` was called from `parse_tc()` with `dir == Direction::ToClient`. The correct behavior would be: 

 ```rust 
 if dir == Direction::ToServer { 
     self.request_frame_size = head.length - (rem.len() as u32); 
 } else { 
     self.response_frame_size = head.length - (rem.len() as u32); 
 } 
 ``` 


 [PoC] 
 Please extract the attached compressed file and proceed. 

 1. docker build -t poc . 
 2. docker run --rm poc 
 스크린샷 2026-03-24 오후 9.51.45.png 

 [Impact] 
 - All client requests following a large server response are invisible to Suricata's detection engine. HTTP-based signature rules (`http.uri`, `http.method`, `http.request_header`, etc.) fail to match. 
 - This is trivially exploitable by any server an attacker controls. HTTP/2 responses >= 64KB are extremely common in normal web traffic. 
 - HTTP/2 is enabled by default in Suricata (`app-layer.protocols.http2.enabled: yes`). 

 [Affected Version] 
 * Current `main` branch (commit bc4a055e7, Suricata 9.0.0-dev) is confirmed affected.

Back