From d4ec5b976560c73da404a2d4fbe632821fe3ec43 Mon Sep 17 00:00:00 2001 From: Alice Akaki Date: Sun, 2 Mar 2025 19:41:26 -0400 Subject: [PATCH] detect: add ldap.responses.attribute_type ldap.responses.attribute_type matches on LDAP attribute type/description This keyword maps the eve field ldap.responses[].search_result_entry.attributes[].type It is a sticky buffer Supports multiple buffer matching Supports prefiltering Ticket: #7533 --- doc/userguide/rules/ldap-keywords.rst | 37 ++++++++++ doc/userguide/rules/multi-buffer-matching.rst | 1 + rust/src/ldap/detect.rs | 73 +++++++++++++++++++ 3 files changed, 111 insertions(+) diff --git a/doc/userguide/rules/ldap-keywords.rst b/doc/userguide/rules/ldap-keywords.rst index d530177a79..167cefd755 100644 --- a/doc/userguide/rules/ldap-keywords.rst +++ b/doc/userguide/rules/ldap-keywords.rst @@ -498,3 +498,40 @@ add request operation and contains the LDAP attribute type .. container:: example-rule alert ldap any any -> any any (msg:"Test attribute type and operation"; :example-rule-emphasis:`ldap.request.operation:add_request; ldap.request.attribute_type; content:"objectClass";` sid:1;) + +ldap.responses.attribute_type +----------------------------- + +Matches on LDAP attribute type from response operations. + +Comparison is case-sensitive. + +Syntax:: + + ldap.responses.attribute_type; content:""; + +``ldap.responses.attribute_type`` is a 'sticky buffer' and can be used as a ``fast_pattern``. + +``ldap.responses.attribute_type`` supports multiple buffer matching, see :doc:`multi-buffer-matching`. + +This keyword maps to the EVE field ``ldap.responses[].search_result_entry.attributes[].type`` + +Example +^^^^^^^ + +Example of a signature that would alert if a packet has the LDAP attribute type ``dc``: + +.. container:: example-rule + + alert ldap any any -> any any (msg:"Test responses attribute type"; :example-rule-emphasis:`ldap.responses.attribute_type; content:"dc";` 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 attribute type ``dc``. + +.. container:: example-rule + + alert ldap any any -> any any (msg:"Test attribute type and operation"; :example-rule-emphasis:`ldap.responses.operation:search_result_entry,1; ldap.responses.attribute_type; content:"dc";` sid:1;) diff --git a/doc/userguide/rules/multi-buffer-matching.rst b/doc/userguide/rules/multi-buffer-matching.rst index 35a511f403..166e37c84e 100644 --- a/doc/userguide/rules/multi-buffer-matching.rst +++ b/doc/userguide/rules/multi-buffer-matching.rst @@ -87,6 +87,7 @@ following keywords: * ``krb5_cname`` * ``krb5_sname`` * ``ldap.request.attribute_type`` +* ``ldap.responses.attribute_type`` * ``ldap.responses.dn`` * ``ldap.responses.message`` * ``mqtt.subscribe.topic`` diff --git a/rust/src/ldap/detect.rs b/rust/src/ldap/detect.rs index 6ea4f7d69d..614c289ac9 100644 --- a/rust/src/ldap/detect.rs +++ b/rust/src/ldap/detect.rs @@ -71,6 +71,7 @@ static mut G_LDAP_RESPONSES_RESULT_CODE_KW_ID: c_int = 0; static mut G_LDAP_RESPONSES_RESULT_CODE_BUFFER_ID: c_int = 0; static mut G_LDAP_RESPONSES_MSG_BUFFER_ID: c_int = 0; static mut G_LDAP_REQUEST_ATTRIBUTE_TYPE_BUFFER_ID: c_int = 0; +static mut G_LDAP_RESPONSES_ATTRIBUTE_TYPE_BUFFER_ID: c_int = 0; unsafe extern "C" fn ldap_parse_protocol_req_op( ustr: *const std::os::raw::c_char, @@ -635,6 +636,59 @@ unsafe extern "C" fn ldap_tx_get_req_attribute_type( return false; } +unsafe extern "C" fn ldap_detect_responses_attibute_type_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_ATTRIBUTE_TYPE_BUFFER_ID) < 0 { + return -1; + } + return 0; +} + +unsafe extern "C" fn ldap_detect_responses_attribute_type_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_resp_attribute_type, + ); +} + +unsafe extern "C" fn ldap_tx_get_resp_attribute_type( + tx: *const c_void, _flags: u8, local_id: u32, buffer: *mut *const u8, buffer_len: *mut u32, +) -> bool { + let tx = cast_pointer!(tx, LdapTransaction); + + let mut pos = 0_u32; + for i in 0..tx.responses.len() { + let response = &tx.responses[i]; + match &response.protocol_op { + ProtocolOp::SearchResultEntry(resp) => { + if local_id < pos + resp.attributes.len() as u32 { + let value = &resp.attributes[(local_id - pos) as usize].attr_type.0; + *buffer = value.as_ptr(); //unsafe + *buffer_len = value.len() as u32; + return true; + } else { + pos += resp.attributes.len() as u32; + } + } + _ => continue, + } + } + return false; +} + #[no_mangle] pub unsafe extern "C" fn SCDetectLdapRegister() { let kw = SCSigTableElmt { @@ -776,4 +830,23 @@ pub unsafe extern "C" fn SCDetectLdapRegister() { true, //to server ldap_detect_request_attribute_type_get_data, ); + let kw = SCSigTableElmt { + name: b"ldap.responses.attribute_type\0".as_ptr() as *const libc::c_char, + desc: b"match LDAP responses attribute type\0".as_ptr() as *const libc::c_char, + url: b"/rules/ldap-keywords.html#ldap.responses.attribute_type\0".as_ptr() + as *const libc::c_char, + Setup: ldap_detect_responses_attibute_type_setup, + flags: SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER, + AppLayerTxMatch: None, + Free: None, + }; + let _g_ldap_responses_attribute_type_kw_id = DetectHelperKeywordRegister(&kw); + G_LDAP_RESPONSES_ATTRIBUTE_TYPE_BUFFER_ID = DetectHelperMultiBufferMpmRegister( + b"ldap.responses.attribute_type\0".as_ptr() as *const libc::c_char, + b"LDAP RESPONSES ATTRIBUTE TYPE\0".as_ptr() as *const libc::c_char, + ALPROTO_LDAP, + true, //to client + false, //to server + ldap_detect_responses_attribute_type_get_data, + ); } -- 2.47.2