From 75fb352bdefd7a7dc4f9ba73d1bf2b9c96922f59 Mon Sep 17 00:00:00 2001 From: Alice Akaki Date: Thu, 13 Feb 2025 01:18:37 -0400 Subject: [PATCH] detect: add ldap.request.attribute_type ldap.request.attribute_type matches on LDAP attribute type/description This keyword maps the following eve fields: ldap.request.search_request.attributes[] ldap.request.modify_request.changes[].modification.attribute_type ldap.request.add_request.attributes[].name ldap.request.compare_request.attribute_value_assertion.description It is a sticky buffer Supports multiple buffer matching Supports prefiltering Ticket: #7533 --- doc/userguide/rules/ldap-keywords.rst | 44 ++++++++- doc/userguide/rules/multi-buffer-matching.rst | 1 + rust/src/ldap/detect.rs | 94 +++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) diff --git a/doc/userguide/rules/ldap-keywords.rst b/doc/userguide/rules/ldap-keywords.rst index dd196a13e7..d530177a79 100644 --- a/doc/userguide/rules/ldap-keywords.rst +++ b/doc/userguide/rules/ldap-keywords.rst @@ -455,4 +455,46 @@ Example of a signature that would alert if a packet has the LDAP error message ` .. container:: example-rule - alert ldap any any -> any any (msg:"Test LDAP error message"; ldap.responses.message; content:"Size limit exceeded"; sid:1;) \ No newline at end of file + alert ldap any any -> any any (msg:"Test LDAP error message"; ldap.responses.message; content:"Size limit exceeded"; sid:1;) + +ldap.request.attribute_type +--------------------------- + +Matches on LDAP attribute type from request operations. + +Comparison is case-sensitive. + +Syntax:: + + ldap.request.attribute_type; content:""; + +``ldap.request.attribute_type`` is a 'sticky buffer' and can be used as a ``fast_pattern``. + +``ldap.request.attribute_type`` supports multiple buffer matching, see :doc:`multi-buffer-matching`. + +This keyword maps to the EVE fields: + + - ``ldap.request.search_request.attributes[]`` + - ``ldap.request.modify_request.changes[].modification.attribute_type`` + - ``ldap.request.add_request.attributes[].name`` + - ``ldap.request.compare_request.attribute_value_assertion.description`` + +Example +^^^^^^^ + +Example of a signature that would alert if a packet has the LDAP attribute type ``objectClass``: + +.. container:: example-rule + + alert ldap any any -> any any (msg:"Test attribute type"; :example-rule-emphasis:`ldap.request.attribute_type; content:"objectClass";` sid:1;) + +It is possible to use the keyword ``ldap.request.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 +add request operation and contains the LDAP attribute type +``objectClass``. + +.. 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;) diff --git a/doc/userguide/rules/multi-buffer-matching.rst b/doc/userguide/rules/multi-buffer-matching.rst index 1985f389a9..35a511f403 100644 --- a/doc/userguide/rules/multi-buffer-matching.rst +++ b/doc/userguide/rules/multi-buffer-matching.rst @@ -86,6 +86,7 @@ following keywords: * ``ike.vendor`` * ``krb5_cname`` * ``krb5_sname`` +* ``ldap.request.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 857b400982..6ea4f7d69d 100644 --- a/rust/src/ldap/detect.rs +++ b/rust/src/ldap/detect.rs @@ -70,6 +70,7 @@ static mut G_LDAP_RESPONSES_DN_BUFFER_ID: c_int = 0; 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; unsafe extern "C" fn ldap_parse_protocol_req_op( ustr: *const std::os::raw::c_char, @@ -560,6 +561,80 @@ unsafe extern "C" fn ldap_tx_get_responses_msg( return true; } +unsafe extern "C" fn ldap_detect_request_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_REQUEST_ATTRIBUTE_TYPE_BUFFER_ID) < 0 { + return -1; + } + return 0; +} + +unsafe extern "C" fn ldap_detect_request_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_req_attribute_type, + ); +} + +unsafe extern "C" fn ldap_tx_get_req_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); + + *buffer = std::ptr::null(); + *buffer_len = 0; + if let Some(request) = &tx.request { + let str_buffer: &str = match &request.protocol_op { + ProtocolOp::SearchRequest(req) => { + if local_id as usize >= req.attributes.len() { + return false; + } + req.attributes[local_id as usize].0.as_str() + } + ProtocolOp::ModifyRequest(req) => { + if local_id as usize >= req.changes.len() { + return false; + } + req.changes[local_id as usize] + .modification + .attr_type + .0 + .as_str() + } + ProtocolOp::AddRequest(req) => { + if local_id as usize >= req.attributes.len() { + return false; + } + req.attributes[local_id as usize].attr_type.0.as_str() + } + ProtocolOp::CompareRequest(req) => { + if local_id > 0 { + return false; + } + req.ava.attribute_desc.0.as_str() + } + _ => return false, + }; + *buffer = str_buffer.as_ptr(); + *buffer_len = str_buffer.len() as u32; + return true; + } + return false; +} + #[no_mangle] pub unsafe extern "C" fn SCDetectLdapRegister() { let kw = SCSigTableElmt { @@ -682,4 +757,23 @@ pub unsafe extern "C" fn SCDetectLdapRegister() { false, //to server ldap_detect_responses_msg_get_data, ); + let kw = SCSigTableElmt { + name: b"ldap.request.attribute_type\0".as_ptr() as *const libc::c_char, + desc: b"match request LDAP attribute type\0".as_ptr() as *const libc::c_char, + url: b"/rules/ldap-keywords.html#ldap.request.attribute_type\0".as_ptr() + as *const libc::c_char, + Setup: ldap_detect_request_attibute_type_setup, + flags: SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER, + AppLayerTxMatch: None, + Free: None, + }; + let _g_ldap_request_attribute_type_kw_id = DetectHelperKeywordRegister(&kw); + G_LDAP_REQUEST_ATTRIBUTE_TYPE_BUFFER_ID = DetectHelperMultiBufferMpmRegister( + b"ldap.request.attribute_type\0".as_ptr() as *const libc::c_char, + b"LDAP REQUEST ATTRIBUTE TYPE\0".as_ptr() as *const libc::c_char, + ALPROTO_LDAP, + false, //to client + true, //to server + ldap_detect_request_attribute_type_get_data, + ); }