detecting-anomalies-in-industrial-control-systems

本技能涵盖使用机器学习模型(基于OT网络基线训练)、基于物理过程模型以及工业协议通信行为分析,在工业控制环境中部署异常检测(anomaly detection)系统。内容涉及为SCADA轮询模式构建正常行为基线、检测Modbus/DNP3/OPC UA流量中的偏差、识别未授权设备,以及将网络异常与历史数据服务器的物理过程数据进行关联。

9 stars

Best use case

detecting-anomalies-in-industrial-control-systems is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

本技能涵盖使用机器学习模型(基于OT网络基线训练)、基于物理过程模型以及工业协议通信行为分析,在工业控制环境中部署异常检测(anomaly detection)系统。内容涉及为SCADA轮询模式构建正常行为基线、检测Modbus/DNP3/OPC UA流量中的偏差、识别未授权设备,以及将网络异常与历史数据服务器的物理过程数据进行关联。

Teams using detecting-anomalies-in-industrial-control-systems should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/detecting-anomalies-in-industrial-control-systems/SKILL.md --create-dirs "https://raw.githubusercontent.com/killvxk/cybersecurity-skills-zh/main/skills/detecting-anomalies-in-industrial-control-systems/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/detecting-anomalies-in-industrial-control-systems/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How detecting-anomalies-in-industrial-control-systems Compares

Feature / Agentdetecting-anomalies-in-industrial-control-systemsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

本技能涵盖使用机器学习模型(基于OT网络基线训练)、基于物理过程模型以及工业协议通信行为分析,在工业控制环境中部署异常检测(anomaly detection)系统。内容涉及为SCADA轮询模式构建正常行为基线、检测Modbus/DNP3/OPC UA流量中的偏差、识别未授权设备,以及将网络异常与历史数据服务器的物理过程数据进行关联。

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# 检测工业控制系统中的异常行为

## 适用场景

- 为缺乏入侵检测的OT环境部署持续监控
- 构建基于行为的检测以补充OT网络中基于特征的入侵检测系统(IDS)
- 为确定性SCADA通信建立基线以检测偏差
- 将机器学习异常检测与OT安全监控平台集成
- 调查Nozomi Guardian或Dragos Platform告警时需要进行深入分析

**不适用于**基于特征的已知漏洞利用检测(参见detecting-attacks-on-scada-systems)、不含OT协议的IT网络异常检测,或替代过程安全系统(SIS)。

## 前置条件

- 在OT网络SPAN/TAP端口上部署被动网络监控传感器
- 正常运营期间至少2-4周的基线流量采集
- Python 3.9+,包含用于ML模型训练的scikit-learn、numpy、pandas
- 访问过程历史数据服务器以获取物理过程关联数据
- 了解正常操作模式,包括班次交接、批次处理和维护窗口

## 工作流程

### 步骤 1:构建多维基线模型

从多个维度捕获并建模ICS通信的确定性行为:时序、协议行为和网络拓扑。

```python
#!/usr/bin/env python3
"""ICS Anomaly Detection System.

Builds multi-dimensional baselines from OT network traffic and
detects anomalies using statistical and machine learning methods.
Designed for deterministic SCADA communication patterns.
"""

import json
import sys
import time
import warnings
from collections import defaultdict
from datetime import datetime, timedelta
from dataclasses import dataclass, field

import numpy as np
import pandas as pd
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler

warnings.filterwarnings("ignore")


@dataclass
class CommunicationProfile:
    """Profile for a single master-slave communication pair."""
    src_ip: str
    dst_ip: str
    protocol: str
    port: int
    avg_interval_ms: float = 0.0
    std_interval_ms: float = 0.0
    avg_payload_size: float = 0.0
    function_codes: dict = field(default_factory=dict)
    packets_per_minute: float = 0.0
    first_seen: str = ""
    last_seen: str = ""


class ICSAnomalyDetector:
    """Multi-dimensional anomaly detection for ICS environments."""

    def __init__(self):
        self.profiles = {}
        self.topology_baseline = set()
        self.timing_model = None
        self.isolation_forest = None
        self.scaler = StandardScaler()
        self.anomalies = []
        self.training_data = []

    def build_baseline_from_pcap(self, pcap_data):
        """Build baselines from parsed pcap data (list of flow records)."""
        print("[*] 正在构建ICS通信基线...")

        for flow in pcap_data:
            key = f"{flow['src']}->{flow['dst']}:{flow['port']}"

            if key not in self.profiles:
                self.profiles[key] = CommunicationProfile(
                    src_ip=flow["src"],
                    dst_ip=flow["dst"],
                    protocol=flow.get("protocol", "TCP"),
                    port=flow["port"],
                    first_seen=flow.get("timestamp", ""),
                )

            profile = self.profiles[key]
            profile.last_seen = flow.get("timestamp", "")

            # 跟踪工业协议的功能码
            fc = flow.get("function_code")
            if fc is not None:
                profile.function_codes[fc] = profile.function_codes.get(fc, 0) + 1

            # 添加到拓扑基线
            self.topology_baseline.add((flow["src"], flow["dst"], flow["port"]))

        # 计算时序统计信息
        self._calculate_timing_stats(pcap_data)

        print(f"  通信对数量: {len(self.profiles)}")
        print(f"  拓扑条目数: {len(self.topology_baseline)}")

    def _calculate_timing_stats(self, flows):
        """Calculate packet timing statistics per communication pair."""
        timestamps = defaultdict(list)
        for flow in flows:
            key = f"{flow['src']}->{flow['dst']}:{flow['port']}"
            ts = flow.get("timestamp_epoch")
            if ts:
                timestamps[key].append(ts)

        for key, ts_list in timestamps.items():
            if key in self.profiles and len(ts_list) > 1:
                ts_sorted = sorted(ts_list)
                intervals = [
                    (ts_sorted[i+1] - ts_sorted[i]) * 1000
                    for i in range(len(ts_sorted) - 1)
                ]
                self.profiles[key].avg_interval_ms = np.mean(intervals)
                self.profiles[key].std_interval_ms = np.std(intervals)
                duration_min = (ts_sorted[-1] - ts_sorted[0]) / 60
                if duration_min > 0:
                    self.profiles[key].packets_per_minute = len(ts_list) / duration_min

    def train_isolation_forest(self, features_df):
        """Train Isolation Forest model on feature vectors from baseline traffic."""
        print("[*] 正在训练孤立森林(Isolation Forest)模型...")

        feature_cols = [
            "interval_ms", "payload_size", "packets_per_window",
            "unique_func_codes", "new_connection_flag",
        ]

        available_cols = [c for c in feature_cols if c in features_df.columns]
        X = features_df[available_cols].fillna(0).values

        X_scaled = self.scaler.fit_transform(X)

        self.isolation_forest = IsolationForest(
            n_estimators=200,
            contamination=0.01,  # 预期基线中1%的异常率
            random_state=42,
            n_jobs=-1,
        )
        self.isolation_forest.fit(X_scaled)

        scores = self.isolation_forest.decision_function(X_scaled)
        print(f"  模型训练样本数: {len(X)}")
        print(f"  异常分数范围: [{scores.min():.4f}, {scores.max():.4f}]")
        print(f"  阈值: {np.percentile(scores, 1):.4f}")

    def detect_topology_anomaly(self, src_ip, dst_ip, port):
        """Detect new/unauthorized communication pairs."""
        if (src_ip, dst_ip, port) not in self.topology_baseline:
            return {
                "type": "NEW_COMMUNICATION_PAIR",
                "severity": "high",
                "detail": f"新连接: {src_ip} -> {dst_ip}:{port} 不在基线中",
                "recommendation": "验证是否为已授权的新设备或配置变更",
            }
        return None

    def detect_timing_anomaly(self, src_ip, dst_ip, port, interval_ms):
        """Detect polling interval deviations."""
        key = f"{src_ip}->{dst_ip}:{port}"
        profile = self.profiles.get(key)

        if profile and profile.std_interval_ms > 0:
            z_score = abs(interval_ms - profile.avg_interval_ms) / profile.std_interval_ms
            if z_score > 4.0:
                return {
                    "type": "TIMING_ANOMALY",
                    "severity": "medium",
                    "detail": (
                        f"间隔 {interval_ms:.1f}ms 偏离基线 "
                        f"{profile.avg_interval_ms:.1f}ms (z分数: {z_score:.1f})"
                    ),
                    "recommendation": "检查网络拥塞、设备故障或中间人攻击",
                }
        return None

    def detect_function_code_anomaly(self, src_ip, dst_ip, port, func_code):
        """Detect unauthorized Modbus/DNP3 function codes."""
        key = f"{src_ip}->{dst_ip}:{port}"
        profile = self.profiles.get(key)

        if profile and func_code not in profile.function_codes:
            severity = "critical" if func_code in {5, 6, 15, 16, 8} else "high"
            return {
                "type": "UNAUTHORIZED_FUNCTION_CODE",
                "severity": severity,
                "detail": (
                    f"来自 {src_ip} 到 {dst_ip}:{port} 的功能码 {func_code} "
                    f"不在基线中。允许的功能码: {list(profile.function_codes.keys())}"
                ),
                "recommendation": "调查来源 - 可能是命令注入攻击",
            }
        return None

    def analyze_flow(self, flow):
        """Analyze a single network flow against all detection models."""
        results = []

        # 拓扑检查
        topo = self.detect_topology_anomaly(flow["src"], flow["dst"], flow["port"])
        if topo:
            results.append(topo)

        # 时序检查
        if "interval_ms" in flow:
            timing = self.detect_timing_anomaly(
                flow["src"], flow["dst"], flow["port"], flow["interval_ms"])
            if timing:
                results.append(timing)

        # 功能码检查
        if "function_code" in flow:
            fc = self.detect_function_code_anomaly(
                flow["src"], flow["dst"], flow["port"], flow["function_code"])
            if fc:
                results.append(fc)

        self.anomalies.extend(results)
        return results

    def generate_report(self):
        """Generate anomaly detection report."""
        print(f"\n{'='*60}")
        print(f"ICS 异常检测报告")
        print(f"{'='*60}")
        print(f"基线配置文件数: {len(self.profiles)}")
        print(f"检测到的异常数: {len(self.anomalies)}")

        severity_counts = defaultdict(int)
        for a in self.anomalies:
            severity_counts[a["severity"]] += 1

        for sev in ["critical", "high", "medium", "low"]:
            if severity_counts[sev]:
                print(f"  {sev.upper()}: {severity_counts[sev]}")

        for a in self.anomalies[:20]:
            print(f"\n  [{a['severity'].upper()}] {a['type']}")
            print(f"    {a['detail']}")


if __name__ == "__main__":
    print("ICS 异常检测系统")
    print("加载基线数据并调用 analyze_flow() 进行实时检测")
```

## 核心概念

| 术语 | 定义 |
|------|------|
| 确定性流量(Deterministic Traffic) | ICS网络表现出高度可预测的通信模式,相同的主站以固定时间间隔、相同的功能码轮询相同的从站 |
| 孤立森林(Isolation Forest) | 通过随机划分特征空间来隔离异常的无监督机器学习算法,适用于低异常率的OT流量 |
| 轮询间隔(Polling Interval) | SCADA主站连续向从站设备发送请求之间的时间间隔,通常固定且可配置(100ms至10s) |
| 功能码白名单(Function Code Allowlist) | 每个通信对允许使用的工业协议操作集合,由异常检测规则强制执行 |
| 拓扑基线(Topology Baseline) | OT网络中所有已授权设备间通信路径的完整映射 |
| 基于物理的检测(Physics-Based Detection) | 使用物理过程模型(热力学、流体动力学)检测在欺骗传感器数据的同时操控过程的攻击 |

## 工具与系统

- **Nozomi Networks Guardian**:具备AI驱动基线学习和工业协议分析的OT异常检测平台
- **Dragos Platform**:使用行为分析和ICS特定威胁情报的威胁检测平台
- **Scikit-learn**:Python ML库,包含用于异常检测的孤立森林、单类SVM和局部离群因子算法
- **Zeek with OT plugins**:具备Modbus、DNP3和BACnet协议分析器的网络安全监控工具,用于基线构建

## 输出格式

```
ICS 异常检测报告
==============================
检测周期: YYYY-MM-DD 至 YYYY-MM-DD
基线规模: [N] 个通信配置文件

检测到的异常数: [N]
  严重: [N]  高: [N]  中: [N]  低: [N]

[严重级别] 异常类型
  来源: [IP] -> 目标: [IP]:[端口]
  详情: [偏离基线的描述]
  基线: [预期行为]
  观测: [实际行为]
```

Related Skills

testing-for-broken-access-control

9
from killvxk/cybersecurity-skills-zh

系统性测试 Web 应用程序中的访问控制缺陷,包括权限提升、缺失的功能级检查以及不安全的直接对象引用。

implementing-usb-device-control-policy

9
from killvxk/cybersecurity-skills-zh

实施 USB 设备控制策略,限制端点上未授权的可移动媒体访问,防止通过 USB 设备 进行数据泄露和引入恶意软件。适用于通过组策略、Intune 或 EDR 平台部署设备控制 以执行 USB 限制的场景。适用于涉及 USB 控制、可移动媒体策略、设备控制或 通过 USB 进行数据丢失防护的请求。

implementing-pod-security-admission-controller

9
from killvxk/cybersecurity-skills-zh

使用内置准入控制器在命名空间级别实施 Kubernetes Pod Security Admission(Pod 安全准入),强制执行基线和受限安全配置文件。

implementing-pci-dss-compliance-controls

9
from killvxk/cybersecurity-skills-zh

PCI DSS 4.0.1 为存储、处理或传输持卡人数据的组织建立了跨 6 个控制目标的 12 项要求。随着 PCI DSS 3.2.1 于 2024 年 4 月退休,51 项新要求将于 2025 年 3 月 31 日强制生效,本技能涵盖所有要求的实施,包括新的定制化验证方法、增强身份认证和持续监控控制。

implementing-patch-management-for-ot-systems

9
from killvxk/cybersecurity-skills-zh

本技能涵盖为OT/ICS环境实施结构化补丁管理程序,在传统IT补丁方法可能导致过程中断或安全隐患的情况下进行管理。内容包括供应商兼容性测试、基于风险的补丁优先级排序、通过测试环境的分阶段部署、维护窗口协调、回滚程序,以及在因运营约束或供应商限制无法应用补丁时的补偿控制措施。

implementing-network-access-control

9
from killvxk/cybersecurity-skills-zh

使用 RADIUS 认证、PacketFence NAC 和交换机配置实施 802.1X 基于端口的网络访问控制, 以强制执行基于身份的访问策略、态势评估和授权设备的自动 VLAN 分配。

implementing-network-access-control-with-cisco-ise

9
from killvxk/cybersecurity-skills-zh

部署 Cisco Identity Services Engine,实现 802.1X 有线和无线认证、MAC 认证旁路、态势评估和动态 VLAN 分配以进行网络访问控制。

implementing-nerc-cip-compliance-controls

9
from killvxk/cybersecurity-skills-zh

本技能涵盖为大型电力系统(BES)网络系统实施北美电力可靠性公司关键基础设施保护(NERC CIP)合规控制措施。内容包括资产分类(CIP-002)、电子安全边界(CIP-005)、系统安全管理(CIP-007)、配置管理(CIP-010)、供应链风险管理(CIP-013),以及2025年更新内容,包括远程访问强制MFA和扩展的低影响资产要求。

implementing-gdpr-data-protection-controls

9
from killvxk/cybersecurity-skills-zh

《通用数据保护条例》(EU)2016/679(GDPR)是欧盟关于个人数据收集、处理、存储和传输的综合数据保护法律。本技能涵盖实施 GDPR 要求的技术和组织措施。

implementing-endpoint-dlp-controls

9
from killvxk/cybersecurity-skills-zh

实施端点数据丢失防护(DLP)控制,检测并防止敏感数据通过电子邮件、USB、 云存储和打印进行泄露。适用于部署 DLP 代理、创建内容检查策略或防止 端点上未授权数据移动的场景。适用于涉及 DLP、数据泄露防护、内容检查 或端点敏感数据保护的请求。

implementing-api-key-security-controls

9
from killvxk/cybersecurity-skills-zh

实施安全的API密钥生成、存储、轮换和吊销控制,防止API认证凭据泄露、暴力破解和滥用。 设计具有足够熵的API密钥格式,实施安全哈希存储,执行按密钥范围限制和速率限制, 监控公共仓库中的密钥泄露,并构建密钥轮换工作流。

implementing-api-gateway-security-controls

9
from killvxk/cybersecurity-skills-zh

在API网关层实施安全控制,包括认证强制执行、速率限制、请求验证、IP白名单、TLS终止和威胁防护。 配置API网关(Kong、AWS API Gateway、Azure APIM、Apigee)作为集中式安全执行点, 在流量到达后端服务前对所有API流量进行验证、节流和监控。