mirror of https://github.com/OISF/suricata
parent
9d0db71ebf
commit
120313f4da
@ -0,0 +1,383 @@
|
||||
/* Copyright (C) 2023-2024 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.
|
||||
|
||||
// Author: Sascha Steinbiss <sascha@steinbiss.name>
|
||||
|
||||
*/
|
||||
|
||||
#[cfg(feature = "ja4")]
|
||||
use digest::Digest;
|
||||
use libc::c_uchar;
|
||||
#[cfg(feature = "ja4")]
|
||||
use sha2::Sha256;
|
||||
use std::{cmp::min, os::raw::c_char};
|
||||
use tls_parser::{TlsCipherSuiteID, TlsExtensionType, TlsVersion};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct JA4 {
|
||||
tls_version: Option<TlsVersion>,
|
||||
ciphersuites: Vec<TlsCipherSuiteID>,
|
||||
extensions: Vec<TlsExtensionType>,
|
||||
signature_algorithms: Vec<u16>,
|
||||
domain: bool,
|
||||
alpn: [char; 2],
|
||||
quic: bool,
|
||||
// Some extensions contribute to the total count component of the
|
||||
// fingerprint, yet are not to be included in the SHA256 hash component.
|
||||
// Let's track the count separately.
|
||||
nof_exts: u16,
|
||||
}
|
||||
|
||||
impl Default for JA4 {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// Stubs for when JA4 is disabled
|
||||
#[cfg(not(feature = "ja4"))]
|
||||
impl JA4 {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tls_version: None,
|
||||
// Vec::new() does not allocate memory until filled, which we
|
||||
// will not do here.
|
||||
ciphersuites: Vec::new(),
|
||||
extensions: Vec::new(),
|
||||
signature_algorithms: Vec::new(),
|
||||
domain: false,
|
||||
alpn: ['0', '0'],
|
||||
quic: false,
|
||||
nof_exts: 0,
|
||||
}
|
||||
}
|
||||
pub fn set_quic(&mut self) {}
|
||||
pub fn set_tls_version(&mut self, _version: TlsVersion) {}
|
||||
pub fn set_alpn(&mut self, _alpn: &[u8]) {}
|
||||
pub fn add_cipher_suite(&mut self, _cipher: TlsCipherSuiteID) {}
|
||||
pub fn add_extension(&mut self, _ext: TlsExtensionType) {}
|
||||
pub fn add_signature_algorithm(&mut self, _sigalgo: u16) {}
|
||||
pub fn get_hash(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ja4")]
|
||||
impl JA4 {
|
||||
#[inline]
|
||||
fn is_grease(val: u16) -> bool {
|
||||
match val {
|
||||
0x0a0a | 0x1a1a | 0x2a2a | 0x3a3a | 0x4a4a | 0x5a5a | 0x6a6a | 0x7a7a | 0x8a8a
|
||||
| 0x9a9a | 0xaaaa | 0xbaba | 0xcaca | 0xdada | 0xeaea | 0xfafa => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn version_to_ja4code(val: Option<TlsVersion>) -> &'static str {
|
||||
match val {
|
||||
Some(TlsVersion::Tls13) => "13",
|
||||
Some(TlsVersion::Tls12) => "12",
|
||||
Some(TlsVersion::Tls11) => "11",
|
||||
Some(TlsVersion::Tls10) => "10",
|
||||
Some(TlsVersion::Ssl30) => "s3",
|
||||
// the TLS parser does not support SSL 1.0 and 2.0 hence no
|
||||
// support for "s1"/"s2"
|
||||
_ => "00",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tls_version: None,
|
||||
ciphersuites: Vec::with_capacity(20),
|
||||
extensions: Vec::with_capacity(20),
|
||||
signature_algorithms: Vec::with_capacity(20),
|
||||
domain: false,
|
||||
alpn: ['0', '0'],
|
||||
quic: false,
|
||||
nof_exts: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_quic(&mut self) {
|
||||
self.quic = true;
|
||||
}
|
||||
|
||||
pub fn set_tls_version(&mut self, version: TlsVersion) {
|
||||
if JA4::is_grease(u16::from(version)) {
|
||||
return;
|
||||
}
|
||||
// Track maximum of seen TLS versions
|
||||
match self.tls_version {
|
||||
None => {
|
||||
self.tls_version = Some(version);
|
||||
}
|
||||
Some(cur_version) => {
|
||||
if u16::from(version) > u16::from(cur_version) {
|
||||
self.tls_version = Some(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_alpn(&mut self, alpn: &[u8]) {
|
||||
if alpn.len() > 1 {
|
||||
if alpn.len() == 2 {
|
||||
// GREASE values are 2 bytes, so this could be one -- check
|
||||
let v: u16 = (alpn[0] as u16) << 8 | alpn[alpn.len() - 1] as u16;
|
||||
if JA4::is_grease(v) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.alpn[0] = char::from(alpn[0]);
|
||||
self.alpn[1] = char::from(alpn[alpn.len() - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_cipher_suite(&mut self, cipher: TlsCipherSuiteID) {
|
||||
if JA4::is_grease(u16::from(cipher)) {
|
||||
return;
|
||||
}
|
||||
self.ciphersuites.push(cipher);
|
||||
}
|
||||
|
||||
pub fn add_extension(&mut self, ext: TlsExtensionType) {
|
||||
if JA4::is_grease(u16::from(ext)) {
|
||||
return;
|
||||
}
|
||||
if ext != TlsExtensionType::ApplicationLayerProtocolNegotiation
|
||||
&& ext != TlsExtensionType::ServerName
|
||||
{
|
||||
self.extensions.push(ext);
|
||||
} else if ext == TlsExtensionType::ServerName {
|
||||
self.domain = true;
|
||||
}
|
||||
self.nof_exts += 1;
|
||||
}
|
||||
|
||||
pub fn add_signature_algorithm(&mut self, sigalgo: u16) {
|
||||
if JA4::is_grease(sigalgo) {
|
||||
return;
|
||||
}
|
||||
self.signature_algorithms.push(sigalgo);
|
||||
}
|
||||
|
||||
pub fn get_hash(&self) -> String {
|
||||
// Calculate JA4_a
|
||||
let ja4_a = format!(
|
||||
"{proto}{version}{sni}{nof_c:02}{nof_e:02}{al1}{al2}",
|
||||
proto = if self.quic { "q" } else { "t" },
|
||||
version = JA4::version_to_ja4code(self.tls_version),
|
||||
sni = if self.domain { "d" } else { "i" },
|
||||
nof_c = min(99, self.ciphersuites.len()),
|
||||
nof_e = min(99, self.nof_exts),
|
||||
al1 = self.alpn[0],
|
||||
al2 = self.alpn[1]
|
||||
);
|
||||
|
||||
// Calculate JA4_b
|
||||
let mut sorted_ciphers = self.ciphersuites.to_vec();
|
||||
sorted_ciphers.sort_by(|a, b| u16::from(*a).cmp(&u16::from(*b)));
|
||||
let sorted_cipherstrings: Vec<String> = sorted_ciphers
|
||||
.iter()
|
||||
.map(|v| format!("{:04x}", u16::from(*v)))
|
||||
.collect();
|
||||
let mut sha = Sha256::new();
|
||||
let ja4_b_raw = sorted_cipherstrings.join(",");
|
||||
sha.update(&ja4_b_raw);
|
||||
let mut ja4_b = format!("{:x}", sha.finalize_reset());
|
||||
ja4_b.truncate(12);
|
||||
|
||||
// Calculate JA4_c
|
||||
let mut sorted_exts = self.extensions.to_vec();
|
||||
sorted_exts.sort_by(|a, b| u16::from(*a).cmp(&u16::from(*b)));
|
||||
let sorted_extstrings: Vec<String> = sorted_exts
|
||||
.iter()
|
||||
.map(|v| format!("{:04x}", u16::from(*v)))
|
||||
.collect();
|
||||
let ja4_c1_raw = sorted_extstrings.join(",");
|
||||
let unsorted_sigalgostrings: Vec<String> = self
|
||||
.signature_algorithms
|
||||
.iter()
|
||||
.map(|v| format!("{:04x}", (*v)))
|
||||
.collect();
|
||||
let ja4_c2_raw = unsorted_sigalgostrings.join(",");
|
||||
let ja4_c_raw = format!("{}_{}", ja4_c1_raw, ja4_c2_raw);
|
||||
sha.update(&ja4_c_raw);
|
||||
let mut ja4_c = format!("{:x}", sha.finalize());
|
||||
ja4_c.truncate(12);
|
||||
|
||||
return format!("{}_{}_{}", ja4_a, ja4_b, ja4_c);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn SCJA4New() -> *mut JA4 {
|
||||
let j = Box::new(JA4::new());
|
||||
Box::into_raw(j)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SCJA4SetTLSVersion(j: &mut JA4, version: u16) {
|
||||
j.set_tls_version(TlsVersion(version));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SCJA4AddCipher(j: &mut JA4, cipher: u16) {
|
||||
j.add_cipher_suite(TlsCipherSuiteID(cipher));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SCJA4AddExtension(j: &mut JA4, ext: u16) {
|
||||
j.add_extension(TlsExtensionType(ext));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SCJA4AddSigAlgo(j: &mut JA4, sigalgo: u16) {
|
||||
j.add_signature_algorithm(sigalgo);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SCJA4SetALPN(j: &mut JA4, proto: *const c_char, len: u16) {
|
||||
let b: &[u8] = std::slice::from_raw_parts(proto as *const c_uchar, len as usize);
|
||||
j.set_alpn(b);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SCJA4GetHash(j: &mut JA4, out: &mut [u8; 36]) {
|
||||
let hash = j.get_hash();
|
||||
out[0..36].copy_from_slice(hash.as_bytes());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SCJA4Free(j: &mut JA4) {
|
||||
let ja4: Box<JA4> = Box::from_raw(j);
|
||||
std::mem::drop(ja4);
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "ja4"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_is_grease() {
|
||||
let mut alpn = "foobar".as_bytes();
|
||||
let mut len = alpn.len();
|
||||
let v: u16 = (alpn[0] as u16) << 8 | alpn[len - 1] as u16;
|
||||
assert!(!JA4::is_grease(v));
|
||||
|
||||
alpn = &[0x0a, 0x0a];
|
||||
len = alpn.len();
|
||||
let v: u16 = (alpn[0] as u16) << 8 | alpn[len - 1] as u16;
|
||||
assert!(JA4::is_grease(v));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tlsversion_max() {
|
||||
let mut j = JA4::new();
|
||||
assert_eq!(j.tls_version, None);
|
||||
j.set_tls_version(TlsVersion::Ssl30);
|
||||
assert_eq!(j.tls_version, Some(TlsVersion::Ssl30));
|
||||
j.set_tls_version(TlsVersion::Tls12);
|
||||
assert_eq!(j.tls_version, Some(TlsVersion::Tls12));
|
||||
j.set_tls_version(TlsVersion::Tls10);
|
||||
assert_eq!(j.tls_version, Some(TlsVersion::Tls12));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_hash_limit_numbers() {
|
||||
// Test whether the limitation of the extension and ciphersuite
|
||||
// count to 99 is reflected correctly.
|
||||
let mut j = JA4::new();
|
||||
|
||||
for i in 1..200 {
|
||||
j.add_cipher_suite(TlsCipherSuiteID(i));
|
||||
}
|
||||
for i in 1..200 {
|
||||
j.add_extension(TlsExtensionType(i));
|
||||
}
|
||||
|
||||
let mut s = j.get_hash();
|
||||
s.truncate(10);
|
||||
assert_eq!(s, "t00i999900");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_short_alpn() {
|
||||
let mut j = JA4::new();
|
||||
|
||||
j.set_alpn("a".as_bytes());
|
||||
let mut s = j.get_hash();
|
||||
s.truncate(10);
|
||||
assert_eq!(s, "t00i000000");
|
||||
|
||||
j.set_alpn("aa".as_bytes());
|
||||
let mut s = j.get_hash();
|
||||
s.truncate(10);
|
||||
assert_eq!(s, "t00i0000aa");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_hash() {
|
||||
let mut j = JA4::new();
|
||||
|
||||
// the empty JA4 hash
|
||||
let s = j.get_hash();
|
||||
assert_eq!(s, "t00i000000_e3b0c44298fc_d2e2adf7177b");
|
||||
|
||||
// set TLS version
|
||||
j.set_tls_version(TlsVersion::Tls12);
|
||||
let s = j.get_hash();
|
||||
assert_eq!(s, "t12i000000_e3b0c44298fc_d2e2adf7177b");
|
||||
|
||||
// set QUIC
|
||||
j.set_quic();
|
||||
let s = j.get_hash();
|
||||
assert_eq!(s, "q12i000000_e3b0c44298fc_d2e2adf7177b");
|
||||
|
||||
// set GREASE extension, should be ignored
|
||||
j.add_extension(TlsExtensionType(0x0a0a));
|
||||
let s = j.get_hash();
|
||||
assert_eq!(s, "q12i000000_e3b0c44298fc_d2e2adf7177b");
|
||||
|
||||
// set SNI extension, should only increase count and change i->d
|
||||
j.add_extension(TlsExtensionType(0x0000));
|
||||
let s = j.get_hash();
|
||||
assert_eq!(s, "q12d000100_e3b0c44298fc_d2e2adf7177b");
|
||||
|
||||
// set ALPN extension, should only increase count and set end of JA4_a
|
||||
j.set_alpn(b"h3-16");
|
||||
j.add_extension(TlsExtensionType::ApplicationLayerProtocolNegotiation);
|
||||
let s = j.get_hash();
|
||||
assert_eq!(s, "q12d0002h6_e3b0c44298fc_d2e2adf7177b");
|
||||
|
||||
// set some ciphers
|
||||
j.add_cipher_suite(TlsCipherSuiteID(0x1111));
|
||||
j.add_cipher_suite(TlsCipherSuiteID(0x0a20));
|
||||
j.add_cipher_suite(TlsCipherSuiteID(0xbada));
|
||||
let s = j.get_hash();
|
||||
assert_eq!(s, "q12d0302h6_f500716053f9_d2e2adf7177b");
|
||||
|
||||
// set some extensions and signature algorithms
|
||||
j.add_extension(TlsExtensionType(0xface));
|
||||
j.add_extension(TlsExtensionType(0x0121));
|
||||
j.add_extension(TlsExtensionType(0x1234));
|
||||
j.add_signature_algorithm(0x6666);
|
||||
let s = j.get_hash();
|
||||
assert_eq!(s, "q12d0305h6_f500716053f9_2debc8880bae");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,173 @@
|
||||
/* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Sascha Steinbiss <sascha@steinbiss.name>
|
||||
*
|
||||
* Implements support for ja4.hash keyword.
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "threads.h"
|
||||
#include "decode.h"
|
||||
#include "detect.h"
|
||||
|
||||
#include "detect-parse.h"
|
||||
#include "detect-engine.h"
|
||||
#include "detect-engine-mpm.h"
|
||||
#include "detect-engine-prefilter.h"
|
||||
#include "detect-ja4-hash.h"
|
||||
|
||||
#include "util-ja4.h"
|
||||
|
||||
#include "app-layer-ssl.h"
|
||||
|
||||
#ifndef HAVE_JA4
|
||||
static int DetectJA4SetupNoSupport(DetectEngineCtx *a, Signature *b, const char *c)
|
||||
{
|
||||
SCLogError("no JA4 support built in");
|
||||
return -1;
|
||||
}
|
||||
#endif /* HAVE_JA4 */
|
||||
|
||||
static int DetectJa4HashSetup(DetectEngineCtx *, Signature *, const char *);
|
||||
static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
|
||||
const DetectEngineTransforms *transforms, Flow *f, const uint8_t flow_flags, void *txv,
|
||||
const int list_id);
|
||||
int Ja4IsDisabled(const char *type);
|
||||
static InspectionBuffer *Ja4DetectGetHash(DetectEngineThreadCtx *det_ctx,
|
||||
const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv,
|
||||
const int list_id);
|
||||
|
||||
static int g_ja4_hash_buffer_id = 0;
|
||||
|
||||
/**
|
||||
* \brief Registration function for keyword: ja4.hash
|
||||
*/
|
||||
void DetectJa4HashRegister(void)
|
||||
{
|
||||
sigmatch_table[DETECT_AL_JA4_HASH].name = "ja4.hash";
|
||||
sigmatch_table[DETECT_AL_JA4_HASH].alias = "ja4_hash";
|
||||
sigmatch_table[DETECT_AL_JA4_HASH].desc = "sticky buffer to match the JA4 hash buffer";
|
||||
sigmatch_table[DETECT_AL_JA4_HASH].url = "/rules/ja4-keywords.html#ja4-hash";
|
||||
#ifdef HAVE_JA4
|
||||
sigmatch_table[DETECT_AL_JA4_HASH].Setup = DetectJa4HashSetup;
|
||||
#else /* HAVE_JA4 */
|
||||
sigmatch_table[DETECT_AL_JA4_HASH].Setup = DetectJA4SetupNoSupport;
|
||||
#endif /* HAVE_JA4 */
|
||||
sigmatch_table[DETECT_AL_JA4_HASH].flags |= SIGMATCH_NOOPT;
|
||||
sigmatch_table[DETECT_AL_JA4_HASH].flags |= SIGMATCH_INFO_STICKY_BUFFER;
|
||||
|
||||
#ifdef HAVE_JA4
|
||||
DetectAppLayerInspectEngineRegister("ja4.hash", ALPROTO_TLS, SIG_FLAG_TOSERVER, 0,
|
||||
DetectEngineInspectBufferGeneric, GetData);
|
||||
|
||||
DetectAppLayerMpmRegister(
|
||||
"ja4.hash", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, GetData, ALPROTO_TLS, 0);
|
||||
|
||||
DetectAppLayerMpmRegister("ja4.hash", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister,
|
||||
Ja4DetectGetHash, ALPROTO_QUIC, 1);
|
||||
|
||||
DetectAppLayerInspectEngineRegister("ja4.hash", ALPROTO_QUIC, SIG_FLAG_TOSERVER, 1,
|
||||
DetectEngineInspectBufferGeneric, Ja4DetectGetHash);
|
||||
|
||||
DetectBufferTypeSetDescriptionByName("ja4.hash", "TLS JA4 hash");
|
||||
|
||||
g_ja4_hash_buffer_id = DetectBufferTypeGetByName("ja4.hash");
|
||||
#endif /* HAVE_JA4 */
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief this function setup the ja4.hash modifier keyword used in the rule
|
||||
*
|
||||
* \param de_ctx Pointer to the Detection Engine Context
|
||||
* \param s Pointer to the Signature to which the current keyword belongs
|
||||
* \param str Should hold an empty string always
|
||||
*
|
||||
* \retval 0 On success
|
||||
* \retval -1 On failure
|
||||
*/
|
||||
static int DetectJa4HashSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
|
||||
{
|
||||
if (DetectBufferSetActiveList(de_ctx, s, g_ja4_hash_buffer_id) < 0)
|
||||
return -1;
|
||||
|
||||
if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS && s->alproto != ALPROTO_QUIC) {
|
||||
SCLogError("rule contains conflicting protocols.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* try to enable JA4 */
|
||||
SSLEnableJA4();
|
||||
|
||||
/* check if JA4 enabling had an effect */
|
||||
if (!RunmodeIsUnittests() && !SSLJA4IsEnabled()) {
|
||||
if (!SigMatchSilentErrorEnabled(de_ctx, DETECT_AL_JA4_HASH)) {
|
||||
SCLogError("JA4 support is not enabled");
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
s->init_data->init_flags |= SIG_FLAG_INIT_JA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
|
||||
const DetectEngineTransforms *transforms, Flow *f, const uint8_t flow_flags, void *txv,
|
||||
const int list_id)
|
||||
{
|
||||
InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
|
||||
if (buffer->inspect == NULL) {
|
||||
const SSLState *ssl_state = (SSLState *)f->alstate;
|
||||
|
||||
if (ssl_state->client_connp.ja4 == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t data[JA4_HEX_LEN];
|
||||
SCJA4GetHash(ssl_state->client_connp.ja4, (uint8_t(*)[JA4_HEX_LEN])data);
|
||||
|
||||
InspectionBufferSetup(det_ctx, list_id, buffer, data, 0);
|
||||
InspectionBufferCopy(buffer, data, JA4_HEX_LEN);
|
||||
InspectionBufferApplyTransforms(buffer, transforms);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static InspectionBuffer *Ja4DetectGetHash(DetectEngineThreadCtx *det_ctx,
|
||||
const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv,
|
||||
const int list_id)
|
||||
{
|
||||
InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
|
||||
if (buffer->inspect == NULL) {
|
||||
uint32_t b_len = 0;
|
||||
const uint8_t *b = NULL;
|
||||
|
||||
if (rs_quic_tx_get_ja4(txv, &b, &b_len) != 1)
|
||||
return NULL;
|
||||
if (b == NULL || b_len == 0)
|
||||
return NULL;
|
||||
|
||||
InspectionBufferSetup(det_ctx, list_id, buffer, NULL, 0);
|
||||
InspectionBufferCopy(buffer, (uint8_t *)b, JA4_HEX_LEN);
|
||||
InspectionBufferApplyTransforms(buffer, transforms);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Sascha Steinbiss <sascha@steinbiss.name>
|
||||
*/
|
||||
|
||||
#ifndef __DETECT_JA4_HASH_H__
|
||||
#define __DETECT_JA4_HASH_H__
|
||||
|
||||
/* Prototypes */
|
||||
void DetectJa4HashRegister(void);
|
||||
|
||||
#endif /* __DETECT_JA4_HASH_H__ */
|
||||
@ -0,0 +1,29 @@
|
||||
/* Copyright (C) 2024 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Sascha Steinbiss <sascha@steinbiss.name>
|
||||
*/
|
||||
|
||||
#ifndef SURICATA_UTIL_JA4_H
|
||||
#define SURICATA_UTIL_JA4_H
|
||||
|
||||
#define JA4_HEX_LEN 36
|
||||
|
||||
#endif /* SURICATA_UTIL_JA4_H */
|
||||
Loading…
Reference in New Issue