Bug #8410
openhttp2: response_frame_size is never set
Description
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`).
- Current `main` branch (commit bc4a055e7, Suricata 9.0.0-dev) is confirmed affected.
Files
Updated by Philippe Antoine 6 days ago
- Status changed from Assigned to In Progress
- Label Needs backport to 7.0 added
Gitlab MR
I think this can be made public as a regular bug
This needs complicity between client and server to evade client detection, so not a security issue for me
Updated by Philippe Antoine 6 days ago
- Status changed from In Progress to In Review
Updated by Jason Ish 6 days ago
- File 스크린샷 2026-03-24 오후 9.51.45.png 스크린샷 2026-03-24 오후 9.51.45.png added
- Description updated (diff)
Updated by Victor Julien 5 days ago
- Private changed from Yes to No
We will consider this to be a regular bug as to use this to evade Suricata it would need the attacker to control both the server and client to cooperate on this.
Updated by Philippe Antoine 1 day ago
Updated by Philippe Antoine about 15 hours ago
- Status changed from In Review to Resolved