Security Probes

Security probes

Axiom Border assesses the network it sits on with four probes. They share one execution model, one audit trail and one accumulated view of results, so the findings of each one reinforce the others instead of living in separate reports.

Probe What it answers Requires
Network discovery What is out there, and what is it listening on? Nothing beyond a target range
Vulnerability scanning Which of those services are vulnerable? Nothing — the checks ship with the product
SNMP What does the device say about itself? A reachable SNMP agent and credentials
Passive analysis What is actually crossing the wire? A capture interface

Each runs two ways: automatically on a schedule you configure, and manually from the console when you want an answer now.

flowchart TB
    DISC["Network discovery<br><i>live hosts and ports</i>"]
    DISC --> VULN["Vulnerabilities"]
    DISC --> SNMP["SNMP identity"]
    PASS["Passive analysis"] --> INV
    VULN --> INV
    SNMP --> INV
    DISC --> INV[("Consolidated<br>inventory")]
    INV --> ALARM["Alarms and<br>audit trail"]

Network discovery establishes the baseline for the two probes that ask questions, and that ordering matters: a vulnerability scan with no explicit target scans every host in the baseline, so discovery must have run for it to have anything to do. Newly discovered hosts also trigger a vulnerability scan on their own, grouped together after a short delay, so new assets get assessed without waiting for the next scheduled sweep.

Passive analysis stands apart — it needs no baseline, because it is not asking anyone anything. See Passive and active for why the two must not be read as the same kind of result.

Network status — where all of it lands

Network status, listing discovered hosts with their type, manufacturer, ports and vulnerabilities

The header names the last assessment that ran and how it finished, so you can tell at a glance whether what you are looking at is current. Three buttons launch and configure the probes:

Button What it does
Scan network Runs a discovery scan now, against a target you choose
Passive analysis Opens what the listening probe has worked out, with its evidence
Automatic scans Jumps to the schedules for all of them, in Configuration

Each host row carries its own actions — Ports, Vulnerabilities and SNMP — each opening that host’s detail and each able to re-scan just that host, so you can reassess one asset without sweeping the whole range.

Network discovery and port scanning

Scan network opens the launch dialog. Every option the probe supports is here, so there is no reason to drop to the API for a scan:

The Launch network scan dialog
Field What to know
Target Whole network uses the configured range; Custom IP scans a single address
Ports Leave empty and it scans all 65,535 per host. On a /24 at cautious timing, that is a long scan — narrow it
Timing Five presets from cautious to flat out, each with a one-line description of what it trades away
Timeout Raise it whenever you widen the target or the port range
Scan ports · UDP · Service detection · OS detection UDP is slow — restrict the port list rather than sweeping. OS detection needs the probe to run privileged
A port list worth copying for industrial networks

1-1024,502,2404,20000,47808 covers the common IT range plus the Modbus, IEC-104, DNP3 and BACnet ports, so industrial assets show up in the baseline the vulnerability probe later works from.

One discovery scan at a time

Launching a second while one is running is refused. Forcing it cancels the running scan and marks it failed with the reason, rather than letting it look as though it completed.

What discovery gives you per host

A host’s Ports panel lists what it is listening on, with the service the scan identified and when each port was first and last seen:

The Ports panel for a single host

The first-seen and last-seen pair is what makes this more than a snapshot: a port with a recent first seen is new, and that is usually the thing worth acting on. The panel re-scans just this host, so you can confirm a change without sweeping the range again.

Scheduled discovery is configured under Configuration → Automatic scans, in the nmap block.

Vulnerability scanning

Two distinct halves. The generic half uses the bundled upstream template set. The OT/ICS half uses Axiom Border’s own suite of 57 industrial protocol checks, which travel inside the product and work with no internet access. Both are covered in Catalogs: vulnerabilities, OIDs, MIBs and more.

Open a host’s Vulnerabilities panel and use Re-scan IP address vulnerabilities:

The vulnerability re-scan dialog, with severity filters, depth and the intrusive switch

Depth is the setting that matters most, and the dialog explains each option as you select it:

Depth What runs
Light Technology detection and TLS only. Checks no CVEs
Medium Adds known CVEs, misconfigurations and default credentials
Deep All of the above, plus exposures, network services and the OT/ICS suite
OT/ICS (industrial) The industrial suite on its own — Modbus, IEC-104, DNP3, BACnet, OPC UA

Selection is by check family rather than by folder, so it does not depend on how the template set is laid out on disk.

Severity toggles which findings are reported at all, and Intrusive mode (OT/ICS) is the switch that adds checks which write to the device rather than only reading it.

Intrusive mode needs two locks released, not one

The switch in this dialog is only one of two locks. The probe must also be configured to permit intrusive scanning at all, and that setting is off on every installation.

With it off, the scan is not quietly downgraded to a read-only run — it is accepted and then ends as failed, and nothing is scanned at all. That matters more than it sounds: a request for intrusive mode you were not authorised to make does not come back as a clean read-only report, it comes back as no report.

The Network status header names the reason, so you are not left looking at a scan that did nothing and said nothing. It is recorded with the execution and in the service log as well.

Read OT/ICS vulnerability scanning before enabling anything against a live plant.

Reading a finding

The Vulnerabilities panel for one host

Name says what was confirmed — Modbus/TCP Diagnostics Function Exposed Without Authentication — and Description explains how it was confirmed and why it matters. CVE is empty whenever the finding is an exposure rather than a published vulnerability, so Template ID is the stable way to refer to one.

Origin is the column to read first: probe means the request was sent and the target answered, while inferred means it was derived from the device’s identity with no traffic emitted — a lead to verify, not a demonstrated fact. Confidence qualifies it further, and where an inference has evidence behind it, the Evidence toggle in that column opens it.

Checks from the write/control layer are marked twice over: the name ends in [INTRUSIVE] and the description opens with INTRUSIVE / GATED.

Detected, and what is merely potential

The table is titled Detected vulnerabilities, and the word is doing real work. It holds what was confirmed by an active scan, plus inferences where the product was actually identified. What it does not hold is the surface attributable to a device known only down to its manufacturer — that would be hundreds of rows the probe cannot stand behind, burying the findings you can act on.

That surface is not discarded. It is summarised above the table, as a count with its severity breakdown and a recommendation to scan the host actively.

An amber Vulnerabilities button means look inside

On Network status, a host whose potential surface is being summarised rather than listed shows its Vulnerabilities row action in amber. Without that cue a host with a hundred unconfirmed CVEs behind it reads exactly like a clean one from the collapsed row — which is the one thing an assessment tool must never do.

There is a cap on how many unconfirmed CVEs a single device lists individually before it switches to a summary — 30 by default. Version-confirmed findings are never summarised, however many there are. The cap is sniffing.passiveVulnRowLimit in Configuration; a negative value removes it and lists everything.

SNMP — interrogation and profiles

Open a host’s SNMP action to query it. The panel takes symbolic names as readily as numeric OIDs and carries a Standard OIDs helper that explains the common ones in plain language, so you do not need a MIB browser open beside you:

The per-host SNMP panel with the Standard OIDs helper expanded

Results render as a table or as a tree, and the tree is the one to use after a walk. How names resolve to numbers and back is covered in Catalogs: vulnerabilities, OIDs, MIBs and more.

Profiles instead of credentials

Rather than supplying credentials with every query, store them once as a profile and bind it to the hosts it applies to. The console has a wizard for this, with predefined profiles to start from, reachable from the SNMP panel’s profile selector.

Supported versions are v1, v2c and v3. For v3 the console explains the three security levels as you choose between them — only authPriv both authenticates and encrypts, and it is the only one that hides what is being polled rather than merely proving who is asking.

Resolution order when a scan runs: the profile named in the request, then the profile bound to that host, then the configured default. A target that resolves to no profile is skipped, and the execution says so.

Prefer profiles over one-off credentials

Credentials passed inline with a single query are persisted with that execution record and appear in its detail afterwards. Profiles keep them out of execution history entirely. Use inline credentials for one-off diagnostics only.

Note

The scheduled SNMP probe runs a discovery pass first, with a fixed budget. A /24 at cautious timing will exhaust it, so narrow the range or raise the discovery timing value in Configuration.

Passive analysis

The one probe you do not launch. It listens continuously on its capture interface and emits nothing, building an inventory indexed by MAC address alongside the address-indexed one the active probes fill.

Passive analysis on the Network status header opens what it has established:

Passive network analysis — inventory, LLDP neighbours and findings

Three tabs: the per-device Inventory with vendor, type, role and the confidence behind each, the LLDP neighbours it has overheard, and Findings such as the capture-point verdict, gateway identification and address conflicts.

The type and role columns are where the industrial coverage shows. The probe recognises thirteen industrial protocols carried over IP — Modbus, S7comm, OPC UA, DNP3, IEC-104, EtherNet/IP, BACnet, CODESYS, MELSEC, PTP, HART-IP, KNXnet/IP and FINS — and records which side is serving and which is asking. It also reads four protocols that travel directly over Ethernet with no IP layer at all: IEC 61850 GOOSE and Sampled Values, PTP/IEEE 1588, and PROFINET DCP. On a substation or production segment those are frequently the bulk of the traffic, and nothing that keys off addresses and ports can describe them. See Passive and active for the full picture, including which ports are vendor convention rather than registered and are therefore reported at lower confidence.

The neighbours tab is LLDP only. The probe sees and counts Cisco’s own discovery frames, but it does not derive neighbours from them — so a segment of Cisco equipment speaking CDP rather than LLDP produces traffic the probe accounts for and a neighbour table that stays empty. That is a limitation, not a fault to chase. The coverage note above the table states plainly what the figure leaves out — passive analysis only sees what talks, so a silent device is not counted.

Everything about it is configured under Configuration → Automatic scans, in the sniffing block: which interfaces to capture on, an optional capture filter, which observed addresses reach the host inventory, how often what it learns is persisted, and whether it infers vulnerabilities from identity.

The interface name is the usual culprit

Capture failing silently almost always comes down to the interface name, which must be the one the capture library uses: eth0 or enp3s0 on Linux, en0 on macOS. The friendly name from the operating system’s own network settings does not work.

The Notices panel on Network status reports capture conditions directly — a rejected capture filter, or analysis stopped by an error — so check there before assuming the network is quiet.

The shared execution model

Every launch is asynchronous: the request is accepted, the work continues in the background, and the Network status header reports the state of the most recent run as it progresses. A run ends as finished or failed, and a run interrupted by a probe restart is moved to failed on start-up rather than being left in progress forever.

Executions also publish over MQTT

For integrations that would rather not poll, the embedded broker publishes started, finished and failed events for every execution. See MQTT and OpenGate operations.

The merged result view

An individual execution tells you what one probe found. The Network status table tells you what is known about a host, merged across all four probes: address and hostname, MAC address, manufacturer and device type with their provenance, operating system, status, when it was first and last seen, then the per-probe contributions — ports from discovery, findings from vulnerability scanning, values from SNMP, and traffic counters from passive analysis.

The first-seen and last-updated pair is what turns the baseline into change detection: a host with a recent first-seen is new to the network.

This same merged view is available through the API for dashboards and integrations, at GET /security/results/last.

Feeding results into rules

Probe results reach the rules engine, so a finding can raise an alarm the same way a metric can — a new host appearing, a critical vulnerability, a port opening that should not be open. See Alarms and rules.

Index

Subsections of Security Probes

OT/ICS Vulnerability Scanning

OT/ICS vulnerability scanning

Generic vulnerability scanners are built for IT. Point one at an industrial segment and two things go wrong: it probes for a web server that a PLC does not have and drops the host from the scan, and when it does find something it has no idea what a coil or a Common Address is.

Axiom Border ships its own suite of 57 checks for five industrial protocols, written specifically for this problem. They are installed with the product and made available to every scan automatically, so they travel in every deployment role and work with no internet access and no upstream template feed.

Industrial equipment is not a web application

A write to the wrong register on a live PLC has physical consequences. Axiom Border defaults to read-only and requires two independent switches before it will send a single write frame. Read the safety model before you enable anything, and never enable intrusive mode without written authorisation for the segment you are testing.

Protocol coverage

Protocol Port Checks Of which intrusive
Modbus/TCP 502/TCP 19 6
IEC 60870-5-104 2404/TCP 11 4
DNP3 20000/TCP 10 3
BACnet/IP 47808/UDP 10 1
OPC UA 4840/TCP 7 2
Total 57 16

Every check is labelled with the protocol it targets, and the ones in the write/control layer are additionally labelled as intrusive. That intrusive label is what the safety model keys on.

What each layer detects

The suite is organised in four layers of increasing invasiveness:

flowchart TB
    L2["<b>Layer 2 — Detection</b><br>Is this protocol here?<br>Identify vendor and version"]
    L3["<b>Layer 3 — Exposure</b><br>Structural weaknesses reachable<br>without authentication"]
    L3B["<b>Layer 3b — Recon</b><br>Read process data and object lists<br>without writing anything"]
    L4["<b>Layer 4 — Intrusive</b><br>Write / control confirmation<br><i>gated behind two locks</i>"]
    CVE["<b>CVE checks</b><br>Vendor-specific, only where<br>fingerprinting is reliable"]

    L2 --> L3 --> L3B --> L4
    L2 --> CVE

    L4:::danger

    classDef danger fill:#fff0ed,stroke:#ff664e,color:#101010

Detection confirms the protocol is listening and extracts identity where the protocol allows it — Modbus device identification, DNP3 Object Group 0 device attributes, BACnet vendor identifier, OPC UA BuildInfo software version.

Exposure reports structural problems that need no credentials to observe: Modbus and DNP3 responding to unauthenticated requests, IEC-104 accepting a station interrogation, BACnet/IP being usable as a reflection and amplification source, OPC UA offering endpoints with None security policy or anonymous authentication.

Recon reads real process data — Modbus holding registers via FC03, Modbus diagnostics via FC08 sub-function 0, IEC-104 counter interrogation and read commands, DNP3 event classes and class-0 integrity polls, BACnet Who-Is discovery and object enumeration. These checks read; they never write.

Intrusive confirms a write is actually possible. It is described in detail below.

CVE checks exist only for the cases where the protocol itself reveals enough to be sure: Schneider Modicon over Modbus/UMAS, Delta enteliBUS (CVE-2019-9569) and Contemporary Controls (CVE-2025-13926) over BACnet, plus a multi-vendor Modbus fingerprint that maps identity to known advisories.

Why some known CVEs are deliberately absent

Axiom Border only reports what it can actually confirm. Three known CVEs cannot be confirmed over the industrial protocol itself, so no check claims to detect them: DNP3 CVE-2020-6996 (the Triangle MicroWorks stack version is not observable over DNP3), OPC UA stack versions below 1.5.374.158 (only the Basic128Rsa15 precondition is observable, and that is already covered by an exposure check), and the IEC-104 device CVEs (IEC-104 carries no native device identifier). For those, use SNMP interrogation to fingerprint the device and correlate the version externally.

Scan depth and how OT checks get selected

Depth decides which checks run. OT checks are never included by accident — you either choose a deep scan, choose OT explicitly, or opt in for scheduled scans:

Depth in the console Web templates OT read-only checks Notes
Light Yes No Unless the scheduled probe has OT checks enabled
Medium Yes No Unless the scheduled probe has OT checks enabled
Deep Yes (full) Yes Full web scan plus a separate OT pass
OT/ICS (industrial) No Yes The industrial suite only — for dedicated OT segments
flowchart TB
    REQ["Scan request"] --> LVL{"Depth"}
    LVL -->|"Light<br>Medium"| W["Web templates<br>only"]
    LVL -->|"Deep"| WOT["Web scan +<br>OT read-only"]
    LVL -->|"OT/ICS"| OT["OT/ICS<br>checks only"]
The configuration file uses different words

In configuration.yaml the same four depths are written ligero, medio, profundo and ot. The console labels them Light, Medium, Deep and OT/ICS. They are the same four settings — worth knowing if you move between the two.

Scheduled scans with OT checks enabled add the read-only OT layer to whatever depth they run at.

The OT pass runs separately, on purpose

When a scan includes OT checks, that part runs as a pass of its own that does not discard hosts without a web server. A general-purpose scan pre-filters targets by HTTP reachability, which would drop a PLC or RTU before a single Modbus request was ever sent. Running the OT pass separately is what makes industrial assets visible at all.

The safety model: two locks

Intrusive checks send write or control frames. Enabling them requires two independent switches that live in different places, so neither an operator nor a configuration mistake can unlock them alone:

  1. Per-scan opt-inIntrusive mode (OT/ICS) in the scan dialog, off every time you open it:
The vulnerability scan dialog. Intrusive mode is the switch at the bottom, off by default
  1. Deployment kill-switchvulnScan: authorise intrusive mode in Configuration → Automatic scans, which is off on every installation and has to be turned on deliberately, by someone with access to the probe’s configuration.

If the deployment lock is closed, an intrusive scan is rejected outright, not silently downgraded. The run is accepted and then ends as failed, and no checks execute — not even the read-only ones the same request asked for.

So an unauthorised intrusive request gives you nothing, never a partial result you might mistake for a clean bill of health — and the Network status header names the setting that blocked it, so the refusal is visible where you launched the scan rather than buried in a log.

Neither lock can be bypassed — the same rule applies to scans launched by the platform as to scans launched from the console.

The scheduled sweep is never intrusive

Whatever those two switches say, the scheduled vulnerability scan does not run write or control checks. Intrusive mode is a deliberate, manual, per-scan act — it can never become the background behaviour of a probe someone configured months ago and forgot.

flowchart TB
    REQ["Vulnerability<br>scan request"] --> Q1{"intrusive: true<br>in the request?"}
    Q1 -->|no| RO["Read-only scan<br><i>write layer excluded</i>"]
    Q1 -->|yes| Q2{"allowIntrusive<br>in configuration?"}
    Q2 -->|no| REJ["Request rejected<br><i>REST and MQTT</i>"]
    Q2 -->|yes| INT["Write layer enabled<br><i>audited as WARN</i>"]

    REJ:::danger
    INT:::danger

    classDef danger fill:#fff0ed,stroke:#ff664e,color:#101010

Three further guarantees hold regardless:

  • The scheduled probe never runs the intrusive layer. Automatic periodic scans are always read-only, whatever the configuration says. Intrusive checks only ever happen because someone asked for one, explicitly, right now.
  • Every intrusive execution is audited. A WARN entry goes to the log and a notice is attached to the details field of the execution record.
  • Intrusive means “no net change”, not “no writes”. The approach is read-then-write-back — read the current value, write the same value back — or SELECT-only for command protocols, issuing the select phase without the execute phase. Genuinely destructive actuation is excluded from the suite entirely.

What the intrusive layer actually does, per protocol

Protocol Intrusive checks Technique
Modbus/TCP write-single-register (FC03→FC06), write-single-coil (FC01→FC05), write-multiple-registers (FC03→FC16), write-multiple-coils (FC0F), mask-write-register (FC22), read-write-multiple-registers (FC17) Read current value, write the identical value back
IEC 60870-5-104 control-select (C_SC), double-command-select (C_DC), setpoint-select (C_SE), regulating-step-select (C_RC) SELECT phase only, S/E=1, execute phase never sent
DNP3 analog-output-writeback (g40→g41), binary-output-crob (g10v2→g12v1), crob-select-only Write-back of the read value; CROB in SELECT-only form
BACnet/IP writeproperty-noauth Writes present-value back verbatim, preserving the original tag encoding
OPC UA anonymous-session, node-write-back Establishes an anonymous session; writes a read value back
Real physical risk

“No net change” is a design goal, not a law of physics. A PLC may react to the act of being written to — some stacks latch, some log, some fault. A SELECT without an execute leaves a control point reserved on some IEC-104 implementations. Treat intrusive mode as an operation on live plant, because that is what it is: schedule it, get authorisation, and have someone watching the process while it runs.

Three ways to run it

All three start the same way: open the host’s Vulnerabilities panel and use Re-scan IP address vulnerabilities. What changes is what you set in the dialog.

A dedicated OT segment scan, read-only

The common case — assess industrial equipment without touching a single register.

Set Depth to OT/ICS (industrial) and leave Intrusive mode off. That runs the industrial suite on its own: detection, exposure and recon layers, all read-only. Nothing else runs, so there is no web scanning noise against equipment that has no web interface.

A deep scan covering both IT and OT

Set Depth to Deep. That is everything — technology detection, CVEs, misconfigurations, default credentials, exposures, network services and the OT/ICS read-only suite.

Use it on a mixed segment where industrial equipment sits alongside ordinary servers. On a pure OT segment prefer OT/ICS, which does the same industrial work without the rest.

An intrusive confirmation scan

Only once the deployment lock has been opened, and only against equipment you are authorised to write to.

Set Depth to OT/ICS, turn Intrusive mode (OT/ICS) on, and — this is the part that matters — run it against one host, not a subnet. Open the panel for that single asset rather than sweeping a range.

Narrow the target before you open the second lock

A read-only sweep across a /24 is routine. An intrusive sweep across a /24 sends write and control frames to every industrial device that answers, including ones you did not have in mind.

Intrusive runs are per-asset by discipline, not because the product forces it.

Enabling OT checks on the scheduled probe

To have the periodic automatic scan include the OT read-only layer without changing its level:

securityProbes:
  vulnScan:
    enabled: true
    level: "medio"
    enableOT: true          # adds the read-only OT layer to scheduled scans
    allowIntrusive: false   # keep the master lock closed

This is the recommended steady-state configuration for a probe sitting on an industrial segment: continuous read-only OT visibility, with the write layer bolted shut.

Reading the results

OT findings surface through the same execution model as every other probe — poll GET /security/executions/{uuid} for status, and read the findings from the execution record once the status reaches finished. See Security probes for the shared asynchronous execution model.

Findings identify the protocol, the affected host and port, the check that fired, and the severity. For fingerprint checks the extracted identity (vendor, model, firmware or software version) is part of the finding, which is what makes the CVE correlation useful downstream.

Operational guidance

Start with level: "ot" on a narrow target. A /24 sweep of an industrial segment generates traffic that some networks are not used to. Validate against one host, confirm the findings make sense, then widen.

Keep allowIntrusive: false as the normal state. Open it for the duration of an authorised test window and close it again. It is a configuration change and requires a service restart, which is a feature here rather than an inconvenience — it makes the unlock deliberate and visible.

Expect true negatives. The multi-vendor Modbus fingerprint reports nothing when no listed vendor is present. That is correct behaviour, not a missed detection.

Segment scans do not need internet. The OT checks are installed with the product and run offline. If your OT network is air-gapped — and it should be — nothing about this capability degrades.