Skip to main content

OT posture rules

Cyprobe evaluates discovered assets against posture rules written in YAML. The rule schema is PeriDex -- the same format used by cyscan for SAST rules and the Cybrium CSPM engine for cloud posture. One schema across code, cloud, and OT.

Running a posture check

sudo cyprobe passive --interface eth0 --duration 300 --format json > assets.json
cyprobe rules check --input assets.json

Or inline after an active probe:

sudo cyprobe active --targets 192.168.1.0/24 --active-confirm --format json \
| cyprobe rules check --input -

Rule format

id: CYPROBE-OT-MODBUS-NO-AUTH
title: "Modbus TCP endpoint has no authentication"
severity: high
protocol: modbus
cwe: [CWE-306]
iec62443: [SR-1.1]
message: |
Modbus TCP has no built-in authentication. Any host on the network can
read/write coils and registers. Segment the device behind a firewall or
deploy a Modbus-aware gateway with access controls.
match:
protocol: modbus
field: auth_required
operator: eq
value: false

Required fields

FieldTypeNotes
idstringStable identifier. Convention: CYPROBE-OT-<PROTOCOL>-<NAME>
titlestringOne-line summary
severitycritical / high / medium / low / info
protocolstringProtocol this rule applies to, or any
messagemulti-line stringDescription + remediation guidance
matchobjectCondition to evaluate against asset data

Optional fields

FieldTypeNotes
cwelistCWE identifiers
iec62443listIEC 62443 security requirement references
nist_splistNIST SP 800-82 control references

Match operators

OperatorDescriptionExample
eqEqualsauth_required eq false
neNot equalstls_version ne 1.2
ltLess thanfirmware_age_days lt 365
gtGreater thanopen_port_count gt 5
containsString/list containssecurity_mode contains None
regexRegex matchfirmware_version regex ^1\.

Seed rules

Cyprobe ships with 10 seed rules covering the most common OT posture gaps:

IDSeverityProtocolWhat it checks
CYPROBE-OT-MODBUS-NO-AUTHhighmodbusModbus endpoint accepts unauthenticated requests
CYPROBE-OT-OPCUA-SECURITY-NONEcriticalopcuaOPC UA endpoint allows SecurityMode None
CYPROBE-OT-OPCUA-NO-CERThighopcuaOPC UA endpoint has no server certificate
CYPROBE-OT-MQTT-NO-AUTHhighmqttMQTT broker accepts anonymous connections
CYPROBE-OT-MQTT-NO-TLSmediummqttMQTT traffic is unencrypted (port 1883)
CYPROBE-OT-S7-DEFAULT-RACKmediums7commS7 PLC on default rack 0 / slot 2 (factory config)
CYPROBE-OT-DNP3-NO-AUTHhighdnp3DNP3 Secure Authentication not enabled
CYPROBE-OT-BACNET-WRITE-OPENhighbacnetBACnet device allows unauthenticated writes
CYPROBE-OT-FW-OUTDATEDmediumanyFirmware older than 365 days
CYPROBE-OT-FLAT-NETWORKhighanyIT and OT assets share the same L2 broadcast domain

Writing custom rules

  1. Create a .yml file in the rules directory.
  2. Follow the schema above.
  3. Validate: cyprobe rules validate
  4. Test against a known asset inventory: cyprobe rules check --input assets.json --rules ./my-rules

Example: flag any device with firmware older than 180 days

id: ACME-OT-FW-STALE
title: "Firmware not updated in 180 days"
severity: medium
protocol: any
message: |
Device firmware has not been updated in over 180 days. Review the vendor's
advisory feed and schedule a maintenance window for patching.
match:
protocol: any
field: firmware_age_days
operator: gt
value: 180

ID conventions

The bundled rules use the CYPROBE-OT-* prefix. Use a different prefix for your own rules (e.g. ACME-OT-*) to avoid collisions on upgrade.

Next step