diff --git a/doc/userguide/rules/kerberos-keywords.rst b/doc/userguide/rules/kerberos-keywords.rst index 67d1408dd9..30d0210d31 100644 --- a/doc/userguide/rules/kerberos-keywords.rst +++ b/doc/userguide/rules/kerberos-keywords.rst @@ -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 diff --git a/rust/src/krb/detect.rs b/rust/src/krb/detect.rs index 5dffce674c..cde061ed3a 100644 --- a/rust/src/krb/detect.rs +++ b/rust/src/krb/detect.rs @@ -17,7 +17,18 @@ // written by Pierre Chifflier -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, +} + +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 + where + Self: Sized; +} + +impl MyFromStr for EncryptionType { + fn from_str(s: &str) -> Result { + 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::() { + 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."); + } + } + } +} diff --git a/src/Makefile.am b/src/Makefile.am index 481d093b84..23e185a60c 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index e0d8eefa61..1c9d7a275b 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -187,6 +187,7 @@ #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(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index beca581fbd..1451bfbd9c 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -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 index 0000000000..2e2389aea9 --- /dev/null +++ b/src/detect-krb5-ticket-encryption.c @@ -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 index 0000000000..bae81b30ad --- /dev/null +++ b/src/detect-krb5-ticket-encryption.h @@ -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__ */