external/anthropic-cybersecurity-skills/skills/monitoring-scada-modbus-traffic-anomalies/SKILL.md
Monitors Modbus TCP traffic on SCADA and ICS networks to detect anomalous function code usage, unauthorized register writes, and suspicious communication patterns. The analyst uses deep packet inspection with pymodbus, Scapy, and Zeek to baseline normal PLC/RTU communication behavior, then applies statistical and rule-based anomaly detection to identify reconnaissance, parameter manipulation, and denial-of-service attacks targeting Modbus devices on port 502. Activates for requests involving Modbus traffic analysis, SCADA network monitoring, ICS anomaly detection, PLC security monitoring, or OT network threat detection.
npx skillsauth add seikaikyo/dash-skills monitoring-scada-modbus-traffic-anomaliesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Do not use on networks without authorization from the asset owner, for active injection or fuzzing against production SCADA systems, or as a replacement for safety-instrumented systems (SIS) that provide physical process protection.
Establish passive monitoring on the OT network segment and begin capturing Modbus TCP frames:
tcpdump -i eth0 port 502 -c 100 -w modbus_capture.pcap.from scapy.all import rdpcap, TCP
from scapy.contrib.modbus import ModbusADURequest, ModbusADUResponse
packets = rdpcap("modbus_capture.pcap")
for pkt in packets:
if pkt.haslayer(ModbusADURequest):
adu = pkt[ModbusADURequest]
print(f"Src: {pkt['IP'].src} -> Dst: {pkt['IP'].dst} "
f"Unit: {adu.unitId} FuncCode: {adu.funcCode}")
@load policy/protocols/modbus/known-masters-slaves to generate modbus.log entries containing timestamp, source/destination IPs, function code, and exception responses. This provides continuous passive logging without custom scripting.Build a behavioral profile of legitimate Modbus traffic to distinguish normal operations from anomalies:
Normal baseline example (72-hour period):
HMI (10.1.1.10) -> PLC (10.1.1.50):
FC 03 (Read Holding Registers): 432,180 packets (97.2%)
FC 04 (Read Input Registers): 10,540 packets (2.4%)
FC 06 (Write Single Register): 1,780 packets (0.4%)
FC 16 (Write Multiple Registers): 0 packets (0.0%)
FC 43 (Read Device ID): 0 packets (0.0%)
Apply rule-based and statistical detection to identify suspicious function code usage:
WRITE_FUNCTION_CODES = {5, 6, 15, 16}
AUTHORIZED_WRITERS = {"10.1.1.10", "10.1.1.11"} # HMI and engineering WS
def check_unauthorized_write(src_ip, function_code):
if function_code in WRITE_FUNCTION_CODES and src_ip not in AUTHORIZED_WRITERS:
return {
"alert": "UNAUTHORIZED_MODBUS_WRITE",
"severity": "CRITICAL",
"src_ip": src_ip,
"function_code": function_code,
"description": f"Write FC {function_code} from unauthorized source {src_ip}"
}
return None
Exception response correlation:
- Isolated exception (1-2 per hour): Normal operational error
- Burst (>10 per minute): Active scanning or fuzzing attempt
- Continuous (>100 per hour): Denial-of-service or tool malfunction
Detect attempts to manipulate physical process parameters through register value analysis:
REGISTER_LIMITS = {
40001: {"name": "Reactor Temperature Setpoint", "min": 50, "max": 200, "unit": "C",
"max_rate": 5}, # Max 5 degrees per write cycle
40010: {"name": "Pump Speed", "min": 0, "max": 3600, "unit": "RPM",
"max_rate": 200}, # Max 200 RPM change per cycle
40020: {"name": "Valve Position", "min": 0, "max": 100, "unit": "%",
"max_rate": 10}, # Max 10% per cycle
}
def check_register_value(register_addr, new_value, previous_value):
if register_addr not in REGISTER_LIMITS:
return None
limits = REGISTER_LIMITS[register_addr]
alerts = []
if new_value < limits["min"] or new_value > limits["max"]:
alerts.append({
"alert": "REGISTER_VALUE_OUT_OF_RANGE",
"severity": "CRITICAL",
"register": register_addr,
"name": limits["name"],
"value": new_value,
"range": f"{limits['min']}-{limits['max']} {limits['unit']}"
})
if previous_value is not None:
rate = abs(new_value - previous_value)
if rate > limits["max_rate"]:
alerts.append({
"alert": "REGISTER_VALUE_EXCESSIVE_RATE",
"severity": "HIGH",
"register": register_addr,
"name": limits["name"],
"change": rate,
"max_allowed": limits["max_rate"]
})
return alerts if alerts else None
Identify anomalies in communication patterns that may indicate man-in-the-middle, replay, or denial-of-service attacks:
AUTHORIZED_MASTERS = {"10.1.1.10", "10.1.1.11"}
def detect_rogue_master(src_ip, dst_ip, dst_port):
if dst_port == 502 and src_ip not in AUTHORIZED_MASTERS:
return {
"alert": "ROGUE_MODBUS_MASTER",
"severity": "CRITICAL",
"src_ip": src_ip,
"target_slave": dst_ip,
"description": "Unauthorized device initiating Modbus connection"
}
return None
import numpy as np
from collections import defaultdict
class TimingAnomalyDetector:
def __init__(self, window_size=1000, threshold_sigma=3.0):
self.windows = defaultdict(list)
self.window_size = window_size
self.threshold_sigma = threshold_sigma
def check(self, src_ip, dst_ip, timestamp):
key = (src_ip, dst_ip)
window = self.windows[key]
if len(window) > 0:
interval = timestamp - window[-1]
if len(window) >= 100:
mean = np.mean(np.diff(window[-100:]))
std = np.std(np.diff(window[-100:]))
if std > 0 and abs(interval - mean) > self.threshold_sigma * std:
return {
"alert": "TIMING_ANOMALY",
"severity": "MEDIUM",
"pair": f"{src_ip}->{dst_ip}",
"interval": interval,
"expected_mean": mean,
"deviation_sigma": abs(interval - mean) / std
}
window.append(timestamp)
if len(window) > self.window_size:
window.pop(0)
return None
| Term | Definition | |------|------------| | Modbus TCP | An application-layer protocol encapsulating Modbus frames in TCP/IP, communicating on port 502. It uses a 7-byte MBAP header (transaction ID, protocol ID, length, unit ID) followed by the Modbus PDU containing the function code and data. | | Function Code | A single-byte identifier in the Modbus PDU specifying the operation: read coils (01), read discrete inputs (02), read holding registers (03), read input registers (04), write single coil (05), write single register (06), write multiple coils (15), write multiple registers (16), diagnostics (08), and device identification (43). | | MBAP Header | Modbus Application Protocol header used in Modbus TCP. Contains Transaction ID for request-response matching, Protocol ID (always 0x0000 for Modbus), Length of remaining bytes, and Unit Identifier for addressing slaves behind gateways. | | Holding Register | A 16-bit read/write register in a Modbus slave addressed at range 40001-49999 (protocol address 0-9998). Used for setpoints, configuration, and control values that can be written by the master. Primary target for process manipulation attacks. | | Coil | A single-bit read/write data element in a Modbus slave addressed at range 00001-09999. Controls discrete outputs (valves, pumps, breakers). Write operations (FC 05/15) to coils can directly affect physical equipment state. | | Deep Packet Inspection | Analysis beyond TCP/IP headers into the Modbus application-layer payload to extract function codes, register addresses, and values. Required because standard firewalls only inspect IP/port, missing protocol-level attacks that use legitimate Modbus framing. | | Rogue Master | An unauthorized device sending Modbus requests to slave devices. In OT environments, only designated HMI servers and engineering workstations should act as Modbus masters. A rogue master can read process data or write dangerous values to PLCs. | | Register Value Baseline | The statistical profile (min, max, mean, standard deviation) of values observed in specific registers during normal operations. Deviations beyond physical process bounds indicate sensor failure or malicious manipulation. |
modbus.func_code == 6), and exporting specific fields for analysis.Context: A water treatment facility uses Modbus TCP to communicate between the SCADA server (10.1.1.10) and six PLCs controlling chemical dosing pumps, filtration valves, and flow meters. The security team deploys passive Modbus traffic monitoring after an industry advisory about attacks targeting water utilities.
Approach:
Pitfalls:
Context: A manufacturing plant's SOC observes unusual network activity from an engineering workstation (10.1.2.20) that is authorized for PLC programming. The OT security team uses Modbus traffic monitoring to determine if the workstation is being used for reconnaissance.
Approach:
Pitfalls:
## Modbus Traffic Anomaly Report
**Monitoring Period**: 2026-03-15 00:00:00 UTC to 2026-03-15 23:59:59 UTC
**Network Segment**: OT VLAN 10 (10.1.1.0/24)
**Packets Analyzed**: 2,847,320
**Anomalies Detected**: 4
---
### Alert 1: Unauthorized Write Operation
**Timestamp**: 2026-03-15 14:23:17 UTC
**Severity**: CRITICAL
**Source**: 10.1.2.20 (Engineering Workstation)
**Destination**: 10.1.1.52 (PLC-3 Chemical Dosing)
**Function Code**: 16 (Write Multiple Registers)
**Registers**: 40050-40055
**Values Written**: [250, 100, 0, 1, 3600, 1]
**Baseline**: FC 16 never observed for this source-destination pair
**Context**: Register 40050 (Chlorine Dosing Rate) changed from 25 to 250
(safe range: 10-40). Register 40054 (Dosing Timer) changed from 1800 to 3600.
Combined effect would double chlorine concentration over extended period.
**Recommended Action**: Immediately verify physical process state. Isolate
source device. Check register values against expected setpoints with
plant operator.
---
### Alert 2: Device Enumeration Detected
**Timestamp**: 2026-03-15 14:20:05 to 14:20:47 UTC
**Severity**: HIGH
**Source**: 10.1.2.20
**Targets**: 10.1.1.50, 10.1.1.51, 10.1.1.52, 10.1.1.53, 10.1.1.54 (+10 more)
**Function Code**: 43 (Read Device Identification)
**Baseline**: FC 43 never observed from this source
**Context**: Sequential scanning of 15 devices in 42 seconds. Device
identification responses reveal PLC vendor, model, and firmware versions
for all scanned devices.
**Recommended Action**: Investigate source workstation for compromise
indicators. Block FC 43 from non-engineering subnets at OT firewall.
development
拋棄式 HTML mockup 比稿:產出 2 到 3 個設計立場不同的變體(密度 / 版式 / 強調軸,不是換色),各附取捨說明,最後給有立場的對比結論。適用:「畫個草圖」「比較 A 版 B 版」「先看方向再做」「給我看幾種做法」。要 production 元件或設計已定案時不適用。
tools
需求不明時的意圖萃取訪談:一次一題、每題附上自己的猜測、聽出「真正想要 vs 覺得應該要」,直到能預測使用者反應(約 95% 信心)才動工。適用:需求缺少對象 / 動機 / 成功標準 / 約束,或使用者點名「訪談我」「先確認一下」「我們確定嗎」。明確自足的指示、純資訊查詢、機械性操作不適用。
development
對非平凡決策啟動新鮮 context 對抗審查(找碴不背書),在修正還便宜的時候抓出錯誤方向。適用:高風險改動(production、資安敏感邏輯、不可逆操作)、不熟的程式碼、要宣稱「這樣是安全的 / 可行的」之前。機械性操作與一行修改不適用。
testing
Reference for writing and editing agent skills well — the vocabulary and principles that make a skill predictable. Consult when authoring, reviewing, or pruning a SKILL.md.