detect.ss: faithful label-level DGA dedup via dga-label kernel
ober
d75d25b3d3ee12101f10690ff01f35a90b851e82
--- a/jsecmon/detect.ss +++ b/jsecmon/detect.ss @@ -29,7 +29,7 @@ make-date make-time) (except (jerboa prelude) meta atom?) (only (jsecmon kernels) - lolbin-score-cmdline lolbin-severity dga-score-domain)) + lolbin-score-cmdline lolbin-severity dga-score-domain dga-label)) (def lolbin-threshold 50) (def dga-threshold 60) @@ -82,9 +82,11 @@ events)) ;; ── rule: DGA / high-entropy DNS query ────────────────────────────────────── - ;; One alert per suspect domain. secmon dedups on (host, process_name, winning - ;; label); we key on the full query_name (the kernel returns the max score, not - ;; which label won) — equivalent for distinct domains, finer-grained otherwise. + ;; One alert per suspect domain, deduped on (host, process_name, winning label) + ;; like secmon's HashMap::or_insert — so a host querying many subdomains of one + ;; DGA label (the leftmost label `dga-label` returns) alerts once. The faithful + ;; storage-Rule port with the full reason breakdown is (jsecmon dga); this is + ;; the live event-pipeline shape feeding (jsecmon analytics). (def (detect-dga-domain events) (let ((seen (make-hash-table)) (out '())) @@ -92,20 +94,21 @@ (lambda (ev) (when (string=? (a-str ev "event_type") "dns_query") (let ((qname (a-str (ev-data ev) "query_name"))) - (when (and (not (string=? qname "")) - (>= (dga-score-domain qname) dga-threshold)) - (let* ((host (a-str ev "host")) - (pname (or (ev-pname ev) "")) - (k (string-append host "\x1f;" pname "\x1f;" qname))) - (unless (hash-get seen k) - (let ((a (make-anomaly "dga_domain" host "high" - (a-num ev "timestamp_ms" 0) - (details-hash "process_name" (if (string=? pname "") "?" pname) - "pid" (hash-get ev "pid") - "query_name" qname - "score" (dga-score-domain qname))))) - (hash-put! seen k a) - (set! out (cons a out))))))))) + (unless (string=? qname "") + (let ((score (dga-score-domain qname))) + (when (>= score dga-threshold) + (let* ((host (a-str ev "host")) + (pname (or (ev-pname ev) "")) + (k (string-append host "\x1f;" pname "\x1f;" (dga-label qname)))) + (unless (hash-get seen k) + (let ((a (make-anomaly "dga_domain" host "high" + (a-num ev "timestamp_ms" 0) + (details-hash "process_name" (if (string=? pname "") "?" pname) + "pid" (hash-get ev "pid") + "query_name" qname + "score" score)))) + (hash-put! seen k a) + (set! out (cons a out))))))))))) events) (reverse out)))