Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

smb: add smb.version keyword 5075 v6 #9906

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions doc/userguide/rules/smb-keywords.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,48 @@ Examples::
``smb.ntlmssp_domain`` is a 'sticky buffer'.

``smb.ntlmssp_domain`` can be used as ``fast_pattern``.


smb.version
------------

Keyword to match on SMB version.

Examples::

alert smb $HOME_NET any -> any any (msg:"SMBv1 version rule"; smb.version:1; sid:1;)
alert smb $HOME_NET any -> any any (msg:"SMBv2 version rule"; smb.version:2; sid:2;)


Matching in transition from SMBv1 to SMBv2
******************************************

In the initial negotiation protocol request, a client supporting SMBv1 and SMBv2 can send an initial SMBv1 request and receive an SMBv2 response from server, indicating that SMBv2 will be used.

This first SMBv2 response made by the server will match as SMBv1, since the entire transaction will be considered a SMBv1 transaction.

Will `smb.version` match SMBv3 traffic?
***************************************

Yes, it will match SMBv3 messages using `smb.version:2;`, which will match SMBv2 and SMBv3, since they use the same version identifier in the SMB header.

This keyword will use the Protocol ID specified in SMB header to determine the version. Here is a summary of the Protocol ID codes:

- 0xffSMB is SMB1 `header <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb/3c0848a6-efe9-47c2-b57a-f7e8217150b9>`_
- 0xfeSMB is SMB2 `normal header <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5cd64522-60b3-4f3e-a157-fe66f1228052>`_ (can be `sync <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/fb188936-5050-48d3-b350-dc43059638a4>`_ or `async <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ea4560b7-90da-4803-82b5-344754b92a79>`_)
- 0xfdSMB is SMB2 `transform header <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/d6ce2327-a4c9-4793-be66-7b5bad2175fa>`_. This is only valid for the SMB 3.x dialect family.
- 0xfcSMB is SMB2 `transform compression header <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/d6ce2327-a4c9-4793-be66-7b5bad2175fa>`_ (can be `chained <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/aa880fe8-ebed-4409-a474-ec6e0ca0dbcb>`_ or `unchained <https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/793db6bb-25b4-4469-be49-a8d7045ba3a6>`_). These ones requires the use of 3.1.1 dialect.

The Protocol ID in header distinguishes only SMB1 and SMB2 since they are totally different protocols with total different message formats, types and implementation.

On the other hand SMB3 is more an extension for SMB2. When using SMB2 we can select one of the following dialects for the conversation between client and server:

- 2.0.2
- 2.1
- 3.0
- 3.0.2
- 3.1.1

We say we are using SMB3 when we select a 3.x dialect for the conversation, so you can use SMB3.0, SMB3.0.2 or SMB3.1.1. The higher you choose, the more capabilities you have, but the message syntax and message command number remains the same.

SMB version and dialect are separate components. In the case of SMBv3 for instance, the SMB version will be 2 but the dialect will be 3.x.
147 changes: 102 additions & 45 deletions rust/src/smb/detect.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2017 Open Information Security Foundation
/* Copyright (C) 2017-2023 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
Expand All @@ -15,19 +15,20 @@
* 02110-1301, USA.
*/

use std::ptr;
use crate::core::*;
use crate::smb::smb::*;
use crate::dcerpc::detect::{DCEIfaceData, DCEOpnumData, DETECT_DCE_OPNUM_RANGE_UNINITIALIZED};
use crate::dcerpc::dcerpc::DCERPC_TYPE_REQUEST;
use crate::dcerpc::detect::{DCEIfaceData, DCEOpnumData, DETECT_DCE_OPNUM_RANGE_UNINITIALIZED};
use crate::detect::uint::detect_match_uint;
use crate::smb::smb::SMBTransaction;
use crate::smb::smb::*;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
use std::ptr;

#[no_mangle]
pub unsafe extern "C" fn rs_smb_tx_get_share(tx: &mut SMBTransaction,
buffer: *mut *const u8,
buffer_len: *mut u32)
-> u8
{
pub unsafe extern "C" fn rs_smb_tx_get_share(
tx: &mut SMBTransaction, buffer: *mut *const u8, buffer_len: *mut u32,
) -> u8 {
if let Some(SMBTransactionTypeData::TREECONNECT(ref x)) = tx.type_data {
SCLogDebug!("is_pipe {}", x.is_pipe);
if !x.is_pipe {
Expand All @@ -43,11 +44,9 @@ pub unsafe extern "C" fn rs_smb_tx_get_share(tx: &mut SMBTransaction,
}

#[no_mangle]
pub unsafe extern "C" fn rs_smb_tx_get_named_pipe(tx: &mut SMBTransaction,
buffer: *mut *const u8,
buffer_len: *mut u32)
-> u8
{
pub unsafe extern "C" fn rs_smb_tx_get_named_pipe(
tx: &mut SMBTransaction, buffer: *mut *const u8, buffer_len: *mut u32,
) -> u8 {
if let Some(SMBTransactionTypeData::TREECONNECT(ref x)) = tx.type_data {
SCLogDebug!("is_pipe {}", x.is_pipe);
if x.is_pipe {
Expand All @@ -63,12 +62,9 @@ pub unsafe extern "C" fn rs_smb_tx_get_named_pipe(tx: &mut SMBTransaction,
}

#[no_mangle]
pub unsafe extern "C" fn rs_smb_tx_get_stub_data(tx: &mut SMBTransaction,
direction: u8,
buffer: *mut *const u8,
buffer_len: *mut u32)
-> u8
{
pub unsafe extern "C" fn rs_smb_tx_get_stub_data(
tx: &mut SMBTransaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
) -> u8 {
if let Some(SMBTransactionTypeData::DCERPC(ref x)) = tx.type_data {
let vref = if direction == Direction::ToServer as u8 {
&x.stub_data_ts
Expand All @@ -88,10 +84,9 @@ pub unsafe extern "C" fn rs_smb_tx_get_stub_data(tx: &mut SMBTransaction,
}

#[no_mangle]
pub extern "C" fn rs_smb_tx_match_dce_opnum(tx: &mut SMBTransaction,
dce_data: &mut DCEOpnumData)
-> u8
{
pub extern "C" fn rs_smb_tx_match_dce_opnum(
tx: &mut SMBTransaction, dce_data: &mut DCEOpnumData,
) -> u8 {
SCLogDebug!("rs_smb_tx_get_dce_opnum: start");
if let Some(SMBTransactionTypeData::DCERPC(ref x)) = tx.type_data {
if x.req_cmd == DCERPC_TYPE_REQUEST {
Expand All @@ -115,17 +110,13 @@ pub extern "C" fn rs_smb_tx_match_dce_opnum(tx: &mut SMBTransaction,
* dce_opnum and dce_stub_data)
* - only match on approved ifaces (so ack_result == 0) */
#[no_mangle]
pub extern "C" fn rs_smb_tx_get_dce_iface(state: &mut SMBState,
tx: &mut SMBTransaction,
dce_data: &mut DCEIfaceData)
-> u8
{
pub extern "C" fn rs_smb_tx_get_dce_iface(
state: &mut SMBState, tx: &mut SMBTransaction, dce_data: &mut DCEIfaceData,
) -> u8 {
let if_uuid = dce_data.if_uuid.as_slice();
let is_dcerpc_request = match tx.type_data {
Some(SMBTransactionTypeData::DCERPC(ref x)) => {
x.req_cmd == DCERPC_TYPE_REQUEST
},
_ => { false },
Some(SMBTransactionTypeData::DCERPC(ref x)) => x.req_cmd == DCERPC_TYPE_REQUEST,
_ => false,
};
if !is_dcerpc_request {
return 0;
Expand All @@ -134,13 +125,18 @@ pub extern "C" fn rs_smb_tx_get_dce_iface(state: &mut SMBState,
Some(ref x) => x,
_ => {
return 0;
},
}
};

SCLogDebug!("looking for UUID {:?}", if_uuid);

for i in ifaces {
SCLogDebug!("stored UUID {:?} acked {} ack_result {}", i, i.acked, i.ack_result);
SCLogDebug!(
"stored UUID {:?} acked {} ack_result {}",
i,
i.acked,
i.ack_result
);

if i.acked && i.ack_result == 0 && i.uuid == if_uuid {
if let Some(x) = &dce_data.du16 {
Expand All @@ -156,11 +152,9 @@ pub extern "C" fn rs_smb_tx_get_dce_iface(state: &mut SMBState,
}

#[no_mangle]
pub unsafe extern "C" fn rs_smb_tx_get_ntlmssp_user(tx: &mut SMBTransaction,
buffer: *mut *const u8,
buffer_len: *mut u32)
-> u8
{
pub unsafe extern "C" fn rs_smb_tx_get_ntlmssp_user(
tx: &mut SMBTransaction, buffer: *mut *const u8, buffer_len: *mut u32,
) -> u8 {
if let Some(SMBTransactionTypeData::SESSIONSETUP(ref x)) = tx.type_data {
if let Some(ref ntlmssp) = x.ntlmssp {
*buffer = ntlmssp.user.as_ptr();
Expand All @@ -175,11 +169,9 @@ pub unsafe extern "C" fn rs_smb_tx_get_ntlmssp_user(tx: &mut SMBTransaction,
}

#[no_mangle]
pub unsafe extern "C" fn rs_smb_tx_get_ntlmssp_domain(tx: &mut SMBTransaction,
buffer: *mut *const u8,
buffer_len: *mut u32)
-> u8
{
pub unsafe extern "C" fn rs_smb_tx_get_ntlmssp_domain(
tx: &mut SMBTransaction, buffer: *mut *const u8, buffer_len: *mut u32,
) -> u8 {
if let Some(SMBTransactionTypeData::SESSIONSETUP(ref x)) = tx.type_data {
if let Some(ref ntlmssp) = x.ntlmssp {
*buffer = ntlmssp.domain.as_ptr();
Expand All @@ -192,3 +184,68 @@ pub unsafe extern "C" fn rs_smb_tx_get_ntlmssp_domain(tx: &mut SMBTransaction,
*buffer_len = 0;
return 0;
}

#[no_mangle]
pub unsafe extern "C" fn rs_smb_version_match(
tx: &mut SMBTransaction, version_data: &mut u8,
) -> u8 {
let version = tx.vercmd.get_version();
SCLogDebug!("smb_version: version returned: {}", version);
if version == *version_data {
return 1;
}

return 0;
}

#[no_mangle]
pub unsafe extern "C" fn rs_smb_version_parse(carg: *const c_char) -> *mut c_void {
if carg.is_null() {
return std::ptr::null_mut();
}

if let Ok(arg) = CStr::from_ptr(carg).to_str() {
if let Ok(detect) = parse_version_data(arg) {
return Box::into_raw(Box::new(detect)) as *mut _;
}
}

return std::ptr::null_mut();
}

fn parse_version_data(arg: &str) -> Result<u8, ()> {
let arg = arg.trim();
let version: u8 = arg.parse().map_err(|_| ())?;

SCLogDebug!("smb_version: sig parse arg: {} version: {}", arg, version);

if version != 1 && version != 2 {
return Err(());
}

return Ok(version);
}

#[no_mangle]
pub unsafe extern "C" fn rs_smb_version_free(ptr: *mut c_void) {
std::mem::drop(Box::from_raw(ptr as *mut u8));
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_cmd_data() {
assert_eq!(Err(()), parse_version_data("0"));
assert_eq!(1u8, parse_version_data("1").unwrap());
assert_eq!(2u8, parse_version_data("2").unwrap());
assert_eq!(Err(()), parse_version_data("3"));
}

#[test]
fn test_parse_cmd_data_with_spaces() {
assert_eq!(1u8, parse_version_data(" 1").unwrap());
assert_eq!(2u8, parse_version_data(" 2 ").unwrap());
}
}
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ noinst_HEADERS = \
detect-sip-uri.h \
detect-smb-ntlmssp.h \
detect-smb-share.h \
detect-smb-version.h \
detect-snmp-community.h \
detect-snmp-pdu_type.h \
detect-snmp-usm.h \
Expand Down Expand Up @@ -908,6 +909,7 @@ libsuricata_c_a_SOURCES = \
detect-sip-uri.c \
detect-smb-ntlmssp.c \
detect-smb-share.c \
detect-smb-version.c \
detect-snmp-community.c \
detect-snmp-pdu_type.c \
detect-snmp-usm.c \
Expand Down
3 changes: 3 additions & 0 deletions src/detect-engine-register.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
#include "detect-config.h"

#include "detect-smb-share.h"
#include "detect-smb-version.h"

#include "detect-base64-decode.h"
#include "detect-base64-data.h"
Expand Down Expand Up @@ -609,6 +610,8 @@ void SigTableSetup(void)
DetectSmbShareRegister();
DetectSmbNtlmsspUserRegister();
DetectSmbNtlmsspDomainRegister();
DetectSmbVersionRegister();

DetectTlsRegister();
DetectTlsValidityRegister();
DetectTlsVersionRegister();
Expand Down
1 change: 1 addition & 0 deletions src/detect-engine-register.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ enum DetectKeywordId {
DETECT_SMB_SHARE,
DETECT_SMB_NTLMSSP_USER,
DETECT_SMB_NTLMSSP_DOMAIN,
DETECT_SMB_VERSION,

DETECT_ASN1,

Expand Down
Loading
Loading