Bug #8410
closedhttp2: 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
OT Updated by OISF Ticketbot 28 days ago
- Subtask #8411 added
OT Updated by OISF Ticketbot 28 days ago
- Label deleted (
Needs backport to 8.0)
PA Updated by Philippe Antoine 28 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
OT Updated by OISF Ticketbot 28 days ago
- Subtask #8412 added
OT Updated by OISF Ticketbot 28 days ago
- Label deleted (
Needs backport to 7.0)
PA Updated by Philippe Antoine 28 days ago
- Status changed from In Progress to In Review
JI Updated by Jason Ish 28 days ago
- File 스크린샷 2026-03-24 오후 9.51.45.png 스크린샷 2026-03-24 오후 9.51.45.png added
- Description updated (diff)
VJ Updated by Victor Julien 27 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.
PA Updated by Philippe Antoine 24 days ago
PA Updated by Philippe Antoine 23 days ago
- Status changed from In Review to Resolved
PA Updated by Philippe Antoine 22 days ago
- Status changed from Resolved to Closed