detect: add ldap.responses.dn

ldap.responses.dn matches on LDAPDN from responses operations
This keyword maps the following eve fields:
ldap.responses[].search_result_entry.base_object
ldap.responses[].bind_response.matched_dn
ldap.responses[].search_result_done.matched_dn
ldap.responses[].modify_response.matched_dn
ldap.responses[].add_response.matched_dn
ldap.responses[].del_response.matched_dn
ldap.responses[].mod_dn_response.matched_dn
ldap.responses[].compare_response.matched_dn
ldap.responses[].extended_response.matched_dn
It is a sticky buffer
Supports prefiltering

Ticket: #7471
pull/12653/head
Alice Akaki 3 weeks ago committed by Victor Julien
parent 16dcee46fc
commit 73ae6e997f

@ -205,3 +205,49 @@ search request operation and contains the LDAP distinguished name
.. container:: example-rule
alert ldap any any -> any any (msg:"Test LDAPDN and operation"; :example-rule-emphasis:`ldap.request.operation:search_request; ldap.request.dn; content:"dc=example,dc=com";` sid:1;)
ldap.responses.dn
-----------------
Matches on LDAP distinguished names from response operations.
Comparison is case-sensitive.
Syntax::
ldap.responses.dn; content:dc=example,dc=com;
``ldap.responses.dn`` is a 'sticky buffer' and can be used as a ``fast_pattern``.
``ldap.responses.dn`` supports multiple buffer matching, see :doc:`multi-buffer-matching`.
This keyword maps to the EVE fields:
``ldap.responses[].search_result_entry.base_object``
``ldap.responses[].bind_response.matched_dn``
``ldap.responses[].search_result_done.matched_dn``
``ldap.responses[].modify_response.matched_dn``
``ldap.responses[].add_response.matched_dn``
``ldap.responses[].del_response.matched_dn``
``ldap.responses[].mod_dn_response.matched_dn``
``ldap.responses[].compare_response.matched_dn``
``ldap.responses[].extended_response.matched_dn``
Example
^^^^^^^
Example of a signature that would alert if a packet has the LDAP distinguished name ``dc=example,dc=com``:
.. container:: example-rule
alert ldap any any -> any any (msg:"Test LDAPDN"; :example-rule-emphasis:`ldap.responses.dn; content:"dc=example,dc=com";` sid:1;)
It is possible to use the keyword ``ldap.responses.operation`` in the same rule to
specify the operation to match.
Here is an example of a signature that would alert if a packet has an LDAP
search result entry operation at index 1 on the responses array,
and contains the LDAP distinguished name ``dc=example,dc=com``.
.. container:: example-rule
alert ldap any any -> any any (msg:"Test LDAPDN and operation"; :example-rule-emphasis:`ldap.responses.operation:search_result_entry,1; ldap.responses.dn; content:"dc=example,dc=com";` sid:1;)

@ -22,7 +22,8 @@ use crate::detect::uint::{
};
use crate::detect::{
DetectBufferSetActiveList, DetectHelperBufferMpmRegister, DetectHelperBufferRegister,
DetectHelperGetData, DetectHelperKeywordRegister, DetectSignatureSetAppProto, SCSigTableElmt,
DetectHelperGetData, DetectHelperGetMultiData, DetectHelperKeywordRegister,
DetectHelperMultiBufferMpmRegister, DetectSignatureSetAppProto, SCSigTableElmt,
SigMatchAppendSMToList, SIGMATCH_INFO_STICKY_BUFFER, SIGMATCH_NOOPT,
};
use crate::ldap::types::{LdapMessage, ProtocolOp, ProtocolOpCode};
@ -55,6 +56,7 @@ static mut G_LDAP_RESPONSES_OPERATION_BUFFER_ID: c_int = 0;
static mut G_LDAP_RESPONSES_COUNT_KW_ID: c_int = 0;
static mut G_LDAP_RESPONSES_COUNT_BUFFER_ID: c_int = 0;
static mut G_LDAP_REQUEST_DN_BUFFER_ID: c_int = 0;
static mut G_LDAP_RESPONSES_DN_BUFFER_ID: c_int = 0;
unsafe extern "C" fn ldap_parse_protocol_req_op(
ustr: *const std::os::raw::c_char,
@ -318,6 +320,65 @@ unsafe extern "C" fn ldap_tx_get_request_dn(
return false;
}
unsafe extern "C" fn ldap_detect_responses_dn_setup(
de: *mut c_void, s: *mut c_void, _raw: *const std::os::raw::c_char,
) -> c_int {
if DetectSignatureSetAppProto(s, ALPROTO_LDAP) != 0 {
return -1;
}
if DetectBufferSetActiveList(de, s, G_LDAP_RESPONSES_DN_BUFFER_ID) < 0 {
return -1;
}
return 0;
}
unsafe extern "C" fn ldap_detect_responses_dn_get_data(
de: *mut c_void, transforms: *const c_void, flow: *const c_void, flow_flags: u8,
tx: *const c_void, list_id: c_int, local_id: u32,
) -> *mut c_void {
return DetectHelperGetMultiData(
de,
transforms,
flow,
flow_flags,
tx,
list_id,
local_id,
ldap_tx_get_responses_dn,
);
}
unsafe extern "C" fn ldap_tx_get_responses_dn(
tx: *const c_void, _flags: u8, local_id: u32, buffer: *mut *const u8, buffer_len: *mut u32,
) -> bool {
let tx = cast_pointer!(tx, LdapTransaction);
if local_id as usize >= tx.responses.len() {
return false;
}
*buffer = std::ptr::null();
*buffer_len = 0;
let response = &tx.responses[local_id as usize];
// We expect every response in one tx to be the same protocol_op
let str_buffer: &str = match &response.protocol_op {
ProtocolOp::SearchResultEntry(req) => req.object_name.0.as_str(),
ProtocolOp::BindResponse(req) => req.result.matched_dn.0.as_str(),
ProtocolOp::SearchResultDone(req) => req.matched_dn.0.as_str(),
ProtocolOp::ModifyResponse(req) => req.result.matched_dn.0.as_str(),
ProtocolOp::AddResponse(req) => req.matched_dn.0.as_str(),
ProtocolOp::DelResponse(req) => req.matched_dn.0.as_str(),
ProtocolOp::ModDnResponse(req) => req.matched_dn.0.as_str(),
ProtocolOp::CompareResponse(req) => req.matched_dn.0.as_str(),
ProtocolOp::ExtendedResponse(req) => req.result.matched_dn.0.as_str(),
_ => return false,
};
*buffer = str_buffer.as_ptr();
*buffer_len = str_buffer.len() as u32;
return true;
}
#[no_mangle]
pub unsafe extern "C" fn SCDetectLdapRegister() {
let kw = SCSigTableElmt {
@ -387,4 +448,22 @@ pub unsafe extern "C" fn SCDetectLdapRegister() {
true, //to server
ldap_detect_request_dn_get_data,
);
let kw = SCSigTableElmt {
name: b"ldap.responses.dn\0".as_ptr() as *const libc::c_char,
desc: b"match responses LDAPDN\0".as_ptr() as *const libc::c_char,
url: b"/rules/ldap-keywords.html#ldap.responses.dn\0".as_ptr() as *const libc::c_char,
Setup: ldap_detect_responses_dn_setup,
flags: SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER,
AppLayerTxMatch: None,
Free: None,
};
let _g_ldap_responses_dn_kw_id = DetectHelperKeywordRegister(&kw);
G_LDAP_RESPONSES_DN_BUFFER_ID = DetectHelperMultiBufferMpmRegister(
b"ldap.responses.dn\0".as_ptr() as *const libc::c_char,
b"LDAP RESPONSES DISTINGUISHED_NAME\0".as_ptr() as *const libc::c_char,
ALPROTO_LDAP,
true, //to client
false, //to server
ldap_detect_responses_dn_get_data,
);
}

Loading…
Cancel
Save