feat: implement Pillars 2-4 and fix a production false-positive in Pillar 1

Pillar 1 v1.1 (shipped earlier) flagged benign browsing bursts as
beaconing: on short captures, grid periods longer than the observed
span never complete a cycle, so significance saturates to 1 and the
score sticks at exactly 0.5. v1.2 caps the grid at a third of the
observed span, requires a minimum number of completed cycles, and
excludes protocols that are periodic by nature (NTP, DNS, mDNS,
ICMP). Validated against 300 seeds per scenario: false positives on
bursty traffic drop from the 290-300/300 that v1.1 produced to 0/300,
with every beacon-detection scenario scoring unchanged.

Add Pillar 2 (persistent/long-lived sessions, with Zeek's fragmented
TCP sessions rejoined after a 5-minute gap), Pillar 3 (DNS tunneling
via Shannon-entropy information volume per subdomain per hour; the
Jensen-Shannon divergence was measured and does not separate tunnels
from benign CDN subdomains, so it stays in the evidence at zero
weight instead of in the score), and Pillar 4 (deterministic
noisy-OR fusion across the three pillars).

The fusion formula was validated against 8 labeled real-traffic
vectors (IoT-23, CTU-Normal) with tolerance <= 1e-4. A known
limitation is kept, not hidden: a benign Philips HUE bulb's strict
periodic polling and a 21.9h session score it above three real C2
hosts. No weight or threshold was tuned to mask this; it is
documented in the architecture docs instead.

Also fixes real bugs found along the way: ingest_dir's partition
count summed file names instead of distinct dates, DuckDB errors
were counted as bad rows, /api/alerts always returned an empty list
regardless of the last analysis, and the dashboard still claimed
every pillar was unimplemented.

Restores four design corrections that were missing from
docs/architecture.md (KL to Jensen-Shannon, the persistent-session
detector, the JA4 licensing boundary, and deterministic reduction
order), and catalogs the public datasets used for validation
(IoT-23, CTU-Normal, Mendeley DTQBC) with their licenses and SHA-256
checksums under research/validation/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
This commit is contained in:
Lucas RayanandClaude Sonnet 5 committed 2026-11-01 00:28:54 -03:00
1 parent 8a80504c45
commit c64bbbf775
41 files changed
+4034 -348

No files matched your search

+85 -3
View File
@@ -80,24 +80,106 @@ It is bounded in `[-1.0, 1.0]` by definition and resistant to extreme outliers.
When `Q3 == Q1` (concentrated data), the implementation assigns `0.0` with a
sentinel flag.
### 4. Isolate the Python research workspace
### 4. Jensen-Shannon divergence instead of Kullback-Leibler
Kullback-Leibler divergence diverges to infinity when `Q(x) = 0` on non-empty `P(x)`.
Acauã adopts the Jensen-Shannon Divergence (JSD): bounded in `[0, 1]`, symmetric,
and strictly finite even on disjoint supports. Measured in real DNS tunneling and
CDN traffic, JSD alone does not separate benign CDNs from tunnels; hence in v1 it
is recorded in alert evidence with weight 0.0 rather than driving the score.
### 5. Dedicated persistent session detector (Pillar 2)
A long-lived interactive session or steady C2 channel does not generate frequent
reconnections (`Δt`), making it invisible to point-process periodicity analysis.
Acauã implements a dedicated persistent session detector with TCP session chaining
that re-aggregates sessions split by Zeek's 5-minute inactivity timeouts.
### 6. JA4 licensing boundary
Only the original TLS client fingerprint JA4 is BSD-3 licensed. Other modules
(JA4S, JA4H, JA4X, JA4SSH, JA4L) use the proprietary FoxIO License 1.1 (non-OSI).
Acauã does not bundle or depend on FoxIO-licensed modules by default in an
open-source architecture.
### 7. Determinism and fixed reduction order
Parallel reductions (e.g. via `rayon`) reorder floating-point additions and
multiplications across threads, breaking bit-level reproducibility. All reductions,
periodogram summations, and host evidence combinations are computed in a fixed,
deterministic sequential order, producing byte-for-byte identical JSON evidence.
### 8. Isolate the Python research workspace
`research/` has its own virtual environment and dependencies. It is excluded
from the Cargo workspace, ignored by Docker builds and CI releases, and tested
only when research validation scripts are invoked directly.
### 5. Documented degraded mode for NetFlow/IPFIX
### 9. Documented degraded mode for NetFlow/IPFIX
NetFlow v9 and IPFIX do not carry DNS query names, TLS SNI, certificate chains
or HTTP headers. Any analysis that depends on protocol features is unavailable.
Acauã documents this as a degraded mode and never claims parity between flow
records and full Zeek logs.
### 6. Single HTTP and RPC boundary
### 10. Single HTTP and RPC boundary
The daemon exposes an Axum API on loopback by default. The embedded web UI
communicates with the daemon exclusively via standard HTTP calls.
## Detection (v1)
### Pillar 1: Low-and-slow C2 Beaconing (v1.2)
Evaluates session starts per `(orig_h, resp_h, resp_p, proto)` using a Rayleigh/Schuster
periodogram over an effective grid bounded by `span / 3` (requiring at least 3 full cycles
observed). Significance is gated by dispersion regularity (`gate = min(1, regularity / 0.25)`),
preventing false positives on bursty benign browsing. Alert threshold: `score >= 0.40`.
Non-TCP/UDP protocols, multicast/broadcast addresses, and infrastructure service ports
(e.g., NTP 123, DNS 53, mDNS 5353, NetBIOS, DHCP) are audited and excluded.
### Pillar 2: Persistent Sessions (v1.0)
Chains TCP connection segments across Zeek inactivity timeouts (gap <= 3600s with no `S`
SYN flag in Zeek history). Persistence score is logarithmic:
`persistence = clamp(ln(L / 3600) / ln(24), 0, 1)`. Sessions with `L >= 7200s`
(`score >= 0.2181`) trigger an alert. Producer-Consumer Ratio (PCR), byte transfer rates,
and failed connection attempts are captured in evidence.
### Pillar 3: DNS Subdomain Information Capacity (v1.0)
Measures the information payload capacity pushed across DNS queries by aggregating unique subdomains
per `(orig_h, base_domain, utc_hour)`. Normalizes queries to lowercase, strips trailing dots, and excludes
reverse lookups (`.in-addr.arpa`, `.ip6.arpa`), `.local`, and discovery ports (5353, 5355, 137):
- Information capacity: $I = H \times C$ bits, where $H = -\sum p_c \log_2 p_c$ (Shannon entropy per char)
and $C$ is the sum of characters across unique subdomains.
- Evaluated at peak hour: $I_{\max} = \max_h I$. Requires $U_{\text{peak}} \ge 10$ unique subdomains;
otherwise status is `insufficient_data` with score 0.0.
- Scoring: $\text{score} = \text{clamp}(\log_2(I_{\max} / 8192) / \log_2(32), 0, 1)$ ($8\text{ kbit} \to 0.0$,
$32\text{ kbit} \to 0.40$, $256\text{ kbit} \to 1.0$).
- Alert threshold: $\text{score} \ge 0.40$ ($\iff I_{\max} \ge 32768$ bits/h).
- Jensen-Shannon Divergence (JSD) against the mixture of other active domains in the window is stored in
evidence with weight 0.0 (tunnels measured 0.157–0.193 vs. benign CDNs 0.226–0.227, inverting rank).
### Pillar 4: Per-Host Evidence Fusion (v1.0)
Combines alerts per `orig_h` using a weighted noisy-OR model across pillars 1, 2, and 3:
- For each host, $m_i$ is the maximum alert score from pillar $i$ ($0.0$ if no alert exists).
- Fixed weights $w_1 = w_2 = w_3 = 0.75$.
- Score formula: $S = 1 - \prod_{i=1}^3 (1 - w_i m_i)$ evaluated in fixed sequential order $[1, 2, 3]$.
- Properties: $S \in [0, 1]$, monotonic in every $m_i$, single pillar capped at $0.75$, two pillars capped at
$0.9375$, three pillars capped at $0.984375$. Invariant under alert input permutations.
- Fusion ranks hosts in `AnalysisReport.hosts` by $(S \text{ desc}, orig\_h \text{ asc})$; it provides investigation
prioritization without fabricating synthetic alert classes.
### Known Limitations
1. In live captures, open sessions remain unobserved until Zeek flushes them on closure or timeout.
2. Benign cloud polling and persistent services (e.g., smart home devices like Philips HUE, websockets, push notifications) exhibit genuine periodicity and persistence, ranking high in fused scores without network-wide context.
3. Pillar 3 tunnel detection sensitivity is approximately 100 unique queries per hour.
4. Base domain extraction uses an effective heuristic without a full embedded Public Suffix List (PSL).
### Validation Datasets
Public datasets used for empirical verification (detailed in `research/validation/datasets.tsv`):
- **IoT-23** (captures 34-1, 20-1, 8-1, 42-1, Honeypot 4-1): CC BY 2.0 ground truth for C2 beaconing, persistence, and benign IoT controls.
- **CTU-Normal** (captures 20, 18, 26): public captures representing benign browsing and Facebook traffic.
- **Mendeley dtqbc-m-test** (Y. Bubnov): CC BY 4.0 dataset with real DNS tunneling payloads (dns2tcp, dnscapy, tuns).
## API
The HTTP API is served by `acaua serve`:
+15 -2
View File
@@ -39,8 +39,21 @@ acaua analyze /var/log/zeek/current
acaua analyze /var/log/zeek/current --json
```
Validates the target directory, ingests `conn.log` traffic into Parquet partitions,
and runs threat detection algorithms.
Validates the target directory, ingests `conn.log` and `dns.log` traffic into Parquet partitions,
and runs threat detection algorithms across pillars 1–4. The human-readable output reports summary metrics,
individual alerts, and the `hosts:` ranking section (top 10 by priority score):
```text
diretório: /var/log/zeek/current
logs lidos: 2
registros: 10450
alertas: 2
[0.850] Beaconing detected: 192.168.1.50:any -> 198.51.100.1:443 (period ~14400s, score 0.850)
[0.600] Persistent session: 192.168.1.50 -> 198.51.100.1:443 (6.0h, score 0.600)
hosts:
[0.784] 192.168.1.50 0.850/0.600/0.000
estado: ok
```
## `acaua doctor`
+87 -3
View File
@@ -79,24 +79,108 @@ Ela é limitada em `[-1.0, 1.0]` por definição e resistente a outliers extremo
Quando `Q3 == Q1` (dados concentrados), a implementação atribui `0.0` com flag
sentinela.
### 4. Isolar o espaço de pesquisa Python
### 4. Divergência de Jensen-Shannon em vez de Kullback-Leibler
A divergência de Kullback-Leibler diverge para infinito quando `Q(x) = 0` sobre
suporte não-vazio de `P(x)`. O Acauã adota a Divergência de Jensen-Shannon (JSD):
limitada em `[0, 1]`, simétrica e estritamente finita mesmo em suportes disjuntos.
Medições empíricas em tráfego de túnel DNS real e CDNs benignas demonstraram que a
JSD isolada não separa CDNs de túneis; assim, na v1 ela entra na evidência do
alerta com peso 0.0 sem distorcer o score.
### 5. Detector dedicado de sessões persistentes (Pilar 2)
Sessões interativas de longa duração ou canais C2 estáveis não geram reconexões
frequentes (`Δt`), tornando-se invisíveis para a análise pontual de periodicidade.
O Acauã implementa um detector dedicado de sessões persistentes com encadeamento
TCP que reagrupa segmentos divididos pelo timeout de inatividade de 5 minutos do Zeek.
### 6. Fronteira de licenciamento do JA4
Apenas a impressão digital original de cliente TLS JA4 está sob licença BSD-3.
Os outros módulos (JA4S, JA4H, JA4X, JA4SSH, JA4L) utilizam a FoxIO License 1.1
proprietária (não-OSI). O Acauã não embute nem depende de módulos sob licença FoxIO
por padrão em sua arquitetura de código aberto.
### 7. Determinismo e ordem fixa de redução
Reduções paralelas (ex.: via `rayon`) alteram a ordem de somas e multiplicações
em ponto flutuante entre threads, quebrando a reprodutibilidade bit a bit. Todas as
reduções, somas de periodograma e combinações de evidência são executadas em ordem
sequencial fixa e determinística, produzindo evidências JSON idênticas byte a byte.
### 8. Isolar o espaço de pesquisa Python
`research/` tem seu próprio ambiente virtual e dependências. Ele é excluído do
workspace Cargo, ignorado em builds Docker e releases de CI, e testado apenas
quando scripts de validação de pesquisa forem invocados diretamente.
### 5. Modo degradado documentado para NetFlow/IPFIX
### 9. Modo degradado documentado para NetFlow/IPFIX
NetFlow v9 e IPFIX não carregam nomes de consulta DNS, SNI TLS, cadeias de
certificados ou cabeçalhos HTTP. Qualquer análise que dependa de recursos de
protocolo fica indisponível. O Acauã documenta isso como modo degradado e nunca
alega paridade entre registros de fluxo e logs Zeek completos.
### 6. Fronteira única HTTP e RPC
### 10. Fronteira única HTTP e RPC
O daemon expõe uma API Axum em loopback por padrão. A UI web embutida se
comunica com o daemon exclusivamente por chamadas HTTP padrão.
## Detecção (v1)
### Pilar 1: Beaconing C2 Low-and-Slow (v1.2)
Avalia inícios de sessão por `(orig_h, resp_h, resp_p, proto)` através de um periodograma
de Rayleigh/Schuster sobre uma grade efetiva limitada a `span / 3` (exigindo no mínimo 3
ciclos completos observados). A significância é controlada pela regularidade da dispersão
(`gate = min(1, regularidade / 0.25)`), eliminando falsos positivos em tráfego de navegação
em rajadas. Limiar de alerta: `score >= 0.40`. Protocolos não-TCP/UDP, endereços multicast
e broadcast, e portas de infraestrutura (ex.: NTP 123, DNS 53, mDNS 5353, NetBIOS, DHCP)
são auditados e excluídos com transparência.
### Pilar 2: Sessões Persistentes (v1.0)
Encadeia segmentos de conexão TCP divididos por inatividade no Zeek (intervalo <= 3600s
sem flag SYN `S` no histórico). O score de persistência é logarítmico:
`persistence = clamp(ln(L / 3600) / ln(24), 0, 1)`. Sessões com `L >= 7200s`
(`score >= 0.2181`) disparam alerta. A razão produtor-consumidor (PCR), taxa de bytes
por hora e contagem de tentativas falhas são registradas na evidência.
### Pilar 3: Capacidade de Informação em Subdomínios DNS (v1.0)
Mede a capacidade física de informação transmitida por consultas DNS agregando subdomínios únicos
por `(orig_h, base_domain, hora_utc)`. Normaliza para minúsculas, remove ponto final e exclui consultas
reversas (`.in-addr.arpa`, `.ip6.arpa`), `.local` e portas de descoberta local (5353, 5355, 137):
- Capacidade de informação: $I = H \times C$ bits, onde $H = -\sum p_c \log_2 p_c$ (entropia de Shannon por caractere)
e $C$ é a soma dos caracteres dos subdomínios únicos sem os pontos.
- Avaliação na hora de pico: $I_{\max} = \max_h I$. Exige $U_{\text{pico}} \ge 10$ subdomínios únicos; caso
contrário, o status é `insufficient_data` com score 0.0.
- Pontuação: $\text{score} = \text{clamp}(\log_2(I_{\max} / 8192) / \log_2(32), 0, 1)$ ($8\text{ kbit} \to 0.0$,
$32\text{ kbit} \to 0.40$, $256\text{ kbit} \to 1.0$).
- Limiar de alerta: $\text{score} \ge 0.40$ ($\iff I_{\max} \ge 32768$ bits/h).
- A Divergência Jensen-Shannon (JSD) contra a mistura de outros domínios da janela entra na evidência com peso 0.0
(túneis reais deram 0.157–0.193 vs. CDNs benignas 0.226–0.227, o que inverteria a classificação).
### Pilar 4: Fusão de Evidências por Host (v1.0)
Combina alertas por `orig_h` usando um modelo noisy-OR ponderado sobre os pilares 1, 2 e 3:
- Para cada host, $m_i$ é a pontuação máxima de alerta do pilar $i$ ($0.0$ caso não haja alertas do pilar).
- Pesos fixos: $w_1 = w_2 = w_3 = 0.75$.
- Fórmula: $S = 1 - \prod_{i=1}^3 (1 - w_i m_i)$ calculado na ordem sequencial fixa $[1, 2, 3]$.
- Propriedades: $S \in [0, 1]$, monotônica em cada $m_i$, um pilar isolado atinge no máximo $0.75$, dois pilares
$0.9375$ e três pilares $0.984375$. Invariante à permutação dos alertas de entrada.
- A fusão classifica os hosts em `AnalysisReport.hosts` por $(S \text{ desc}, orig\_h \text{ asc})$; fornece priorização
de investigação sem fabricar categorias sintéticas de alerta.
### Limitações Conhecidas
1. Em capturas ao vivo, sessões em aberto permanecem invisíveis até que o Zeek descarregue o registro por encerramento ou timeout.
2. Polling legítimo de nuvem e serviços persistentes (ex.: dispositivos IoT como lâmpada Philips HUE, websockets, push) exibem periodicidade e persistência reais, ficando com pontuação fundida elevada sem contexto global da rede.
3. A sensibilidade do Pilar 3 é de cerca de 100 consultas de túnel únicas por hora.
4. A extração de domínio-base usa heurística eficiente sem embutir a lista completa da Public Suffix List (PSL).
### Dados de Validação
Datasets públicos utilizados para verificação empírica (detalhados em `research/validation/datasets.tsv`):
- **IoT-23** (cenários 34-1, 20-1, 8-1, 42-1, Honeypot 4-1): verdade de referência CC BY 2.0 para beaconing C2, persistência e controles benignos IoT.
- **CTU-Normal** (capturas 20, 18, 26): capturas públicas de tráfego benigno de navegação e Facebook.
- **Mendeley dtqbc-m-test** (Y. Bubnov): dataset sob CC BY 4.0 contendo consultas reais de túneis DNS (dns2tcp, dnscapy, tuns).
## API
A API HTTP é servida por `acaua serve`:
+15 -2
View File
@@ -39,8 +39,21 @@ acaua analyze /var/log/zeek/current
acaua analyze /var/log/zeek/current --json
```
Valida o diretório alvo, ingere tráfego do `conn.log` em partições Parquet e
executa os algoritmos de detecção de ameaças.
Valida o diretório alvo, ingere tráfego do `conn.log` e `dns.log` em partições Parquet e
executa os algoritmos de detecção de ameaças dos pilares 1 a 4. A saída legível para humanos relata métricas de sumário,
alertas individuais e a seção de classificação `hosts:` (top 10 por score de prioridade):
```text
diretório: /var/log/zeek/current
logs lidos: 2
registros: 10450
alertas: 2
[0.850] Beaconing detected: 192.168.1.50:any -> 198.51.100.1:443 (period ~14400s, score 0.850)
[0.600] Persistent session: 192.168.1.50 -> 198.51.100.1:443 (6.0h, score 0.600)
hosts:
[0.784] 192.168.1.50 0.850/0.600/0.000
estado: ok
```
## `acaua doctor`