From 2c79e1566d639e4c98f6ee5af854d30681a0a8b6 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Sat, 7 Mar 2026 11:04:15 +0100 Subject: [PATCH 1/4] sip: make pattern matching more robust SIP and SSDP share method names like NOTIFY and SUBSCRIBE, causing SSDP traffic to be misidentified as SIP. Add a probing parser callback that checks for "SIP/" in the payload before accepting a pattern match. Example of a misidentified flow before the fix: {"timestamp":"2014-02-27T19:44:43.164211+0100","flow_id":986757542077835,"event_type":"flow","src_ip":"192.168.1.1","src_port":9489,"dest_ip":"239.255.255.250 ","dest_port":1900,"ip_v":4,"proto":"UDP","app_proto":"sip","flow":{"..."}} After the fix: {"timestamp":"2014-02-27T19:44:43.164211+0100","flow_id":986757542077835,"event_type":"flow","src_ip":"192.168.1.1","src_port":9489,"dest_ip":"239.255.255.250 ","dest_port":1900,"ip_v":4,"proto":"UDP","app_proto":"failed","flow":{"..."}} Ticket #8355 --- rust/src/sip/parser.rs | 32 ++++++++++++++++++++-- rust/src/sip/sip.rs | 60 +++++++++++++++++++++++++++++++----------- 2 files changed, 74 insertions(+), 18 deletions(-) diff --git a/rust/src/sip/parser.rs b/rust/src/sip/parser.rs index d6ed09f882ff..ae7a0b72c605 100644 --- a/rust/src/sip/parser.rs +++ b/rust/src/sip/parser.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2022 Open Information Security Foundation +/* Copyright (C) 2019-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -18,9 +18,10 @@ // written by Giuseppe Longo use crate::sdp::parser::{sdp_parse_message, SdpMessage}; -use nom8::bytes::streaming::{tag, take, take_while, take_while1}; +use nom8::bytes::streaming::{tag, take, take_until, take_while, take_while1}; use nom8::character::streaming::{char, crlf}; use nom8::combinator::{map, map_res, opt}; +use nom8::error::{Error, ErrorKind}; use nom8::sequence::delimited; use nom8::{AsChar, Err, IResult, Needed, Parser}; use std; @@ -112,6 +113,21 @@ fn expand_header_name(h: &str) -> &str { } } +pub fn sip_probe_protocol(input: &[u8]) -> IResult<&[u8], ()> { + let len = std::cmp::min(input.len(), 65536); + let i = &input[..len]; + + if tag::<_, _, Error<&[u8]>>("SIP/").parse(i).is_ok() { + return Ok((input, ())); + } + + if take_until::<_, _, Error<&[u8]>>("SIP/").parse(i).is_ok() { + Ok((input, ())) + } else { + Err(Err::Error(Error::new(i, ErrorKind::Tag))) + } +} + pub fn parse_request(oi: &[u8]) -> IResult<&[u8], Request> { let (i, method) = parse_method(oi)?; let (i, _) = char(' ').parse(i)?; @@ -364,6 +380,18 @@ mod tests { assert_eq!(result, "SIP/2.0"); } + #[test] + fn test_probe_sip_request() { + let buf = b"REGISTER sip:sip.example.com SIP/2.0\r\n"; + assert!(sip_probe_protocol(buf).is_ok()); + } + + #[test] + fn test_probe_sip_response() { + let buf = b"SIP/2.0 200 OK\r\n"; + assert!(sip_probe_protocol(buf).is_ok()); + } + #[test] fn test_header_multi_value() { let buf: &[u8] = "REGISTER sip:sip.cybercity.dk SIP/2.0\r\n\ diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs index bcec343d0fa7..eccdca90ae81 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2022 Open Information Security Foundation +/* Copyright (C) 2019-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -20,7 +20,8 @@ use crate::applayer::{self, *}; use crate::core; use crate::core::{ - sc_app_layer_parser_trigger_raw_stream_inspection, ALPROTO_UNKNOWN, IPPROTO_TCP, IPPROTO_UDP, + sc_app_layer_parser_trigger_raw_stream_inspection, ALPROTO_FAILED, ALPROTO_UNKNOWN, + IPPROTO_TCP, IPPROTO_UDP, }; use crate::direction::Direction; use crate::flow::Flow; @@ -34,6 +35,7 @@ use suricata_sys::sys::{ AppLayerParserState, AppProto, SCAppLayerParserConfParserEnabled, SCAppLayerParserRegisterLogger, SCAppLayerParserStateIssetFlag, SCAppLayerProtoDetectConfProtoDetectionEnabled, SCAppLayerProtoDetectPMRegisterPatternCS, + SCAppLayerProtoDetectPMRegisterPatternCSwPP, }; // app-layer-frame-documentation tag start: FrameType enum @@ -492,21 +494,41 @@ unsafe extern "C" fn sip_parse_response_tcp( state.parse_response_tcp(flow, stream_slice) } +unsafe extern "C" fn sip_probing_parser( + _f: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, +) -> AppProto { + if input.is_null() || input_len == 0 { + return ALPROTO_UNKNOWN; + } + let buf = std::slice::from_raw_parts(input, input_len as usize); + match sip_probe_protocol(buf) { + Ok(_) => ALPROTO_SIP, + Err(Err::Incomplete(_)) => ALPROTO_UNKNOWN, + Err(_) => ALPROTO_FAILED, + } +} + fn register_pattern_probe(proto: u8) -> i8 { + let methods_with_probe: Vec<&str> = vec![ + "ACK\0", + "INFO\0", + "NOTIFY\0", + "SUBSCRIBE\0", + "OPTIONS\0", + "UPDATE\0", + ]; + let methods: Vec<&str> = vec![ "REGISTER\0", "INVITE\0", - "ACK\0", "BYE\0", "CANCEL\0", "REFER\0", "PRACK\0", - "SUBSCRIBE\0", - "NOTIFY\0", "PUBLISH\0", "MESSAGE\0", - "INFO\0", ]; + let mut r = 0; unsafe { for method in methods { @@ -520,6 +542,22 @@ fn register_pattern_probe(proto: u8) -> i8 { Direction::ToServer as u8, ); } + + for method in methods_with_probe { + let depth = (method.len() - 1) as u16; + r |= SCAppLayerProtoDetectPMRegisterPatternCSwPP( + proto, + ALPROTO_SIP, + method.as_ptr() as *const std::os::raw::c_char, + depth, + 0, + Direction::ToServer as u8, + Some(sip_probing_parser), + 0, + 0, + ); + } + r |= SCAppLayerProtoDetectPMRegisterPatternCS( proto, ALPROTO_SIP, @@ -528,16 +566,6 @@ fn register_pattern_probe(proto: u8) -> i8 { 0, Direction::ToClient as u8, ); - if proto == core::IPPROTO_UDP { - r |= SCAppLayerProtoDetectPMRegisterPatternCS( - proto, - ALPROTO_SIP, - "UPDATE\0".as_ptr() as *const std::os::raw::c_char, - "UPDATE".len() as u16, - 0, - Direction::ToServer as u8, - ); - } } if r == 0 { From 39ed391d4a9c0c92297ed4e56cd1f58299df1b82 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Wed, 20 May 2026 14:04:57 +0200 Subject: [PATCH 2/4] sip: remove incomplete data event It's no longer useful to set an event when the data is incomplete, since an error is returned. Ticket #8524 --- rust/src/sip/sip.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/rust/src/sip/sip.rs b/rust/src/sip/sip.rs index eccdca90ae81..b9ae1fefb466 100755 --- a/rust/src/sip/sip.rs +++ b/rust/src/sip/sip.rs @@ -53,7 +53,6 @@ pub enum SIPFrameType { #[derive(AppLayerEvent)] pub enum SIPEvent { - IncompleteData, InvalidData, } @@ -148,10 +147,6 @@ impl SIPState { return AppLayerResult::ok(); } // app-layer-frame-documentation tag end: parse_request - Err(Err::Incomplete(_)) => { - self.set_event(SIPEvent::IncompleteData); - return AppLayerResult::err(); - } Err(_) => { self.set_event(SIPEvent::InvalidData); return AppLayerResult::err(); @@ -245,10 +240,6 @@ impl SIPState { self.transactions.push_back(tx); return AppLayerResult::ok(); } - Err(Err::Incomplete(_)) => { - self.set_event(SIPEvent::IncompleteData); - return AppLayerResult::err(); - } Err(_) => { self.set_event(SIPEvent::InvalidData); return AppLayerResult::err(); From 85b41b4eee1712bc1c033cfe399ba78fd1049676 Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Wed, 20 May 2026 14:09:59 +0200 Subject: [PATCH 3/4] rules: add sip-events.rules Ticket #8524 --- rules/Makefile.am | 1 + rules/README.md | 1 + rules/sip-events.rules | 5 +++++ 3 files changed, 7 insertions(+) create mode 100644 rules/sip-events.rules diff --git a/rules/Makefile.am b/rules/Makefile.am index 785b25b26999..73226efb3a91 100644 --- a/rules/Makefile.am +++ b/rules/Makefile.am @@ -25,6 +25,7 @@ pop3-events.rules \ quic-events.rules \ rfb-events.rules \ sctp-events.rules \ +sip-events.rules \ smb-events.rules \ smtp-events.rules \ snmp-events.rules \ diff --git a/rules/README.md b/rules/README.md index b4abaf6128d8..90a266e320b3 100644 --- a/rules/README.md +++ b/rules/README.md @@ -41,4 +41,5 @@ signature IDs. | Bittorent| 2243000 | 2243999 | | MODBUS | 2250000 | 2250999 | | DNP3 | 2270000 | 2270999 | +| SIP | 2280000 | 2280999 | | HTTP2 | 2290000 | 2290999 | diff --git a/rules/sip-events.rules b/rules/sip-events.rules new file mode 100644 index 000000000000..351d073866d2 --- /dev/null +++ b/rules/sip-events.rules @@ -0,0 +1,5 @@ +# SIP app layer event rules +# +# SID's fall in the 2280000-2280999 range. +# +alert sip any any -> any any (msg:"SURICATA SIP invalid data"; app-layer-event:sip.invalid_data; classtype:protocol-command-decode; sid:2280001; rev:1;) From ba876ce3f91d974b37bf1d0cd96a7fedcdcad6df Mon Sep 17 00:00:00 2001 From: Giuseppe Longo Date: Wed, 3 Jun 2026 18:32:21 +0200 Subject: [PATCH 4/4] suricata.yaml.in: enable app-layer.sip by default --- suricata.yaml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/suricata.yaml.in b/suricata.yaml.in index ccf98213ad0b..bf07ad1c0a2b 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1278,7 +1278,7 @@ app-layer: enabled: yes sip: - #enabled: yes + enabled: yes ldap: tcp: