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
pull/15610/head
Giuseppe Longo 6 months ago committed by Victor Julien
parent 762b725e1f
commit ba5e850264

@ -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 <giuseppe@glongo.it>
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\

@ -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 {

Loading…
Cancel
Save