Skip to content

Commit

Permalink
smb: add smb version keyword
Browse files Browse the repository at this point in the history
Signed-off-by: jason taylor <[email protected]>
  • Loading branch information
jmtaylor90 committed Aug 28, 2023
1 parent becb8ce commit a9b36fc
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 45 deletions.
42 changes: 42 additions & 0 deletions doc/userguide/rules/smb-keywords.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,45 @@ 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:"SMB1 version rule"; smb.version:1; sid:1;)
alert smb $HOME_NET any -> any any (msg:"SMB2 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.
149 changes: 104 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) 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,70 @@ 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) {
if !ptr.is_null() {
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 @@ -292,6 +292,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 @@ -901,6 +902,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
2 changes: 2 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 @@ -601,6 +602,7 @@ 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 @@ -197,6 +197,7 @@ enum DetectKeywordId {
DETECT_SMB_SHARE,
DETECT_SMB_NTLMSSP_USER,
DETECT_SMB_NTLMSSP_DOMAIN,
DETECT_SMB_VERSION,

DETECT_ASN1,

Expand Down
Loading

0 comments on commit a9b36fc

Please sign in to comment.