]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
krb: detection for ticket encryption
authorPhilippe Antoine <contact@catenacyber.fr>
Fri, 22 Jul 2022 15:21:31 +0000 (17:21 +0200)
committerVictor Julien <vjulien@oisf.net>
Fri, 5 Aug 2022 17:51:07 +0000 (19:51 +0200)
As is done for logging.

Ticket: #5442

doc/userguide/rules/kerberos-keywords.rst
rust/src/krb/detect.rs
src/Makefile.am
src/detect-engine-register.c
src/detect-engine-register.h
src/detect-krb5-ticket-encryption.c [new file with mode: 0644]
src/detect-krb5-ticket-encryption.h [new file with mode: 0644]

index 67d1408dd976863ea75b9bc8cd736cedbe522b64..30d0210d3115425b7eaaebfacb95ed6a9a32f867 100644 (file)
@@ -111,3 +111,20 @@ Syntax::
 Signature example::
 
  alert krb5 any any -> any any (msg:"SURICATA Kerberos 5 malformed request data"; flow:to_server; app-layer-event:krb5.malformed_data; classtype:protocol-command-decode; sid:2226000; rev:1;)
+
+krb5.ticket_encryption
+----------------------
+
+Kerberos ticket encryption (enumeration).
+
+For a list of encryption types, refer to RFC3961 section 8.
+
+Syntax::
+
+ krb5.ticket_encryption: (!)"weak" or (space or comma)-separated list of integer or string values for an encryption type
+
+Signature example::
+
+ alert krb5 any any -> any any (krb5.ticket_encryption: weak; sid:1;)
+ alert krb5 any any -> any any (krb5.ticket_encryption: 23; sid:2;)
+ alert krb5 any any -> any any (krb5.ticket_encryption: rc4-hmac,rc4-hmac-exp; sid:3;)
\ No newline at end of file
index 5dffce674c2123ef0fe82c37222432232db5bec3..cde061ed3a3222ce69ae2f7da324bee5226c7f5a 100644 (file)
 
 // written by Pierre Chifflier  <chifflier@wzdftpd.net>
 
-use crate::krb::krb5::KRB5Transaction;
+use crate::krb::krb5::{test_weak_encryption, KRB5Transaction};
+
+use kerberos_parser::krb5::EncryptionType;
+
+use nom7::branch::alt;
+use nom7::bytes::complete::{is_a, tag, take_while, take_while1};
+use nom7::character::complete::char;
+use nom7::combinator::{all_consuming, map_res, opt};
+use nom7::multi::many1;
+use nom7::IResult;
+
+use std::ffi::CStr;
 
 #[no_mangle]
 pub unsafe extern "C" fn rs_krb5_tx_get_msgtype(tx: &mut KRB5Transaction, ptr: *mut u32) {
@@ -66,3 +77,248 @@ pub unsafe extern "C" fn rs_krb5_tx_get_sname(
     }
     0
 }
+
+const KRB_TICKET_FASTARRAY_SIZE: usize = 256;
+
+#[derive(Debug)]
+pub struct DetectKrb5TicketEncryptionList {
+    positive: [bool; KRB_TICKET_FASTARRAY_SIZE],
+    negative: [bool; KRB_TICKET_FASTARRAY_SIZE],
+    other: Vec<EncryptionType>,
+}
+
+impl DetectKrb5TicketEncryptionList {
+    pub fn new() -> DetectKrb5TicketEncryptionList {
+        DetectKrb5TicketEncryptionList {
+            positive: [false; KRB_TICKET_FASTARRAY_SIZE],
+            negative: [false; KRB_TICKET_FASTARRAY_SIZE],
+            other: Vec::new(),
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum DetectKrb5TicketEncryptionData {
+    WEAK(bool),
+    LIST(DetectKrb5TicketEncryptionList),
+}
+
+pub fn detect_parse_encryption_weak(i: &str) -> IResult<&str, DetectKrb5TicketEncryptionData> {
+    let (i, neg) = opt(char('!'))(i)?;
+    let (i, _) = tag("weak")(i)?;
+    let value = match neg {
+        Some(_) => false,
+        _ => true,
+    };
+    return Ok((i, DetectKrb5TicketEncryptionData::WEAK(value)));
+}
+
+trait MyFromStr {
+    fn from_str(s: &str) -> Result<Self, String>
+    where
+        Self: Sized;
+}
+
+impl MyFromStr for EncryptionType {
+    fn from_str(s: &str) -> Result<Self, String> {
+        let su_slice: &str = &*s;
+        match su_slice {
+            "des-cbc-crc" => Ok(EncryptionType::DES_CBC_CRC),
+            "des-cbc-md4" => Ok(EncryptionType::DES_CBC_MD4),
+            "des-cbc-md5" => Ok(EncryptionType::DES_CBC_MD5),
+            "des3-cbc-md5" => Ok(EncryptionType::DES3_CBC_MD5),
+            "des3-cbc-sha1" => Ok(EncryptionType::DES3_CBC_SHA1),
+            "dsaWithSHA1-CmsOID" => Ok(EncryptionType::DSAWITHSHA1_CMSOID),
+            "md5WithRSAEncryption-CmsOID" => Ok(EncryptionType::MD5WITHRSAENCRYPTION_CMSOID),
+            "sha1WithRSAEncryption-CmsOID" => Ok(EncryptionType::SHA1WITHRSAENCRYPTION_CMSOID),
+            "rc2CBC-EnvOID" => Ok(EncryptionType::RC2CBC_ENVOID),
+            "rsaEncryption-EnvOID" => Ok(EncryptionType::RSAENCRYPTION_ENVOID),
+            "rsaES-OAEP-ENV-OID" => Ok(EncryptionType::RSAES_OAEP_ENV_OID),
+            "des-ede3-cbc-Env-OID" => Ok(EncryptionType::DES_EDE3_CBC_ENV_OID),
+            "des3-cbc-sha1-kd" => Ok(EncryptionType::DES3_CBC_SHA1_KD),
+            "aes128-cts-hmac-sha1-96" => Ok(EncryptionType::AES128_CTS_HMAC_SHA1_96),
+            "aes256-cts-hmac-sha1-96" => Ok(EncryptionType::AES256_CTS_HMAC_SHA1_96),
+            "aes128-cts-hmac-sha256-128" => Ok(EncryptionType::AES128_CTS_HMAC_SHA256_128),
+            "aes256-cts-hmac-sha384-192" => Ok(EncryptionType::AES256_CTS_HMAC_SHA384_192),
+            "rc4-hmac" => Ok(EncryptionType::RC4_HMAC),
+            "rc4-hmac-exp" => Ok(EncryptionType::RC4_HMAC_EXP),
+            "camellia128-cts-cmac" => Ok(EncryptionType::CAMELLIA128_CTS_CMAC),
+            "camellia256-cts-cmac" => Ok(EncryptionType::CAMELLIA256_CTS_CMAC),
+            "subkey-keymaterial" => Ok(EncryptionType::SUBKEY_KEYMATERIAL),
+            "rc4-md4" => Ok(EncryptionType::RC4_MD4),
+            "rc4-plain2" => Ok(EncryptionType::RC4_PLAIN2),
+            "rc4-lm" => Ok(EncryptionType::RC4_LM),
+            "rc4-sha" => Ok(EncryptionType::RC4_SHA),
+            "des-plain" => Ok(EncryptionType::DES_PLAIN),
+            "rc4-hmac-OLD" => Ok(EncryptionType::RC4_HMAC_OLD),
+            "rc4-plain-OLD" => Ok(EncryptionType::RC4_PLAIN_OLD),
+            "rc4-hmac-OLD-exp" => Ok(EncryptionType::RC4_HMAC_OLD_EXP),
+            "rc4-plain-OLD-exp" => Ok(EncryptionType::RC4_PLAIN_OLD_EXP),
+            "rc4-plain" => Ok(EncryptionType::RC4_PLAIN),
+            "rc4-plain-exp" => Ok(EncryptionType::RC4_PLAIN_EXP),
+            _ => {
+                if let Ok(num) = s.parse::<i32>() {
+                    return Ok(EncryptionType(num));
+                } else {
+                    return Err(format!("'{}' is not a valid value for EncryptionType", s));
+                }
+            }
+        }
+    }
+}
+
+pub fn is_alphanumeric_or_dash(chr: char) -> bool {
+    return chr.is_alphanumeric() || chr == '-';
+}
+
+pub fn detect_parse_encryption_item(i: &str) -> IResult<&str, EncryptionType> {
+    let (i, _) = opt(is_a(" "))(i)?;
+    let (i, e) = map_res(take_while1(is_alphanumeric_or_dash), |s: &str| {
+        EncryptionType::from_str(s)
+    })(i)?;
+    let (i, _) = opt(is_a(" "))(i)?;
+    let (i, _) = opt(char(','))(i)?;
+    return Ok((i, e));
+}
+
+pub fn detect_parse_encryption_list(i: &str) -> IResult<&str, DetectKrb5TicketEncryptionData> {
+    let mut l = DetectKrb5TicketEncryptionList::new();
+    let (i, v) = many1(detect_parse_encryption_item)(i)?;
+    for &val in v.iter() {
+        let vali = val.0;
+        if vali < 0 && ((-vali) as usize) < KRB_TICKET_FASTARRAY_SIZE {
+            l.negative[(-vali) as usize] = true;
+        } else if vali >= 0 && (vali as usize) < KRB_TICKET_FASTARRAY_SIZE {
+            l.positive[vali as usize] = true;
+        } else {
+            l.other.push(val);
+        }
+    }
+    return Ok((i, DetectKrb5TicketEncryptionData::LIST(l)));
+}
+
+pub fn detect_parse_encryption(i: &str) -> IResult<&str, DetectKrb5TicketEncryptionData> {
+    let (i, _) = opt(is_a(" "))(i)?;
+    let (i, parsed) = alt((detect_parse_encryption_weak, detect_parse_encryption_list))(i)?;
+    let (i, _) = all_consuming(take_while(|c| c == ' '))(i)?;
+    return Ok((i, parsed));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_krb5_detect_encryption_parse(
+    ustr: *const std::os::raw::c_char,
+) -> *mut DetectKrb5TicketEncryptionData {
+    let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
+    if let Ok(s) = ft_name.to_str() {
+        if let Ok((_, ctx)) = detect_parse_encryption(s) {
+            let boxed = Box::new(ctx);
+            return Box::into_raw(boxed) as *mut _;
+        }
+    }
+    return std::ptr::null_mut();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_krb5_detect_encryption_match(
+    tx: &mut KRB5Transaction, ctx: &DetectKrb5TicketEncryptionData,
+) -> std::os::raw::c_int {
+    if let Some(x) = tx.ticket_etype {
+        match ctx {
+            DetectKrb5TicketEncryptionData::WEAK(w) => {
+                if (test_weak_encryption(x) && *w) || (!test_weak_encryption(x) && !*w) {
+                    return 1;
+                }
+            }
+            DetectKrb5TicketEncryptionData::LIST(l) => {
+                let vali = x.0;
+                if vali < 0 && ((-vali) as usize) < KRB_TICKET_FASTARRAY_SIZE {
+                    if l.negative[(-vali) as usize] {
+                        return 1;
+                    }
+                } else if vali >= 0 && (vali as usize) < KRB_TICKET_FASTARRAY_SIZE {
+                    if l.positive[vali as usize] {
+                        return 1;
+                    }
+                } else {
+                    for &val in l.other.iter() {
+                        if x == val {
+                            return 1;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_krb5_detect_encryption_free(ctx: &mut DetectKrb5TicketEncryptionData) {
+    // Just unbox...
+    std::mem::drop(Box::from_raw(ctx));
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+
+    #[test]
+    fn test_detect_parse_encryption() {
+        match detect_parse_encryption(" weak  ") {
+            Ok((rem, ctx)) => {
+                match ctx {
+                    DetectKrb5TicketEncryptionData::WEAK(w) => {
+                        assert_eq!(w, true);
+                    }
+                    _ => {
+                        panic!("Result should have been weak.");
+                    }
+                }
+                // And we should have no bytes left.
+                assert_eq!(rem.len(), 0);
+            }
+            _ => {
+                panic!("Result should have been ok.");
+            }
+        }
+        match detect_parse_encryption("!weak") {
+            Ok((rem, ctx)) => {
+                match ctx {
+                    DetectKrb5TicketEncryptionData::WEAK(w) => {
+                        assert_eq!(w, false);
+                    }
+                    _ => {
+                        panic!("Result should have been weak.");
+                    }
+                }
+                // And we should have no bytes left.
+                assert_eq!(rem.len(), 0);
+            }
+            _ => {
+                panic!("Result should have been ok.");
+            }
+        }
+        match detect_parse_encryption(" des-cbc-crc , -128,2 257") {
+            Ok((rem, ctx)) => {
+                match ctx {
+                    DetectKrb5TicketEncryptionData::LIST(l) => {
+                        assert_eq!(l.positive[EncryptionType::DES_CBC_CRC.0 as usize], true);
+                        assert_eq!(l.negative[128], true);
+                        assert_eq!(l.positive[2], true);
+                        assert_eq!(l.other.len(), 1);
+                        assert_eq!(l.other[0], EncryptionType(257));
+                    }
+                    _ => {
+                        panic!("Result should have been list.");
+                    }
+                }
+                // And we should have no bytes left.
+                assert_eq!(rem.len(), 0);
+            }
+            _ => {
+                panic!("Result should have been ok.");
+            }
+        }
+    }
+}
index 481d093b847ad5475c27b8a24a329af642ad12da..23e185a60c254e6f73e0f5fbb9fa99e68c52cccd 100755 (executable)
@@ -234,6 +234,7 @@ noinst_HEADERS = \
        detect-krb5-errcode.h \
        detect-krb5-msgtype.h \
        detect-krb5-sname.h \
+       detect-krb5-ticket-encryption.h \
        detect-l3proto.h \
        detect-lua-extensions.h \
        detect-lua.h \
@@ -829,6 +830,7 @@ libsuricata_c_a_SOURCES = \
        detect-krb5-errcode.c \
        detect-krb5-msgtype.c \
        detect-krb5-sname.c \
+       detect-krb5-ticket-encryption.c \
        detect-l3proto.c \
        detect-lua.c \
        detect-lua-extensions.c \
index e0d8eefa6173afeb3b58162e5227e3539d1a51cd..1c9d7a275b030d52d1bc49b5693ce04b78b184d7 100644 (file)
 #include "detect-krb5-errcode.h"
 #include "detect-krb5-msgtype.h"
 #include "detect-krb5-sname.h"
+#include "detect-krb5-ticket-encryption.h"
 #include "detect-sip-method.h"
 #include "detect-sip-uri.h"
 #include "detect-sip-protocol.h"
@@ -623,6 +624,7 @@ void SigTableSetup(void)
     DetectKrb5ErrCodeRegister();
     DetectKrb5MsgTypeRegister();
     DetectKrb5SNameRegister();
+    DetectKrb5TicketEncryptionRegister();
     DetectSipMethodRegister();
     DetectSipUriRegister();
     DetectSipProtocolRegister();
index beca581fbd8e0acc813d0de80a2440ceec25f924..1451bfbd9cf9df67f4e8f6632553ca586a2cff23 100644 (file)
@@ -244,6 +244,7 @@ enum DetectKeywordId {
     DETECT_AL_KRB5_MSGTYPE,
     DETECT_AL_KRB5_CNAME,
     DETECT_AL_KRB5_SNAME,
+    DETECT_AL_KRB5_TICKET_ENCRYPTION,
 
     DETECT_AL_SIP_METHOD,
     DETECT_AL_SIP_URI,
diff --git a/src/detect-krb5-ticket-encryption.c b/src/detect-krb5-ticket-encryption.c
new file mode 100644 (file)
index 0000000..2e2389a
--- /dev/null
@@ -0,0 +1,101 @@
+/* Copyright (C) 2022 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
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "rust.h"
+
+#include "detect-krb5-ticket-encryption.h"
+
+#include "detect-engine.h"
+#include "detect-parse.h"
+
+static int g_krb5_ticket_encryption_list_id = 0;
+
+static uint8_t DetectEngineInspectKRB5Generic(DetectEngineCtx *de_ctx,
+        DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine,
+        const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
+{
+    return DetectEngineInspectGenericList(
+            de_ctx, det_ctx, s, engine->smd, f, flags, alstate, txv, tx_id);
+}
+
+static void DetectKrb5TicketEncryptionFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+    rs_krb5_detect_encryption_free(ptr);
+}
+
+static int DetectKrb5TicketEncryptionMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags,
+        void *state, void *txv, const Signature *s, const SigMatchCtx *ctx)
+{
+    const DetectKrb5TicketEncryptionData *dd = (const DetectKrb5TicketEncryptionData *)ctx;
+
+    SCEnter();
+
+    SCReturnInt(rs_krb5_detect_encryption_match(txv, dd));
+}
+
+static int DetectKrb5TicketEncryptionSetup(
+        DetectEngineCtx *de_ctx, Signature *s, const char *krb5str)
+{
+    DetectKrb5TicketEncryptionData *krb5d = NULL;
+    SigMatch *sm = NULL;
+
+    if (DetectSignatureSetAppProto(s, ALPROTO_KRB5) != 0)
+        return -1;
+
+    krb5d = rs_krb5_detect_encryption_parse(krb5str);
+    if (krb5d == NULL)
+        goto error;
+
+    sm = SigMatchAlloc();
+    if (sm == NULL)
+        goto error;
+
+    sm->type = DETECT_AL_KRB5_TICKET_ENCRYPTION;
+    sm->ctx = (void *)krb5d;
+
+    SigMatchAppendSMToList(s, sm, g_krb5_ticket_encryption_list_id);
+
+    return 0;
+
+error:
+    if (krb5d != NULL)
+        DetectKrb5TicketEncryptionFree(de_ctx, krb5d);
+    if (sm != NULL)
+        SCFree(sm);
+    return -1;
+}
+
+void DetectKrb5TicketEncryptionRegister(void)
+{
+    sigmatch_table[DETECT_AL_KRB5_TICKET_ENCRYPTION].name = "krb5.ticket_encryption";
+    sigmatch_table[DETECT_AL_KRB5_TICKET_ENCRYPTION].desc = "match Kerberos 5 ticket encryption";
+    sigmatch_table[DETECT_AL_KRB5_TICKET_ENCRYPTION].url =
+            "/rules/kerberos-keywords.html#krb5-ticket-encryption";
+    sigmatch_table[DETECT_AL_KRB5_TICKET_ENCRYPTION].Match = NULL;
+    sigmatch_table[DETECT_AL_KRB5_TICKET_ENCRYPTION].AppLayerTxMatch =
+            DetectKrb5TicketEncryptionMatch;
+    sigmatch_table[DETECT_AL_KRB5_TICKET_ENCRYPTION].Setup = DetectKrb5TicketEncryptionSetup;
+    sigmatch_table[DETECT_AL_KRB5_TICKET_ENCRYPTION].Free = DetectKrb5TicketEncryptionFree;
+
+    // Tickets are only from server to client
+    DetectAppLayerInspectEngineRegister2("krb5_ticket_encryption", ALPROTO_KRB5, SIG_FLAG_TOCLIENT,
+            0, DetectEngineInspectKRB5Generic, NULL);
+
+    g_krb5_ticket_encryption_list_id = DetectBufferTypeRegister("krb5_ticket_encryption");
+    SCLogDebug("g_krb5_ticket_encryption_list_id %d", g_krb5_ticket_encryption_list_id);
+}
diff --git a/src/detect-krb5-ticket-encryption.h b/src/detect-krb5-ticket-encryption.h
new file mode 100644 (file)
index 0000000..bae81b3
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright (C) 2022 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
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __DETECT_KRB5_TICKET_ENCRYPTION_H__
+#define __DETECT_KRB5_TICKET_ENCRYPTION_H__
+
+void DetectKrb5TicketEncryptionRegister(void);
+
+#endif /* __DETECT_KRB5_TICKET_ENCRYPTION_H__ */