Algorithms
Detection algorithms used across the five layers
L2: Shannon Entropy
Shannon entropy measures the randomness of a string. It is the primary signal used to detect DNS tunneling and DGA domains: legitimate domain names have low entropy (human-readable words), while encoded payloads have high entropy.
Formula
Where is the probability of each character appearing in the string.
Interpretation for DNS
| Entropy range | Likely meaning |
|---|---|
| 0.0 – 2.5 | Normal domain (google, github) |
| 2.5 – 3.5 | Watch: could be a CDN hash or legit random subdomain |
| > 3.5 | High suspicion, characteristic of Base32/Base64 encoded tunnel data |
Reference implementation (Rust)
pub fn shannon_entropy(s: &str) -> f64 {
let len = s.len() as f64;
let mut freq = [0u32; 256];
for b in s.bytes() {
freq[b as usize] += 1;
}
freq.iter()
.filter(|&&c| c > 0)
.map(|&c| {
let p = c as f64 / len;
-p * p.log2()
})
.sum()
}Sources
- Shannon, C.E. (1948). A Mathematical Theory of Communication. Bell System Technical Journal.
L2: N-gram Language Model (DGA Detection)
Domain Generation Algorithms (DGA) produce pseudo-random domain names that look nothing like human language. An n-gram model trained on legitimate domains scores a query domain against known linguistic patterns; low score = likely DGA.
Approach
- Build a bigram/trigram frequency table from a corpus of legitimate domains (Alexa Top 1M)
- Score each query:
- Low log-probability → DGA candidate
Thresholds
A log-probability below -15 on trigrams is commonly used as a DGA threshold in production systems (Bambenek, 2015).
Sources
- Antonakakis, M. et al. (2012). From Throw-Away Traffic to Bots: Detecting the Rise of DGA-Based Malware. USENIX Security.
- Plohmann, D. et al. (2016). A Comprehensive Measurement Study of Domain Generating Malware. USENIX Security.
L3: Condition Evaluator
The signature engine evaluates AND-combined conditions against parsed packet fields. Each condition is a triple (field, operator, value).
Operator set
| Operator | Semantic | Implementation |
|---|---|---|
eq / neq | Equality | String or numeric comparison |
gt / lt | Numeric ordering | Parse value as f64, compare |
contains | Substring match | str::contains() |
regex | Pattern match | Compiled Regex from the regex crate, cached at rule load |
Performance note: regex patterns are compiled once when the YAML rule is loaded and cached. Never compile a regex inside the hot path.
L4: Z-score Anomaly Detection
The behavioral layer establishes a baseline (mean and standard deviation) for traffic metrics over a learning window, then flags deviations that exceed a configured sigma threshold.
Formula
- : observed metric value (packets/s, bytes/s, DNS queries/min...)
- : rolling mean over the baseline window
- : rolling standard deviation
A |z| > 3 is the standard 3-sigma rule (99.7% of normal traffic falls within ±3σ).
Sources
- Grubbs, F.E. (1969). Procedures for Detecting Outlying Observations in Samples. Technometrics.
L4: CUSUM (Cumulative Sum Control Chart)
Z-score reacts to point anomalies. CUSUM detects sustained trends: a slow, gradual increase in DNS query rate that individually never crosses the z-score threshold but collectively represents an exfiltration campaign.
Formula
- : target mean (baseline)
- : slack parameter (typically )
- Alert when (decision threshold, typically )
Why both Z-score and CUSUM?
Sources
- Page, E.S. (1954). Continuous Inspection Schemes. Biometrika.
- Basseville, M. & Nikiforov, I. (1993). Detection of Abrupt Changes. Prentice-Hall.
L4: Exponential Moving Average (Baseline)
EMA is used to maintain a lightweight, continuously-updated traffic baseline without storing a full history window.
Formula
- : smoothing factor (0.1–0.3 typical for slow-moving baselines)
- Low → slow to adapt, resistant to sudden spikes
- High → adapts quickly, more reactive
Implementation
pub struct EmaBaseline {
value: f64,
alpha: f64,
}
impl EmaBaseline {
pub fn update(&mut self, sample: f64) -> f64 {
self.value = self.alpha * sample + (1.0 - self.alpha) * self.value;
self.value
}
}L2: JA3 / JA3S Fingerprinting (TLS)
JA3 produces an MD5 hash from specific fields of the TLS ClientHello message. The same client software (and the same malware family) always produces the same JA3 hash, regardless of destination. This allows detection of known malicious TLS clients without decrypting any content.
Fields hashed (ClientHello)
TLS version + Cipher suites + Extensions + Elliptic curves + EC point formatsAll concatenated and MD5-hashed → 32-character hex fingerprint.
JA3S is the server-side equivalent: hashes ServerHello fields to fingerprint the server's TLS stack.
Example
| JA3 Hash | Known client |
|---|---|
769,47-53-5-10-49161-... → a0e9f5d64349fb13191bc781f81f42e1 | Golang net/http default |
771,49195-49199-49196-... → e7d705a3286e19ea42f587b344ee6865 | Cobalt Strike Beacon |
Sources
- Althouse, J., Atkins, J., Atkinson, J. (2017). TLS Fingerprinting with JA3 and JA3S. Salesforce Engineering Blog.
- github.com/salesforce/ja3
L5: Confidence Score Aggregation
The correlation engine aggregates evidence from L3 and L4 into a single confidence score using a weighted combination, then applies temporal decay to avoid stale evidence inflating scores.
Aggregation
- : evidence score from each layer event (0.0–1.0)
- : weight for that layer (L3 signature match = 0.6, L4 anomaly = 0.4 by default)
- Capped at 1.0
Temporal decay
Evidence older than the sliding window (default: 60 seconds) is discounted:
- : decay rate (configurable)
- : age of the event in seconds
Severity mapping
| Score | Severity |
|---|---|
| 0.0 – 0.4 | Low |
| 0.4 – 0.65 | Medium |
| 0.65 – 0.85 | High |
| 0.85 – 1.0 | Critical |