Home Services obserae About Blog Contact us

Votre matrice de flux ne voit pas tout : pourquoi il faut une couche comportementale

Your Flow Matrix Doesn't See Everything: Why You Need a Behavioral Layer

Back to blog

First article in a series on behavioral and statistical detection in obserae. Coming up in future installments: robust statistics, seasonality, entropy, first-seen, and the detection of beaconing, scans, and exfiltration — and how it all comes together into a per-host score. Today, we frame the problem.


obserae rests on a simple, deliberate idea: rather than magically learning what is normal on your network, we ask you to declare it. You name your networks, hosts, groups, and services in the Cartography; you describe the expected communications in the Flow Matrix; and every observed session is checked against that model. Whatever doesn't fit bubbles up.

This declarative approach works remarkably well — and we'll start by saying so, because the rest of this article might suggest otherwise. But it has blind spots. Three attack scenarios, all three mundane, pass straight through it without triggering anything. This series exists because we want to close them, and we want to do it with methods you can understand, audit, and tune — not with a black box.

What the Flow Matrix Does Very Well

Let's start by giving the model its due. When you import your cartography and describe your legitimate flows — backends → postgres:5432, office → proxy:3128, bastion → production:ssh — you get three things most NDRs on the market don't offer:

An exact model, not an inference. The flow matrix isn't a statistical approximation of your architecture: it is your architecture. When a session matches no rule, it isn't "87% abnormal" — it is outside the model you declared, full stop. The Unmatched only filter on the Sessions page is probably the best signal-to-noise view in the whole product: every row is a conversation nobody planned for.

A continuous improvement loop. Uncovered traffic forces you to decide: legitimate → add it to the matrix; suspicious → investigate. Week after week, the matrix converges toward a faithful and living description of your network. It's a baseline in the truest sense — but a baseline you validated line by line, not one learned while, perhaps, an attacker was already inside.

Explicit policy violations. A workstation talking directly to a server in the data zone, a flow bypassing the bastion, PostgreSQL outside the data network: all detections that require no statistics, just traffic checked against intent.

If you're not yet using this mechanism to its full extent, stop reading here and go spend an hour on Unmatched only. The rest of this series can wait; that hour can't.

Three Attacks Your Matrix Watches Go By

The problem is that the matrix answers a single question: is this flow allowed? But mature attacks don't run inside forbidden flows. They run inside your allowed flows. Here are three scenarios — three classics, nothing exotic — seen through the matrix's eyes.

Scenario 1: The C2 Beacon on Port 443

A workstation is compromised by an implant — phishing, an attachment, whatever. The implant does what every modern implant does: it connects periodically to its command-and-control server, over HTTPS, to fetch its instructions.

On the obserae side, here's what that looks like:

client 10.10.42.31:52144  ->  server 203.0.113.50:443  TCP
duration 2s, 1.2 KB client->server, 0.9 KB server->client
matched rule: office-to-internet-https

Short session, tiny volume, port 443, rule matched. This session is indistinguishable from a website visit — and your matrix necessarily contains a rule allowing your workstations to reach the internet over HTTPS, otherwise nobody gets any work done. The implant will reconnect in five minutes. Then in five minutes. Then in five minutes. For weeks.

What the matrix doesn't see: it isn't one session that's suspicious, it's the series. A human browsing produces irregular connections, in bursts, during office hours. An implant produces a metronome. The difference is only visible if you look at the rhythm — and rhythm can be measured.

Scenario 2: Exfiltration Through a Legitimate Flow

Your database server is allowed to talk to the backup server — obviously, that's how backups work. The rule exists, it's clean, it's even tagged critical.

One evening, an attacker who has gained a foothold on the backup server uses it as a collection point: they siphon off the database, reusing exactly that channel.

client 10.0.10.12:48812  ->  server 10.0.20.5:9102  TCP
duration 41m, 380 GB client->server
matched rule: databases-to-backup

380 gigabytes where the daily incremental backup moves 4. The session is perfectly compliant: right endpoints, right port, right direction. The matrix has nothing to object to — it constrains neither volume, nor timing, nor frequency. What nobody declared, because it isn't declarable in a connectivity model, is the usual order of magnitude of this flow.

Scenario 3: The Compromised Account That Does Everything by the Book

An administrator's credentials leak. The attacker connects to the bastion, then from the bastion to production, over SSH. In other words: they follow exactly the path your architecture prescribes, the one you carefully codified in the matrix.

client 10.0.5.2:60318  ->  server 10.0.30.14:22  TCP
duration 2h 12m
matched rule: bastion-to-prod-ssh

On a Saturday, at 3:17 a.m. Your admin team works weekdays, between 8 a.m. and 7 p.m., and the entire session history proves it. But the matrix knows nothing about schedules. To it, a bastion→prod SSH session is as legitimate on Tuesday at 10 a.m. as on Saturday at 3 a.m. — it's the same rule that matches.

Allowed Isn't Normal

These three scenarios share one trait: the attacker doesn't violate your connectivity policy, they abuse it. And they can do so because a session has more dimensions than the ones a connectivity rule constrains.

A Flow Matrix rule fixes who talks to whom, over which service. It leaves at least four dimensions free:

  • volume — how many bytes, in which direction (scenario 2);
  • rhythm — how often, how regularly (scenario 1);
  • timing — what time of day, of the week (scenario 3);
  • spread — toward how many distinct destinations, over how many ports (the host that suddenly starts contacting fifty internal machines in ten minutes: we'll come back to this in the article on scans and lateral movement).

It's precisely on these four dimensions that behavioral detection plays out. Not instead of the matrix, but after it: the matrix eliminates the structurally unexpected; the behavioral layer watches what remains — that is, the bulk of your traffic — along the dimensions the matrix doesn't constrain.

Allowed ≠ normal. The whole series fits inside that sign.

Two Schools of NDR

How does the market tackle this problem? At the risk of barely oversimplifying, two philosophies split the field.

Learning what's normal. You build, for each machine, a profile of its usual behavior — who it talks to, when, how much — and you alert on deviations. This is the baseline school: it excels at detecting what you weren't looking for, including novel attacks. Its Achilles' heel: defining "normal" on a living network is hard, and everything that legitimately changes (a new service, a migration, a seasonal spike) looks like an anomaly at first. Poorly tuned, this approach produces noise; and noise, in detection, kills.

Recognizing attacker behaviors. Conversely, you don't model your network: you model the adversary. A C2 beacon has a statistical signature (temporal regularity, constant sizes) that doesn't depend on your environment. A scan has its own. So does exfiltration. You build one specialized detector per behavior, each with its own metrics. Upside: few false positives, verdicts that carry a name ("beaconing," not "anomaly"). Limitation: you only detect what you've explicitly modeled.

Good NDRs do both, and that's our plan for obserae. The first part of this series covers the "baseline" foundations — the statistical tools to measure normal and spot the deviation. The second part builds, on top of that, named attacker-behavior detectors. Both parts converge at the end on a synthesis question: how to aggregate all these signals per machine, so your alert queue stays triageable by a human.

Why We Start With Statistics, Not Machine Learning

A word on method, because the topic is saturated with marketing. Nearly every NDR on the market presents itself as "AI-driven." Under the hood, a large share of the detections that actually work — the ones that hold up in production, with a bearable false-positive rate — are well-applied robust statistics: medians, absolute deviations, regularity scores, novelty counts. This isn't a criticism; it's good news. It means most of the value is reachable with methods that are:

Explainable. When obserae alerts you, the alert will say "this flow is 14 times the median of the last thirty Tuesdays at this hour," or "regularity score 0.93 over 240 connections." Not "the model produced 0.87." You'll be able to check the math, challenge the threshold, understand the false positive. For a product that claims to provide auditable evidence — that's our NIS2/DORA/SOC 2 positioning — explainability isn't a luxury, it's the contract.

Local and frugal. obserae runs on your premises, on a single machine, without sending your traffic data to a cloud. That will stay true. The methods we choose have to hold to that constraint: incremental computation, bounded memory footprint, no GPU, no training on external datasets. You'll see throughout the series that this is entirely doable — and that a few clever data structures work wonders.

Tunable without a PhD. A threshold expressed in "robust deviations from the median" can be tuned. A neural network can only be endured.

Heavier machine learning isn't ruled out forever — there are places where it will add something, and we'll talk about it honestly when the time comes. But it will come last, as one detector among others, never as an oracle.

The Series Roadmap

Here's where we're headed, article by article:

Part 1 — The statistical foundations. Median, MAD, and the robust z-score: detecting volume spikes without being fooled by your own data. Then seasonality: why 3 a.m. isn't judged like 2 p.m. Shannon entropy: one formula, three detections. First-seen: novelty as a signal — the most cost-effective detector of them all, and it's already partly in obserae. Finally, the product translation: an Anomaly alert condition alongside Presence, Threshold, First seen, and Heartbeat, plus new statistical functions in NFQL.

Part 2 — The behavioral detectors. C2 beaconing and the algorithm that flushes it out (the one from the open-source tool RITA — we'll walk through it calculation by calculation). Scans and reconnaissance, which obserae's sessionizer already materializes through unanswered sessions. Entity profiles and peer comparison — where your cartography becomes a decisive advantage: comparing a workstation to the other workstations is trivial when roles are declared. Exfiltration and data hoarding. External context (ASN, GeoIP, Tor, cloud) turned from a decorative badge into a detection feature. And finally, scoring by evidence accumulation: how three weak signals on the same machine become one strong alert — and how your triage queue stops overflowing.

Every article will follow the same contract: the algorithm explained for real, what it detects (and what it misses — we'll keep the "limitations" column with as much care as the "promises" column), and at least one NFQL query you can run on your own sessions, today, in the Investigation page.

Until the Next Episode

Two pieces of homework, if the topic speaks to you.

The first: open the Sessions page, enable Unmatched only over the last 24 hours, and look. It's the declarative version of everything this series will build, and it's available now.

The second: run this query in Investigation, which gives a first glimpse of the "volume" dimension of your outbound flows — the playground of the next article:

FROM sessions
  | LAST 86400
  | WHERE server_ip == "internet4"
  | STATS out = SUM(client_to_server_bytes), sessions = COUNT(*) BY client_ip
  | SORT out DESC
  | LIMIT 20

Your twenty biggest emitters toward the public internet, over the last day. The question that follows — "and is that normal?" — is exactly the one the next article will teach you to answer with something other than intuition: median, MAD, and a z-score that won't let itself be contaminated by the very anomalies it's meant to detect.

See you soon.


obserae is a self-hosted NDR built on NetFlow and IPFIX: your traffic data stays with you. Install it with Docker and follow the quickstart — ten minutes of your own traffic is enough to see the first unexpected conversations.

See what your flow matrix misses

obserae is a self-hosted NDR that turns your NetFlow and IPFIX into auditable evidence — deployed in ten minutes, your data never leaves your infrastructure.

Discover obserae Back to blog