http2: bound number of http2 frames per tx

Ticket: 8289

If stream.reassembly.depth is unlimited,
an attacker controlling the 2 sides of a communication going through Suricata
can send a transition with an infinite number of headers, until suricata OOMs

Solution is to offer a configuration option to bound the number
of HTTP2 frames we store in a HTTP2 transaction, and produce an
anomaly if this bound is crossed

(cherry picked from commit 784e173278)
pull/15050/head
Philippe Antoine 2 months ago committed by Jason Ish
parent 632acbe74c
commit 2d5172aaf3

@ -24,3 +24,5 @@ alert http2 any any -> any any (msg:"SURICATA HTTP2 reassembly limit reached"; f
alert http2 any any -> any any (msg:"SURICATA HTTP2 dns request too long"; flow:established,to_server; app-layer-event:http2.dns_request_too_long; classtype:protocol-command-decode; sid:2290016; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 dns response too long"; flow:established,to_client; app-layer-event:http2.dns_response_too_long; classtype:protocol-command-decode; sid:2290017; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 data on stream zero"; flow:established; app-layer-event:http2.data_stream_zero; classtype:protocol-command-decode; sid:2290018; rev:1;)
# disabled by default, as it can happen in legit cases depending on the max-frames config value
# alert http2 any any -> any any (msg:"SURICATA HTTP2 too many frames"; flow:established; app-layer-event:http2.too_many_frames; classtype:protocol-command-decode; sid:2290019; rev:1;)

@ -77,6 +77,7 @@ pub static mut HTTP2_MAX_TABLESIZE: u32 = 65536; // 0x10000
// maximum size of reassembly for header + continuation
static mut HTTP2_MAX_REASS: usize = 102400;
static mut HTTP2_MAX_STREAMS: usize = 4096; // 0x1000
static mut HTTP2_MAX_FRAMES: usize = 65536;
#[derive(AppLayerFrameType)]
pub enum Http2FrameType {
@ -529,6 +530,7 @@ pub enum HTTP2Event {
DnsRequestTooLong,
DnsResponseTooLong,
DataStreamZero,
TooManyFrames,
}
pub struct HTTP2DynTable {
@ -1237,16 +1239,18 @@ impl HTTP2State {
let ftype = head.ftype;
let sid = head.stream_id;
let padded = head.flags & parser::HTTP2_FLAG_HEADER_PADDED != 0;
if dir == Direction::ToServer {
tx.frames_ts.push(HTTP2Frame {
header: head,
data: txdata,
});
let h2frames = if dir == Direction::ToServer {
&mut tx.frames_ts
} else {
tx.frames_tc.push(HTTP2Frame {
&mut tx.frames_tc
};
if h2frames.len() < unsafe { HTTP2_MAX_FRAMES } {
h2frames.push(HTTP2Frame {
header: head,
data: txdata,
});
} else {
tx.tx_data.set_event(HTTP2Event::TooManyFrames as u8);
}
if ftype == parser::HTTP2FrameType::Data as u8 && sid == 0 {
tx.tx_data.set_event(HTTP2Event::DataStreamZero as u8);
@ -1587,6 +1591,13 @@ pub unsafe extern "C" fn SCRegisterHttp2Parser() {
SCLogError!("Invalid value for http2.max-streams");
}
}
if let Some(val) = conf_get("app-layer.protocols.http2.max-frames") {
if let Ok(v) = val.parse::<usize>() {
HTTP2_MAX_FRAMES = v;
} else {
SCLogError!("Invalid value for http2.max-frames");
}
}
if let Some(val) = conf_get("app-layer.protocols.http2.max-table-size") {
if let Ok(v) = val.parse::<u32>() {
HTTP2_MAX_TABLESIZE = v;

@ -1013,6 +1013,8 @@ app-layer:
#max-table-size: 65536
# Maximum reassembly size for header + continuation frames
#max-reassembly-size: 102400
# Maximum number of frames per tx
#max-frames: 65536
smtp:
enabled: yes
raw-extraction: no

Loading…
Cancel
Save