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 22, 2023
1 parent becb8ce commit bf7a97c
Show file tree
Hide file tree
Showing 7 changed files with 343 additions and 44 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.
144 changes: 100 additions & 44 deletions rust/src/smb/detect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,67 @@ 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();
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();
}

#[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));
}
}

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

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

return Ok(version);
}

#[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 bf7a97c

Please sign in to comment.