[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6AE8QCX0PK2GSTPTBZM6BDF":3,"skill-same:01M6AE8QCX0PK2GSTPTBZM6BDF":109},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":41,"linkOnly":42,"markdownContent":43,"markdownBody":44,"frontMatter":45,"refName":68,"commitSha":69,"skillDirectory":70,"forksCount":71,"repositoryHtmlUrl":72,"repositoryHomepageUrl":73,"ownerHtmlUrl":74,"ownerBlogUrl":75,"ownerDescription":76,"updatedAt":77,"install":78,"resources":91},"01M6AE8QCX0PK2GSTPTBZM6BDF","detecting-anomalies-in-industrial-control-systems",null,"Deploys anomaly detection for OT\u002FICS environments using machine learning on OT network baselines, physics-based process models, and Modbus\u002FDNP3\u002FOPC UA traffic analysis to flag deviations, rogue devices, and mismatches against historian data. Use for continuous OT monitoring, baselining deterministic SCADA polling, or investigating alerts from Nozomi Guardian\u002FDragos needing deeper protocol analysis.","mukul975\u002FAnthropic-Cybersecurity-Skills","mukul975","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F42860185?v=4","01M6ADEQR8T5M4EEG6C67BQCWF","Mahipal","01M6ADJPMYWG1E5GM1FWBE3128",36078,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-anomalies-in-industrial-control-systems\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"detecting anomalies in industrial control systems","detection","machine-learning","anomaly-detection","iec62443","industrial-control","scada","ics","ot-security","security","data","network","monitoring","protocol","pr","machine learning","cybersecurity","2026-11-10 10:12:30",false,"---\nname: detecting-anomalies-in-industrial-control-systems\ndescription: Deploys anomaly detection for OT\u002FICS environments using machine learning on OT network baselines, physics-based process models, and Modbus\u002FDNP3\u002FOPC UA traffic analysis to flag deviations, rogue devices, and mismatches against historian data. Use for continuous OT monitoring, baselining deterministic SCADA polling, or investigating alerts from Nozomi Guardian\u002FDragos needing deeper protocol analysis.\ndomain: cybersecurity\nsubdomain: ot-ics-security\ntags:\n- ot-security\n- ics\n- scada\n- industrial-control\n- iec62443\n- anomaly-detection\n- machine-learning\nversion: 1.0.0\nauthor: mahipal\nlicense: Apache-2.0\natlas_techniques:\n- AML.T0043\n- AML.T0018\nnist_ai_rmf:\n- MEASURE-2.7\n- MEASURE-2.5\n- MAP-5.1\nnist_csf:\n- PR.IR-01\n- DE.CM-01\n- ID.AM-05\n- GV.OC-02\nmitre_attack:\n- T0836\n- T0831\n- T0832\n- T0814\n- T0801\n---\n\n# Detecting Anomalies in Industrial Control Systems\n\n## When to Use\n\n- When deploying continuous monitoring for OT environments that lack intrusion detection\n- When building behavior-based detection to complement signature-based IDS in OT networks\n- When establishing baselines for deterministic SCADA communications to detect deviations\n- When integrating machine learning anomaly detection with OT security monitoring platforms\n- When investigating alerts from Nozomi Guardian or Dragos Platform that require deeper analysis\n\n**Do not use** for signature-based detection of known exploits (see detecting-attacks-on-scada-systems), for IT network anomaly detection without OT protocols, or as a replacement for process safety systems (SIS).\n\n## Prerequisites\n\n- Passive network monitoring sensors on OT network SPAN\u002FTAP ports\n- Minimum 2-4 weeks of baseline traffic capture during normal operations\n- Python 3.9+ with scikit-learn, numpy, pandas for ML model training\n- Process historian access for physical process correlation data\n- Understanding of normal operational patterns including shift changes, batch processes, and maintenance windows\n\n## Workflow\n\n### Step 1: Build Multi-Dimensional Baseline Model\n\nCapture and model the deterministic behavior of ICS communications across multiple dimensions: timing, protocol behavior, and network topology.\n\n```python\n#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"ICS Anomaly Detection System.\n\nBuilds multi-dimensional baselines from OT network traffic and\ndetects anomalies using statistical and machine learning methods.\nDesigned for deterministic SCADA communication patterns.\n\"\"\"\n\nimport json\nimport sys\nimport time\nimport warnings\nfrom collections import defaultdict\nfrom datetime import datetime, timedelta\nfrom dataclasses import dataclass, field\n\nimport numpy as np\nimport pandas as pd\nfrom sklearn.ensemble import IsolationForest\nfrom sklearn.preprocessing import StandardScaler\n\nwarnings.filterwarnings(\"ignore\")\n\n\n@dataclass\nclass CommunicationProfile:\n    \"\"\"Profile for a single master-slave communication pair.\"\"\"\n    src_ip: str\n    dst_ip: str\n    protocol: str\n    port: int\n    avg_interval_ms: float = 0.0\n    std_interval_ms: float = 0.0\n    avg_payload_size: float = 0.0\n    function_codes: dict = field(default_factory=dict)\n    packets_per_minute: float = 0.0\n    first_seen: str = \"\"\n    last_seen: str = \"\"\n\n\nclass ICSAnomalyDetector:\n    \"\"\"Multi-dimensional anomaly detection for ICS environments.\"\"\"\n\n    def __init__(self):\n        self.profiles = {}\n        self.topology_baseline = set()\n        self.timing_model = None\n        self.isolation_forest = None\n        self.scaler = StandardScaler()\n        self.anomalies = []\n        self.training_data = []\n\n    def build_baseline_from_pcap(self, pcap_data):\n        \"\"\"Build baselines from parsed pcap data (list of flow records).\"\"\"\n        print(\"[*] Building ICS communication baselines...\")\n\n        for flow in pcap_data:\n            key = f\"{flow['src']}->{flow['dst']}:{flow['port']}\"\n\n            if key not in self.profiles:\n                self.profiles[key] = CommunicationProfile(\n                    src_ip=flow[\"src\"],\n                    dst_ip=flow[\"dst\"],\n                    protocol=flow.get(\"protocol\", \"TCP\"),\n                    port=flow[\"port\"],\n                    first_seen=flow.get(\"timestamp\", \"\"),\n                )\n\n            profile = self.profiles[key]\n            profile.last_seen = flow.get(\"timestamp\", \"\")\n\n            # Track function codes for industrial protocols\n            fc = flow.get(\"function_code\")\n            if fc is not None:\n                profile.function_codes[fc] = profile.function_codes.get(fc, 0) + 1\n\n            # Add to topology baseline\n            self.topology_baseline.add((flow[\"src\"], flow[\"dst\"], flow[\"port\"]))\n\n        # Calculate interval statistics\n        self._calculate_timing_stats(pcap_data)\n\n        print(f\"  Communication pairs: {len(self.profiles)}\")\n        print(f\"  Topology entries: {len(self.topology_baseline)}\")\n\n    def _calculate_timing_stats(self, flows):\n        \"\"\"Calculate packet timing statistics per communication pair.\"\"\"\n        timestamps = defaultdict(list)\n        for flow in flows:\n            key = f\"{flow['src']}->{flow['dst']}:{flow['port']}\"\n            ts = flow.get(\"timestamp_epoch\")\n            if ts:\n                timestamps[key].append(ts)\n\n        for key, ts_list in timestamps.items():\n            if key in self.profiles and len(ts_list) > 1:\n                ts_sorted = sorted(ts_list)\n                intervals = [\n                    (ts_sorted[i+1] - ts_sorted[i]) * 1000\n                    for i in range(len(ts_sorted) - 1)\n                ]\n                self.profiles[key].avg_interval_ms = np.mean(intervals)\n                self.profiles[key].std_interval_ms = np.std(intervals)\n                duration_min = (ts_sorted[-1] - ts_sorted[0]) \u002F 60\n                if duration_min > 0:\n                    self.profiles[key].packets_per_minute = len(ts_list) \u002F duration_min\n\n    def train_isolation_forest(self, features_df):\n        \"\"\"Train Isolation Forest model on feature vectors from baseline traffic.\"\"\"\n        print(\"[*] Training Isolation Forest model...\")\n\n        feature_cols = [\n            \"interval_ms\", \"payload_size\", \"packets_per_window\",\n            \"unique_func_codes\", \"new_connection_flag\",\n        ]\n\n        available_cols = [c for c in feature_cols if c in features_df.columns]\n        X = features_df[available_cols].fillna(0).values\n\n        X_scaled = self.scaler.fit_transform(X)\n\n        self.isolation_forest = IsolationForest(\n            n_estimators=200,\n            contamination=0.01,  # Expect 1% anomaly rate in baseline\n            random_state=42,\n            n_jobs=-1,\n        )\n        self.isolation_forest.fit(X_scaled)\n\n        scores = self.isolation_forest.decision_function(X_scaled)\n        print(f\"  Model trained on {len(X)} samples\")\n        print(f\"  Anomaly score range: [{scores.min():.4f}, {scores.max():.4f}]\")\n        print(f\"  Threshold: {np.percentile(scores, 1):.4f}\")\n\n    def detect_topology_anomaly(self, src_ip, dst_ip, port):\n        \"\"\"Detect new\u002Funauthorized communication pairs.\"\"\"\n        if (src_ip, dst_ip, port) not in self.topology_baseline:\n            return {\n                \"type\": \"NEW_COMMUNICATION_PAIR\",\n                \"severity\": \"high\",\n                \"detail\": f\"New connection: {src_ip} -> {dst_ip}:{port} not in baseline\",\n                \"recommendation\": \"Verify if this is an authorized new device or configuration change\",\n            }\n        return None\n\n    def detect_timing_anomaly(self, src_ip, dst_ip, port, interval_ms):\n        \"\"\"Detect polling interval deviations.\"\"\"\n        key = f\"{src_ip}->{dst_ip}:{port}\"\n        profile = self.profiles.get(key)\n\n        if profile and profile.std_interval_ms > 0:\n            z_score = abs(interval_ms - profile.avg_interval_ms) \u002F profile.std_interval_ms\n            if z_score > 4.0:\n                return {\n                    \"type\": \"TIMING_ANOMALY\",\n                    \"severity\": \"medium\",\n                    \"detail\": (\n                        f\"Interval {interval_ms:.1f}ms deviates from baseline \"\n                        f\"{profile.avg_interval_ms:.1f}ms (z-score: {z_score:.1f})\"\n                    ),\n                    \"recommendation\": \"Check for network congestion, device malfunction, or MITM attack\",\n                }\n        return None\n\n    def detect_function_code_anomaly(self, src_ip, dst_ip, port, func_code):\n        \"\"\"Detect unauthorized Modbus\u002FDNP3 function codes.\"\"\"\n        key = f\"{src_ip}->{dst_ip}:{port}\"\n        profile = self.profiles.get(key)\n\n        if profile and func_code not in profile.function_codes:\n            severity = \"critical\" if func_code in {5, 6, 15, 16, 8} else \"high\"\n            return {\n                \"type\": \"UNAUTHORIZED_FUNCTION_CODE\",\n                \"severity\": severity,\n                \"detail\": (\n                    f\"Function code {func_code} from {src_ip} to {dst_ip}:{port} \"\n                    f\"not in baseline. Allowed: {list(profile.function_codes.keys())}\"\n                ),\n                \"recommendation\": \"Investigate source - possible command injection attack\",\n            }\n        return None\n\n    def analyze_flow(self, flow):\n        \"\"\"Analyze a single network flow against all detection models.\"\"\"\n        results = []\n\n        # Topology check\n        topo = self.detect_topology_anomaly(flow[\"src\"], flow[\"dst\"], flow[\"port\"])\n        if topo:\n            results.append(topo)\n\n        # Timing check\n        if \"interval_ms\" in flow:\n            timing = self.detect_timing_anomaly(\n                flow[\"src\"], flow[\"dst\"], flow[\"port\"], flow[\"interval_ms\"])\n            if timing:\n                results.append(timing)\n\n        # Function code check\n        if \"function_code\" in flow:\n            fc = self.detect_function_code_anomaly(\n                flow[\"src\"], flow[\"dst\"], flow[\"port\"], flow[\"function_code\"])\n            if fc:\n                results.append(fc)\n\n        self.anomalies.extend(results)\n        return results\n\n    def generate_report(self):\n        \"\"\"Generate anomaly detection report.\"\"\"\n        print(f\"\\n{'='*60}\")\n        print(f\"ICS ANOMALY DETECTION REPORT\")\n        print(f\"{'='*60}\")\n        print(f\"Baseline Profiles: {len(self.profiles)}\")\n        print(f\"Anomalies Detected: {len(self.anomalies)}\")\n\n        severity_counts = defaultdict(int)\n        for a in self.anomalies:\n            severity_counts[a[\"severity\"]] += 1\n\n        for sev in [\"critical\", \"high\", \"medium\", \"low\"]:\n            if severity_counts[sev]:\n                print(f\"  {sev.upper()}: {severity_counts[sev]}\")\n\n        for a in self.anomalies[:20]:\n            print(f\"\\n  [{a['severity'].upper()}] {a['type']}\")\n            print(f\"    {a['detail']}\")\n\n\nif __name__ == \"__main__\":\n    print(\"ICS Anomaly Detection System\")\n    print(\"Load baseline data and call analyze_flow() for real-time detection\")\n```\n\n## Key Concepts\n\n| Term | Definition |\n|------|------------|\n| Deterministic Traffic | ICS networks exhibit highly predictable communication patterns where the same master polls the same slaves at fixed intervals with identical function codes |\n| Isolation Forest | Unsupervised machine learning algorithm that isolates anomalies by randomly partitioning feature space, effective for OT traffic with low anomaly rates |\n| Polling Interval | Time between consecutive SCADA master requests to a slave device, typically fixed and configurable (100ms to 10s) |\n| Function Code Allowlist | Set of permitted industrial protocol operations for each communication pair, enforced by anomaly detection rules |\n| Topology Baseline | Complete map of all authorized device-to-device communication paths in the OT network |\n| Physics-Based Detection | Using physical process models (thermodynamics, fluid dynamics) to detect attacks that manipulate the process while spoofing sensor data |\n\n## Tools & Systems\n\n- **Nozomi Networks Guardian**: OT anomaly detection with AI-powered baseline learning and industrial protocol analysis\n- **Dragos Platform**: Threat detection using behavioral analytics and threat intelligence specific to ICS environments\n- **Scikit-learn**: Python ML library with Isolation Forest, One-Class SVM, and Local Outlier Factor for anomaly detection\n- **Zeek with OT plugins**: Network security monitor with Modbus, DNP3, and BACnet protocol analyzers for baseline building\n\n## Output Format\n\n```\nICS Anomaly Detection Report\n==============================\nDetection Period: YYYY-MM-DD to YYYY-MM-DD\nBaseline Size: [N] communication profiles\n\nANOMALIES DETECTED: [N]\n  Critical: [N]  High: [N]  Medium: [N]  Low: [N]\n\n[SEVERITY] ANOMALY_TYPE\n  Source: [IP] -> Target: [IP]:[Port]\n  Detail: [Description of deviation from baseline]\n  Baseline: [Expected behavior]\n  Observed: [Actual behavior]\n```\n","# Detecting Anomalies in Industrial Control Systems\n\n## When to Use\n\n- When deploying continuous monitoring for OT environments that lack intrusion detection\n- When building behavior-based detection to complement signature-based IDS in OT networks\n- When establishing baselines for deterministic SCADA communications to detect deviations\n- When integrating machine learning anomaly detection with OT security monitoring platforms\n- When investigating alerts from Nozomi Guardian or Dragos Platform that require deeper analysis\n\n**Do not use** for signature-based detection of known exploits (see detecting-attacks-on-scada-systems), for IT network anomaly detection without OT protocols, or as a replacement for process safety systems (SIS).\n\n## Prerequisites\n\n- Passive network monitoring sensors on OT network SPAN\u002FTAP ports\n- Minimum 2-4 weeks of baseline traffic capture during normal operations\n- Python 3.9+ with scikit-learn, numpy, pandas for ML model training\n- Process historian access for physical process correlation data\n- Understanding of normal operational patterns including shift changes, batch processes, and maintenance windows\n\n## Workflow\n\n### Step 1: Build Multi-Dimensional Baseline Model\n\nCapture and model the deterministic behavior of ICS communications across multiple dimensions: timing, protocol behavior, and network topology.\n\n```python\n#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"ICS Anomaly Detection System.\n\nBuilds multi-dimensional baselines from OT network traffic and\ndetects anomalies using statistical and machine learning methods.\nDesigned for deterministic SCADA communication patterns.\n\"\"\"\n\nimport json\nimport sys\nimport time\nimport warnings\nfrom collections import defaultdict\nfrom datetime import datetime, timedelta\nfrom dataclasses import dataclass, field\n\nimport numpy as np\nimport pandas as pd\nfrom sklearn.ensemble import IsolationForest\nfrom sklearn.preprocessing import StandardScaler\n\nwarnings.filterwarnings(\"ignore\")\n\n\n@dataclass\nclass CommunicationProfile:\n    \"\"\"Profile for a single master-slave communication pair.\"\"\"\n    src_ip: str\n    dst_ip: str\n    protocol: str\n    port: int\n    avg_interval_ms: float = 0.0\n    std_interval_ms: float = 0.0\n    avg_payload_size: float = 0.0\n    function_codes: dict = field(default_factory=dict)\n    packets_per_minute: float = 0.0\n    first_seen: str = \"\"\n    last_seen: str = \"\"\n\n\nclass ICSAnomalyDetector:\n    \"\"\"Multi-dimensional anomaly detection for ICS environments.\"\"\"\n\n    def __init__(self):\n        self.profiles = {}\n        self.topology_baseline = set()\n        self.timing_model = None\n        self.isolation_forest = None\n        self.scaler = StandardScaler()\n        self.anomalies = []\n        self.training_data = []\n\n    def build_baseline_from_pcap(self, pcap_data):\n        \"\"\"Build baselines from parsed pcap data (list of flow records).\"\"\"\n        print(\"[*] Building ICS communication baselines...\")\n\n        for flow in pcap_data:\n            key = f\"{flow['src']}->{flow['dst']}:{flow['port']}\"\n\n            if key not in self.profiles:\n                self.profiles[key] = CommunicationProfile(\n                    src_ip=flow[\"src\"],\n                    dst_ip=flow[\"dst\"],\n                    protocol=flow.get(\"protocol\", \"TCP\"),\n                    port=flow[\"port\"],\n                    first_seen=flow.get(\"timestamp\", \"\"),\n                )\n\n            profile = self.profiles[key]\n            profile.last_seen = flow.get(\"timestamp\", \"\")\n\n            # Track function codes for industrial protocols\n            fc = flow.get(\"function_code\")\n            if fc is not None:\n                profile.function_codes[fc] = profile.function_codes.get(fc, 0) + 1\n\n            # Add to topology baseline\n            self.topology_baseline.add((flow[\"src\"], flow[\"dst\"], flow[\"port\"]))\n\n        # Calculate interval statistics\n        self._calculate_timing_stats(pcap_data)\n\n        print(f\"  Communication pairs: {len(self.profiles)}\")\n        print(f\"  Topology entries: {len(self.topology_baseline)}\")\n\n    def _calculate_timing_stats(self, flows):\n        \"\"\"Calculate packet timing statistics per communication pair.\"\"\"\n        timestamps = defaultdict(list)\n        for flow in flows:\n            key = f\"{flow['src']}->{flow['dst']}:{flow['port']}\"\n            ts = flow.get(\"timestamp_epoch\")\n            if ts:\n                timestamps[key].append(ts)\n\n        for key, ts_list in timestamps.items():\n            if key in self.profiles and len(ts_list) > 1:\n                ts_sorted = sorted(ts_list)\n                intervals = [\n                    (ts_sorted[i+1] - ts_sorted[i]) * 1000\n                    for i in range(len(ts_sorted) - 1)\n                ]\n                self.profiles[key].avg_interval_ms = np.mean(intervals)\n                self.profiles[key].std_interval_ms = np.std(intervals)\n                duration_min = (ts_sorted[-1] - ts_sorted[0]) \u002F 60\n                if duration_min > 0:\n                    self.profiles[key].packets_per_minute = len(ts_list) \u002F duration_min\n\n    def train_isolation_forest(self, features_df):\n        \"\"\"Train Isolation Forest model on feature vectors from baseline traffic.\"\"\"\n        print(\"[*] Training Isolation Forest model...\")\n\n        feature_cols = [\n            \"interval_ms\", \"payload_size\", \"packets_per_window\",\n            \"unique_func_codes\", \"new_connection_flag\",\n        ]\n\n        available_cols = [c for c in feature_cols if c in features_df.columns]\n        X = features_df[available_cols].fillna(0).values\n\n        X_scaled = self.scaler.fit_transform(X)\n\n        self.isolation_forest = IsolationForest(\n            n_estimators=200,\n            contamination=0.01,  # Expect 1% anomaly rate in baseline\n            random_state=42,\n            n_jobs=-1,\n        )\n        self.isolation_forest.fit(X_scaled)\n\n        scores = self.isolation_forest.decision_function(X_scaled)\n        print(f\"  Model trained on {len(X)} samples\")\n        print(f\"  Anomaly score range: [{scores.min():.4f}, {scores.max():.4f}]\")\n        print(f\"  Threshold: {np.percentile(scores, 1):.4f}\")\n\n    def detect_topology_anomaly(self, src_ip, dst_ip, port):\n        \"\"\"Detect new\u002Funauthorized communication pairs.\"\"\"\n        if (src_ip, dst_ip, port) not in self.topology_baseline:\n            return {\n                \"type\": \"NEW_COMMUNICATION_PAIR\",\n                \"severity\": \"high\",\n                \"detail\": f\"New connection: {src_ip} -> {dst_ip}:{port} not in baseline\",\n                \"recommendation\": \"Verify if this is an authorized new device or configuration change\",\n            }\n        return None\n\n    def detect_timing_anomaly(self, src_ip, dst_ip, port, interval_ms):\n        \"\"\"Detect polling interval deviations.\"\"\"\n        key = f\"{src_ip}->{dst_ip}:{port}\"\n        profile = self.profiles.get(key)\n\n        if profile and profile.std_interval_ms > 0:\n            z_score = abs(interval_ms - profile.avg_interval_ms) \u002F profile.std_interval_ms\n            if z_score > 4.0:\n                return {\n                    \"type\": \"TIMING_ANOMALY\",\n                    \"severity\": \"medium\",\n                    \"detail\": (\n                        f\"Interval {interval_ms:.1f}ms deviates from baseline \"\n                        f\"{profile.avg_interval_ms:.1f}ms (z-score: {z_score:.1f})\"\n                    ),\n                    \"recommendation\": \"Check for network congestion, device malfunction, or MITM attack\",\n                }\n        return None\n\n    def detect_function_code_anomaly(self, src_ip, dst_ip, port, func_code):\n        \"\"\"Detect unauthorized Modbus\u002FDNP3 function codes.\"\"\"\n        key = f\"{src_ip}->{dst_ip}:{port}\"\n        profile = self.profiles.get(key)\n\n        if profile and func_code not in profile.function_codes:\n            severity = \"critical\" if func_code in {5, 6, 15, 16, 8} else \"high\"\n            return {\n                \"type\": \"UNAUTHORIZED_FUNCTION_CODE\",\n                \"severity\": severity,\n                \"detail\": (\n                    f\"Function code {func_code} from {src_ip} to {dst_ip}:{port} \"\n                    f\"not in baseline. Allowed: {list(profile.function_codes.keys())}\"\n                ),\n                \"recommendation\": \"Investigate source - possible command injection attack\",\n            }\n        return None\n\n    def analyze_flow(self, flow):\n        \"\"\"Analyze a single network flow against all detection models.\"\"\"\n        results = []\n\n        # Topology check\n        topo = self.detect_topology_anomaly(flow[\"src\"], flow[\"dst\"], flow[\"port\"])\n        if topo:\n            results.append(topo)\n\n        # Timing check\n        if \"interval_ms\" in flow:\n            timing = self.detect_timing_anomaly(\n                flow[\"src\"], flow[\"dst\"], flow[\"port\"], flow[\"interval_ms\"])\n            if timing:\n                results.append(timing)\n\n        # Function code check\n        if \"function_code\" in flow:\n            fc = self.detect_function_code_anomaly(\n                flow[\"src\"], flow[\"dst\"], flow[\"port\"], flow[\"function_code\"])\n            if fc:\n                results.append(fc)\n\n        self.anomalies.extend(results)\n        return results\n\n    def generate_report(self):\n        \"\"\"Generate anomaly detection report.\"\"\"\n        print(f\"\\n{'='*60}\")\n        print(f\"ICS ANOMALY DETECTION REPORT\")\n        print(f\"{'='*60}\")\n        print(f\"Baseline Profiles: {len(self.profiles)}\")\n        print(f\"Anomalies Detected: {len(self.anomalies)}\")\n\n        severity_counts = defaultdict(int)\n        for a in self.anomalies:\n            severity_counts[a[\"severity\"]] += 1\n\n        for sev in [\"critical\", \"high\", \"medium\", \"low\"]:\n            if severity_counts[sev]:\n                print(f\"  {sev.upper()}: {severity_counts[sev]}\")\n\n        for a in self.anomalies[:20]:\n            print(f\"\\n  [{a['severity'].upper()}] {a['type']}\")\n            print(f\"    {a['detail']}\")\n\n\nif __name__ == \"__main__\":\n    print(\"ICS Anomaly Detection System\")\n    print(\"Load baseline data and call analyze_flow() for real-time detection\")\n```\n\n## Key Concepts\n\n| Term | Definition |\n|------|------------|\n| Deterministic Traffic | ICS networks exhibit highly predictable communication patterns where the same master polls the same slaves at fixed intervals with identical function codes |\n| Isolation Forest | Unsupervised machine learning algorithm that isolates anomalies by randomly partitioning feature space, effective for OT traffic with low anomaly rates |\n| Polling Interval | Time between consecutive SCADA master requests to a slave device, typically fixed and configurable (100ms to 10s) |\n| Function Code Allowlist | Set of permitted industrial protocol operations for each communication pair, enforced by anomaly detection rules |\n| Topology Baseline | Complete map of all authorized device-to-device communication paths in the OT network |\n| Physics-Based Detection | Using physical process models (thermodynamics, fluid dynamics) to detect attacks that manipulate the process while spoofing sensor data |\n\n## Tools & Systems\n\n- **Nozomi Networks Guardian**: OT anomaly detection with AI-powered baseline learning and industrial protocol analysis\n- **Dragos Platform**: Threat detection using behavioral analytics and threat intelligence specific to ICS environments\n- **Scikit-learn**: Python ML library with Isolation Forest, One-Class SVM, and Local Outlier Factor for anomaly detection\n- **Zeek with OT plugins**: Network security monitor with Modbus, DNP3, and BACnet protocol analyzers for baseline building\n\n## Output Format\n\n```\nICS Anomaly Detection Report\n==============================\nDetection Period: YYYY-MM-DD to YYYY-MM-DD\nBaseline Size: [N] communication profiles\n\nANOMALIES DETECTED: [N]\n  Critical: [N]  High: [N]  Medium: [N]  Low: [N]\n\n[SEVERITY] ANOMALY_TYPE\n  Source: [IP] -> Target: [IP]:[Port]\n  Detail: [Description of deviation from baseline]\n  Baseline: [Expected behavior]\n  Observed: [Actual behavior]\n```\n",{"name":5,"tags":46,"author":47,"domain":40,"license":15,"version":48,"nist_csf":49,"subdomain":54,"description":7,"nist_ai_rmf":55,"mitre_attack":59,"atlas_techniques":65},[32,31,30,29,28,27,26],"mahipal","1.0.0",[50,51,52,53],"PR.IR-01","DE.CM-01","ID.AM-05","GV.OC-02","ot-ics-security",[56,57,58],"MEASURE-2.7","MEASURE-2.5","MAP-5.1",[60,61,62,63,64],"T0836","T0831","T0832","T0814","T0801",[66,67],"AML.T0043","AML.T0018","main","54a798831d2266a3ca61ce68a7acb80b81160d57","skills\u002Fdetecting-anomalies-in-industrial-control-systems",4414,"https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills","https:\u002F\u002Fmahipal.engineer\u002FAnthropic-Cybersecurity-Skills\u002F","https:\u002F\u002Fgithub.com\u002Fmukul975","Mahipal.engineer","🔐 Cybersecurity | 💻 Dev | 📷 Street Photographer\r\n🎓 MSc | 📚 Research & AI Security","2026-08-31 12:32:44",{"source":8,"skillName":5,"commands":79,"prompt":89,"zipAvailable":90},[80,83,86],{"tool":81,"command":82},"npx","npx skills add mukul975\u002FAnthropic-Cybersecurity-Skills --skill detecting-anomalies-in-industrial-control-systems",{"tool":84,"command":85},"bunx","bunx skills add mukul975\u002FAnthropic-Cybersecurity-Skills --skill detecting-anomalies-in-industrial-control-systems",{"tool":87,"command":88},"pnpm","pnpm dlx skills add mukul975\u002FAnthropic-Cybersecurity-Skills --skill detecting-anomalies-in-industrial-control-systems","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-anomalies-in-industrial-control-systems\u002FSKILL.md",true,[92,97,103],{"path":93,"resourceType":93,"resourceTypeDesc":94,"sizeBytes":95,"sourceHtmlUrl":96},"LICENSE","许可证",11255,"https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-anomalies-in-industrial-control-systems\u002FLICENSE",{"path":98,"resourceType":99,"resourceTypeDesc":100,"sizeBytes":101,"sourceHtmlUrl":102},"references\u002Fapi-reference.md","REFERENCE","参考文档",1772,"https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-anomalies-in-industrial-control-systems\u002Freferences\u002Fapi-reference.md",{"path":104,"resourceType":105,"resourceTypeDesc":106,"sizeBytes":107,"sourceHtmlUrl":108},"scripts\u002Fagent.py","SCRIPT","脚本",6784,"https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-anomalies-in-industrial-control-systems\u002Fscripts\u002Fagent.py",[110,135,159,186,211,238],{"skillNo":111,"name":112,"nameZh":6,"description":113,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":117,"publishedAt":41},"01M6AGDM3HJXTKXX15ZZ0ZVCJ3","verifying-build-provenance-with-slsa-sigstore","Verifies artifact signatures and SLSA provenance using Sigstore's cosign (verify, verify-attestation, verify-blob-attestation) and slsa-verifier (verify-artifact), enforcing keyless OIDC builder identity and source repo against SLSA Build levels. Use in CI\u002FCD before deploying artifacts, when consuming third-party attestations, establishing a SLSA Build L3 pipeline, or confirming provenance during incident response or admission control.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fverifying-build-provenance-with-slsa-sigstore\u002FSKILL.md",[116],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[118,119,120,121,33,40,122,123,38,124,125,126,127,128,129,130,131,132,133,134],"sigstore","provenance","slsa","verifying build provenance with slsa sigstore","incident response","identity","code","artifacts","ci","supply-chain","pipeline","supply-chain-security","t1195","attestation","code-signing","cosign","keyless",{"skillNo":136,"name":137,"nameZh":6,"description":138,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":142,"publishedAt":41},"01M6AGDGBPWYVZ2F11GFKJ4HZ9","validating-tpm-measured-boot-attestation","Verifies TPM 2.0 measured-boot integrity and remote attestation with tpm2-tools -- reading PCRs (tpm2_pcrread), replaying the boot event log, generating and checking signed quotes (tpm2_quote\u002Ftpm2_checkquote), and sealing secrets to a PCR policy. Use to confirm a system booted trusted firmware\u002Fkernel for Zero Trust device posture, detect boot-chain tampering via PCR drift, or build a golden-value baseline for fleet attestation.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fvalidating-tpm-measured-boot-attestation\u002FSKILL.md",[141],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[143,131,144,145,33,146,40,38,147,148,149,150,151,152,153,154,155,156,157,158],"boot","TPM","validating tpm measured boot attestation","policy","verification","ZeRO","firmware","Tools","measured-boot","PCR","integrity","secrets","remote-attestation","tpm2-tools","integrity-verification","trusted-computing",{"skillNo":160,"name":161,"nameZh":6,"description":162,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":163,"categories":164,"keywords":169,"publishedAt":41},"01M6AGDCW0Y4FXZ3362GT1941M","validating-backup-integrity-for-recovery","Validates backup integrity through cryptographic hash verification, automated restore testing, corruption detection, and recoverability checks to confirm backups are reliable for disaster recovery and ransomware response. Use before relying on backups for recovery, when building post-backup validation pipelines, auditing backup infrastructure for compliance, or checking immutable\u002Fair-gapped backups for silent corruption or tampering.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fvalidating-backup-integrity-for-recovery\u002FSKILL.md",[165,168],{"categoryNo":166,"categoryName":167,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[153,170,171,172,173,40,174,175,147,176,177,178,179,180,25,181,182,183,184,185],"backup","recovery","validating backup integrity for recovery","testing","infrastructure","validation","compliance","hash-verification","incident-response","ransomware","cryptocurrency","t1078","t1490","immutable","disaster-recovery","restore-testing",{"skillNo":187,"name":188,"nameZh":6,"description":189,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":190,"categories":191,"keywords":193,"publishedAt":41},"01M6AGD9GBG2KZ336AE763J3WH","triaging-windows-with-kape","Runs KAPE (Kroll Artifact Parser and Extractor) to collect targeted forensic artifacts (registry hives, $MFT, event logs, prefetch, browser data) via Targets and parse them with Modules wrapping Eric Zimmerman's EZ Tools (PECmd, MFTECmd, RECmd). Use during early incident containment\u002Ftriage when full disk imaging is impractical but a defensible, parseable Windows artifact set is needed quickly, including at-scale remote collection.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Ftriaging-windows-with-kape\u002FSKILL.md",[192],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[194,195,196,40,197,198,199,200,201,202,203,34,204,205,206,207,150,208,209,210],"windows","kape","triaging windows with kape","registry","browser","logs","windows-forensics","dfir","mft","mftecmd","prefetch","eric-zimmerman","digital-forensics","SET","collection","pecmd","artifact-collection",{"skillNo":212,"name":213,"nameZh":6,"description":214,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":215,"categories":216,"keywords":218,"publishedAt":41},"01M6AGD5PW5J4EVZM958WD74ZQ","triaging-vulnerabilities-with-ssvc-framework","Triages and prioritizes vulnerabilities with CISA's Stakeholder-Specific Vulnerability Categorization (SSVC) decision tree, weighing exploitation status (via the CISA KEV catalog and FIRST EPSS API), technical impact, automatability, and mission prevalence to output Track\u002FTrack*\u002FAttend\u002FAct decisions. Use when prioritizing vulnerability scan results (OpenVAS, Nessus, Qualys) for remediation planning beyond raw CVSS scores.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Ftriaging-vulnerabilities-with-ssvc-framework\u002FSKILL.md",[217],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[219,220,221,40,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237],"vulnerabilities","framework","triaging vulnerabilities with ssvc framework","status","planning","CISA","vulnerability-management","risk-management","nessus","qualys","openvas","cvss","remediation","risk","vulnerability","api","prioritization","ssvc","decision-tree",{"skillNo":239,"name":240,"nameZh":6,"description":241,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":242,"categories":243,"keywords":245,"publishedAt":41},"01M6AGD24TTQQM4CXG69E3B9TJ","triaging-security-incident","Performs initial triage of security incidents using the NIST SP 800-61r3 and SANS PICERL frameworks, classifying incident type, assigning priority by business impact, and routing to the appropriate response team. Use when a SIEM\u002FEDR alert needs human classification, concurrent alerts must be prioritized, or a user report or threat-intel IOC match requires initial incident categorization.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Ftriaging-security-incident\u002FSKILL.md",[244],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[33,246,40,247,248,249,250,251,252,253,254,181,255,182,256,257,258,259,260,261],"triaging security incident","siem","file","ioc","soc","NIST-800-61","NIST","severity","edr","execution","analysis","classification","severity-classification","incident-triage","SANS-PICERL","SOC-operations"]