diff --git a/rules/http2-events.rules b/rules/http2-events.rules index d6ada2c0a6..4ff172ee9b 100644 --- a/rules/http2-events.rules +++ b/rules/http2-events.rules @@ -27,3 +27,4 @@ alert http2 any any -> any any (msg:"SURICATA HTTP2 data on stream zero"; flow:e # 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;) alert http2 any any -> any any (msg:"SURICATA HTTP2 compression bomb"; flow:established; app-layer-event:http2.compression_bomb; classtype:protocol-command-decode; sid:2290020; rev:1;) +alert http2 any any -> any any (msg:"SURICATA HTTP2 headers table overflow"; flow:established; app-layer-event:http2.header_table_overflow; classtype:protocol-command-decode; sid:2290021; rev:1;) diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs index 72520eab0a..ecd7ea647d 100644 --- a/rust/src/http2/http2.rs +++ b/rust/src/http2/http2.rs @@ -677,6 +677,7 @@ pub enum HTTP2Event { FailedDecompression, InvalidRange, HeaderIntegerOverflow, + HeaderTableOverflow, TooManyStreams, AuthorityHostMismatch, UserinfoInUri, @@ -981,29 +982,24 @@ impl HTTP2State { &mut self, blocks: &Vec, dir: Direction, ) -> Vec { let mut events = Vec::new(); - let (mut update, mut sizeup) = (false, 0); for block in blocks { if block.error >= parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeError { events.push(HTTP2Event::InvalidHeader); - } else if block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { - update = true; - if block.sizeupdate > sizeup { - sizeup = block.sizeupdate; - } } else if block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow { events.push(HTTP2Event::HeaderIntegerOverflow); } } - if update { - //borrow checker forbids to pass directly dyn_headers - let dyn_headers = if dir == Direction::ToClient { - &mut self.dynamic_headers_tc - } else { - &mut self.dynamic_headers_ts - }; - dyn_headers.max_size = sizeup as usize; + let dyn_headers = if dir == Direction::ToClient { + &mut self.dynamic_headers_tc + } else { + &mut self.dynamic_headers_ts + }; + if dyn_headers.overflow == 2 { + // event only once + dyn_headers.overflow = 3; + events.push(HTTP2Event::HeaderTableOverflow); } return events; } diff --git a/rust/src/http2/parser.rs b/rust/src/http2/parser.rs index 92670b6d47..e436bafaed 100644 --- a/rust/src/http2/parser.rs +++ b/rust/src/http2/parser.rs @@ -401,8 +401,8 @@ fn http2_parse_headers_block_literal_incindex<'a>( if dyn_headers.current_size <= (unsafe { HTTP2_MAX_TABLESIZE } as usize) { //overflow had not yet happened dyn_headers.table.push(headcopy); - } else if dyn_headers.current_size > dyn_headers.max_size { - //overflow happens, we cannot replace evicted headers + } else { + //overflow happens, we cannot record new headers dyn_headers.overflow = 2; } } @@ -506,7 +506,14 @@ fn http2_parse_headers_block_dynamic_size<'a>( toremove += 1; } dyn_headers.table.drain(0..toremove); + if maxsize2 <= unsafe { HTTP2_MAX_TABLESIZE.into() } { + dyn_headers.overflow = 0; + } + } else if maxsize2 > unsafe { HTTP2_MAX_TABLESIZE.into() } { + //mark potential overflow + dyn_headers.overflow = 1; } + dyn_headers.max_size = maxsize2 as usize; return Ok(( i3, HTTP2FrameHeaderBlock {