Hunt the Behavior, Not the Attacker: A Technical Deep Dive into OT Insider Risk Detection via Behavioral Baselines + Video

Listen to this Post

Featured Image

Introduction:

Traditional IT security tools hunt for malware signatures and known bad actors—but in Operational Technology (OT), the most devastating threats often come from trusted insiders using legitimate credentials. When a technician’s routine maintenance escalates into a turbine trip or a contractor’s script triggers a safety interlock, the root cause is rarely malicious intent; it is anomalous behavior. This article moves beyond threat hunting and into impact hunting. We will extract technical methods from the post’s logic—baselining engineering workstations, PLC access patterns, and command frequency—and translate them into concrete Linux/Windows commands, Wireshark filters, SIEM queries, and industrial protocol analysis.

Learning Objectives:

  • Differentiate between intent-based detection and anomaly-based baselining in OT/ICS environments.
  • Execute command-line tools to capture, parse, and baseline Siemens S7, Modbus, and EtherNet/IP traffic.
  • Build behavioral rules for Windows Event Logs, Linux auditd, and industrial firewalls to detect high-signal deviations.

You Should Know:

1. Baselining Engineering Workstation-to-Controller Communication

The original post identifies “a new engineering workstation talking to a controller it never touched before” as a high-signal deviation. In most OT networks, PLCs maintain a static list of trusted programming stations. When an unauthorized laptop—or even a compromised domain-joined PC—initiates a stop command or uploads logic, production halts.

Step‑by‑step guide: Capturing and filtering industrial protocol sources with tshark

On a Linux jump box with port mirroring access to the OT switch, install tshark:

sudo apt update && sudo apt install tshark -y

Capture traffic to a specific PLC (IP 192.168.1.10) and display only the source IP addresses that have sent S7COMM (TCP 102) or Modbus (TCP 502) write requests:

sudo tshark -i eth0 -Y "ip.dst == 192.168.1.10 and (tcp.port == 102 or tcp.port == 502) and (s7comm.param.func == 0x05 or modbus.func_code == 0x06 or modbus.func_code == 0x10)" -T fields -e ip.src | sort | uniq -c

This one-liner creates a historical list of source IPs that have ever written to the PLC. Compare this output daily against an approved inventory. Any new IP writing logic or forcing outputs is a candidate for immediate investigation.

On Windows, use Netmon or Microsoft Message Analyzer (legacy) with a display filter:

Tcp.Port == 102 && S7COMM.Function == 0x05

Export the list and cross-reference with Active Directory groups using PowerShell:

Get-NetUDPEndpoint -LocalPort 102 | Select-Object LocalAddress, RemoteAddress

2. Detecting After-Hours Logic Changes and Alarm Suppression

The post warns of “after-hours logic changes followed by disabled alarms or altered setpoints.” Attackers—and negligent insiders—often disable alarms to prevent operators from noticing unsafe conditions while they modify code.

Step‑by‑step guide: Correlating Siemens S7 PLC changes with audit logs

On a Siemens S7-1500, use the integrated Security Event log via TIA Portal or pull logs via Syslog. However, network-level detection is faster. Use Zeek (formerly Bro) to parse S7 traffic and log PLC stop/run transitions and download events.

Install Zeek and enable the Siemens S7 plugin:

sudo apt install zeek zeek-plugin-s7comm

Create a Zeek script to alert on logic downloads (function code 0x29) occurring outside business hours:

module PLC_ALERTS;

export {
redef enum Notice::Type += {
AfterHours_Download,
Alarm_Suppressed
};
}

event s7comm::function_request(c: connection, header: S7COMM::Header, func: S7COMM::Function) {
local time_now = current_time();
if ( func == 0x29 && ( time_now < network_time("08:00:00") || time_now > network_time("18:00:00") ) ) {
NOTICE([$note=AfterHours_Download, $msg=fmt("S7 logic download from %s after hours", c$id$orig_h), $conn=c]);
}
}

For alarm suppression, monitor for the “Disable Alarm” command (often a specific DWord write to a pre-defined DB address). Create a Zeek signature:

signature s7_disable_alarm {
ip-proto == tcp
dst-port == 102
payload /.\x05\x00\x4A.\x12\x34/
event "S7 Alarm Suppression Attempt"
}
  1. Frequency Analysis: The Burst of Normal Commands at Abnormal Rate
    The post mentions “a burst of ‘normal’ commands at an abnormal rate.” A contractor might poll a PLC every 5 minutes; if they suddenly poll it 1000 times in 2 seconds, it could indicate a mapping script, a denial-of-service precursor, or a configuration sweep before an attack.

Step‑by‑step guide: Rate-based detection using tcpdump and simple shell scripts

On a Linux span port, capture Modbus traffic to a specific PLC for one hour to establish a baseline:

sudo tcpdump -i eth0 -n host 192.168.1.10 and port 502 -w modbus_baseline.pcap

Use capinfos and tshark to calculate packets per minute:

capinfos -c modbus_baseline.pcap | awk '{print $NF}' 
tshark -r modbus_baseline.pcap -T fields -e frame.time_epoch | uniq -c

Now implement a sliding-window rate monitor. Every 60 seconds, count how many packets have been sent to the PLC in the last 10 seconds:

!/bin/bash
INTERFACE=eth0
PLC_IP=192.168.1.10
THRESHOLD=200  adjust after baseline

while true; do
COUNT=$(sudo tcpdump -i $INTERFACE -c 1000 -G 10 -W 1 -w /dev/null -n host $PLC_IP and port 502 2>&1 | grep -oP '\d+ packets captured' | grep -oP '\d+')
if [ "$COUNT" -gt "$THRESHOLD" ]; then
echo "ALERT: Modbus flood to $PLC_IP - $COUNT packets in 10s"
fi
sleep 5
done
  1. Contractor Accounts Executing the Same Write Across Multiple PLCs
    A signature of a systematic attack—or a misconfigured deployment script—is a single account touching dozens of controllers in rapid succession. This behavior deviates from human-paced engineering.

Step‑by‑step guide: Windows Security Event log correlation for lateral movement

Enable advanced audit policies on domain controllers and jump servers:

auditpol /set /subcategory:"Process Creation" /success:enable
auditpol /set /subcategory:"Logon" /success:enable

Collect Event ID 4624 (logon) and Event ID 4688 (process creation). Use PowerShell to group logons by user and count unique destination devices:

$Events = Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4624} -MaxEvents 5000
$Events | Where-Object { $<em>.Properties[bash].Value -like 'contractor' } | Group-Object @{e={$</em>.Properties[bash].Value}} | Sort-Object Count -Descending

If a single contractor account has authenticated to 15+ PLC HMI stations in 10 minutes, trigger an alert.

  1. What Changed, From Where, and How Often: Building an OT Asset Baseline
    Without a CMDB, you can build a “normal touch” matrix using Bro/Zeek conn logs and passive asset profiling.

Step‑by‑step guide: Passive fingerprinting and connection tracking

Enable Zeek conn.log and s7comm.log. Use this one-liner to extract all source-destination pairs for Siemens S7:

zeek-cut ts id.orig_h id.resp_h service s7comm.function < conn.log | grep S7

Then, using pandas in Python, create a baseline of “expected pairs” over a 30-day period:

import pandas as pd
df = pd.read_csv('zeek_s7.csv')
pairs = df.groupby(['id.orig_h', 'id.resp_h']).size().reset_index(name='count')
baseline_pairs = pairs[pairs['count'] > 10]  normal if seen >10 times
new_pair = df.iloc[-1]
if not ((baseline_pairs['id.orig_h'] == new_pair['id.orig_h']) & (baseline_pairs['id.resp_h'] == new_pair['id.resp_h'])).any():
print("ALERT: New workstation-to-PLC pair detected")
  1. Disabled Alarms or Altered Setpoints: DPI and Deep Packet Inspection
    Alteration of setpoints rarely occurs in isolation; an attacker usually disables the associated alarm first. To detect this, you must parse write operations to specific memory addresses.

Step‑by‑step guide: Snort rules for critical setpoint modification

Create a Snort rule for Modbus holding register writes (function code 06) to a specific tank pressure register (address 40001):

alert tcp any any -> $PLC_NET 502 (msg:"Modbus write to critical pressure setpoint"; content:"|00 06|"; depth:4; offset:6; content:"|9C 40|"; within:20; sid:1000001; rev:1;)

For Siemens, create Suricata rules targeting writes to DB1.DBW0 (often the master control word):

alert tcp any any -> $HOME_NET 102 (msg:"Siemens S7 Write to DB1.DBW0 - Critical Control"; content:"|32 01 00 00 00 00 00 00 00 00 00 05|"; depth:100; sid:2000001; rev:1;)
  1. FreeBSD/pfSense: Simple Baseline Firewall Logging for OT VLANs
    If you don’t have an IDS, use packet filtering logs to track “first contact” events.

Step‑by‑step guide: pf logging on an OT firewall

On pfSense/FreeBSD, add logging to the default LAN-to-OT rule:

pass log (to pflog0) inet proto tcp from $LAN_NET to $OT_NET port {102, 502, 44818}

Read the log with tcpdump and filter for never-before-seen pairs:

tcpdump -n -e -tttt -r /var/log/pflog | awk '{print $10 " -> " $12}' | sort | uniq > /root/current_pairs.txt
comm -23 /root/current_pairs.txt /root/baseline_pairs.txt

Any line output by `comm` indicates a new source-destination connection since the baseline was created.

What Undercode Say:

  • Key Takeaway 1: Behavioral baselines shift detection from “who is the bad guy” to “what action creates risk.” This removes the reliance on threat intelligence feeds, which are often irrelevant or delayed in OT environments.
  • Key Takeaway 2: The highest-fidelity alerts in OT do not require machine learning—they require accurate, time-indexed asset pairing and frequency thresholds. The tools presented (tshark, Zeek, auditd, pfSense) are free and already available on most engineering jump boxes.

Analysis:

Most OT security programs are paralyzed by incomplete asset inventories. The methods above bypass the need for a perfect CMDB by learning normal behavior through passive observation. This approach respects the fragility of legacy controllers—no active scanning required. The emphasis on “contractor accounts” and “after-hours” is particularly effective because it targets the intersection of elevated privilege and reduced oversight. Implementation should start with the three most critical PLCs, not the entire plant, and expand as confidence in the baseline grows.

Prediction:

Within three years, major industrial SIEM vendors (Dragos, Nozomi, Claroty) will incorporate “insider risk scores” as a default analytic, not a premium add-on. These scores will be derived from the exact parameters described in this post: first-contact connections, command frequency outliers, and out-of-band administrative sessions. The next evolution will be user-entity behavior analytics (UEBA) tailored to Purdue Level 2 and Level 3, effectively merging IT’s UEBA engines with OT’s protocol parsers. Organizations that fail to baseline will be unable to distinguish a disgruntled engineer from a sophisticated supply chain compromise.

▶️ Related Video (72% Match):

🎯Let’s Practice For Free:

IT/Security Reporter URL:

Reported By: Talib Usmani – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky