detecting-modbus-command-injection-attacks

通过使用ICS感知IDS和协议深度包检测,监控未授权写入操作、异常功能码、格式错误的帧以及与已建立通信基线的偏差,检测ICS环境中针对Modbus TCP/RTU协议的命令注入攻击。

9 stars

Best use case

detecting-modbus-command-injection-attacks is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

通过使用ICS感知IDS和协议深度包检测,监控未授权写入操作、异常功能码、格式错误的帧以及与已建立通信基线的偏差,检测ICS环境中针对Modbus TCP/RTU协议的命令注入攻击。

Teams using detecting-modbus-command-injection-attacks 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-modbus-command-injection-attacks/SKILL.md --create-dirs "https://raw.githubusercontent.com/killvxk/cybersecurity-skills-zh/main/skills/detecting-modbus-command-injection-attacks/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/detecting-modbus-command-injection-attacks/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How detecting-modbus-command-injection-attacks Compares

Feature / Agentdetecting-modbus-command-injection-attacksStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

通过使用ICS感知IDS和协议深度包检测,监控未授权写入操作、异常功能码、格式错误的帧以及与已建立通信基线的偏差,检测ICS环境中针对Modbus TCP/RTU协议的命令注入攻击。

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

# 检测Modbus命令注入攻击

## 适用场景

- 为使用Modbus TCP(端口502)或Modbus RTU的环境部署入侵检测
- 调查对PLC寄存器或线圈的疑似未授权修改
- 为监控Modbus密集型环境的OT SOC构建检测分析
- 响应利用Modbus TCP造成操作影响的FrostyGoop式攻击
- 在疑似Modbus主站被入侵后进行基线验证

**不适用于**检测非Modbus协议的攻击(DNP3相关参见detecting-dnp3-protocol-anomalies)、一般IT网络入侵检测,或Modbus设备配置(参见performing-ot-vulnerability-scanning-safely)。

## 前置条件

- 承载Modbus TCP流量的网段的网络SPAN/TAP(通常为端口502)
- 正常Modbus通信模式基线(主站、从站、功能码、寄存器范围、轮询间隔)
- 部署了Modbus协议解析器的Suricata、Zeek或商业OT IDS
- 了解环境中使用的Modbus功能码(读取与写入操作)
- 访问PLC编程文档以验证预期寄存器范围

## 工作流程

### 步骤 1:构建Modbus通信基线

捕获并分析正常Modbus流量,确定合法通信模式的构成。

```python
#!/usr/bin/env python3
"""Modbus Command Injection Detector.

Monitors Modbus TCP traffic for unauthorized write operations, anomalous
function codes, and deviations from established communication baselines.
Detects attacks like FrostyGoop that use Modbus TCP for operational impact.
"""

import json
import struct
import sys
import time
from collections import defaultdict
from datetime import datetime
from typing import Dict, List, Optional, Set, Tuple

try:
    from scapy.all import sniff, IP, TCP
except ImportError:
    print("Install scapy: pip install scapy")
    sys.exit(1)


# Modbus功能码定义
MODBUS_READ_FUNCTIONS = {1, 2, 3, 4}
MODBUS_WRITE_FUNCTIONS = {5, 6, 15, 16}
MODBUS_DIAGNOSTIC_FUNCTIONS = {8, 17, 43}

MODBUS_FUNC_NAMES = {
    1: "Read Coils", 2: "Read Discrete Inputs",
    3: "Read Holding Registers", 4: "Read Input Registers",
    5: "Write Single Coil", 6: "Write Single Register",
    8: "Diagnostics", 15: "Write Multiple Coils",
    16: "Write Multiple Registers", 17: "Report Slave ID",
    22: "Mask Write Register", 23: "Read/Write Multiple Registers",
    43: "Encapsulated Interface Transport",
}


class ModbusAlert:
    """Represents a detected Modbus anomaly."""

    def __init__(self, severity: str, alert_type: str, src_ip: str,
                 dst_ip: str, unit_id: int, func_code: int,
                 description: str, mitre_technique: str = ""):
        self.timestamp = datetime.now().isoformat()
        self.severity = severity
        self.alert_type = alert_type
        self.src_ip = src_ip
        self.dst_ip = dst_ip
        self.unit_id = unit_id
        self.func_code = func_code
        self.func_name = MODBUS_FUNC_NAMES.get(func_code, f"Unknown FC {func_code}")
        self.description = description
        self.mitre_technique = mitre_technique

    def __str__(self):
        return (
            f"[{self.severity}] {self.alert_type} | {self.src_ip} -> {self.dst_ip} "
            f"| 单元 {self.unit_id} | {self.func_name} | {self.description}"
        )


class ModbusInjectionDetector:
    """Detects Modbus command injection attacks."""

    def __init__(self, baseline_file: Optional[str] = None):
        self.alerts: List[ModbusAlert] = []
        self.packet_count = 0
        self.modbus_count = 0

        # 基线数据
        self.authorized_masters: Set[str] = set()
        self.authorized_pairs: Set[Tuple[str, str]] = set()
        self.allowed_write_sources: Set[str] = set()
        self.allowed_function_codes: Dict[str, Set[int]] = defaultdict(set)
        self.allowed_register_ranges: Dict[str, List[Tuple[int, int]]] = defaultdict(list)
        self.polling_intervals: Dict[str, float] = {}
        self.last_seen: Dict[str, float] = {}

        # 速率检测计数器
        self.write_counts: Dict[str, List[float]] = defaultdict(list)

        if baseline_file:
            self.load_baseline(baseline_file)

    def load_baseline(self, filepath: str):
        """Load established Modbus communication baseline."""
        with open(filepath, "r") as f:
            baseline = json.load(f)

        for session_key, data in baseline.get("modbus_baselines", {}).items():
            src, dst = session_key.split("->")
            self.authorized_pairs.add((src.strip(), dst.strip()))
            self.authorized_masters.add(src.strip())

            fc_set = set(data.get("allowed_function_codes", []))
            self.allowed_function_codes[session_key] = fc_set

            if fc_set & MODBUS_WRITE_FUNCTIONS:
                self.allowed_write_sources.add(src.strip())

            for reg_range in data.get("register_ranges", []):
                self.allowed_register_ranges[session_key].append(
                    (reg_range["start"], reg_range["end"])
                )

            if data.get("polling_interval_avg_sec"):
                self.polling_intervals[session_key] = data["polling_interval_avg_sec"]

        print(f"[*] 基线已加载: {len(self.authorized_pairs)} 个授权对, "
              f"{len(self.allowed_write_sources)} 个授权写入来源")

    def parse_modbus_mbap(self, payload: bytes) -> Optional[dict]:
        """Parse Modbus TCP MBAP header and PDU."""
        if len(payload) < 8:
            return None

        transaction_id = struct.unpack(">H", payload[0:2])[0]
        protocol_id = struct.unpack(">H", payload[2:4])[0]
        length = struct.unpack(">H", payload[4:6])[0]
        unit_id = payload[6]
        func_code = payload[7]

        if protocol_id != 0:  # 非Modbus
            return None

        result = {
            "transaction_id": transaction_id,
            "protocol_id": protocol_id,
            "length": length,
            "unit_id": unit_id,
            "func_code": func_code,
        }

        # 解析读写操作的寄存器地址和数量
        if len(payload) >= 12 and func_code in (1, 2, 3, 4, 5, 6, 15, 16):
            result["start_address"] = struct.unpack(">H", payload[8:10])[0]
            result["quantity"] = struct.unpack(">H", payload[10:12])[0]

        return result

    def analyze_packet(self, pkt):
        """Analyze a network packet for Modbus command injection."""
        self.packet_count += 1

        if not pkt.haslayer(IP) or not pkt.haslayer(TCP):
            return

        tcp = pkt[TCP]
        if tcp.dport != 502 and tcp.sport != 502:
            return

        payload = bytes(tcp.payload)
        if not payload:
            return

        modbus = self.parse_modbus_mbap(payload)
        if not modbus:
            return

        self.modbus_count += 1
        src_ip = pkt[IP].src
        dst_ip = pkt[IP].dst
        session_key = f"{src_ip}->{dst_ip}"
        now = time.time()

        # 检测规则1:未授权Modbus主站
        if self.authorized_masters and src_ip not in self.authorized_masters:
            if tcp.dport == 502:
                self.alerts.append(ModbusAlert(
                    severity="CRITICAL",
                    alert_type="UNAUTHORIZED_MASTER",
                    src_ip=src_ip, dst_ip=dst_ip,
                    unit_id=modbus["unit_id"],
                    func_code=modbus["func_code"],
                    description=f"未授权设备 {src_ip} 向 {dst_ip} 发送Modbus命令",
                    mitre_technique="T0843 - Program Download",
                ))

        # 检测规则2:未授权写入操作
        if modbus["func_code"] in MODBUS_WRITE_FUNCTIONS:
            if self.allowed_write_sources and src_ip not in self.allowed_write_sources:
                self.alerts.append(ModbusAlert(
                    severity="CRITICAL",
                    alert_type="UNAUTHORIZED_WRITE",
                    src_ip=src_ip, dst_ip=dst_ip,
                    unit_id=modbus["unit_id"],
                    func_code=modbus["func_code"],
                    description=f"来自非授权来源 {src_ip} 的写入命令",
                    mitre_technique="T0855 - Unauthorized Command Message",
                ))

            # 跟踪写入频率用于速率异常检测
            self.write_counts[src_ip].append(now)
            recent_writes = [t for t in self.write_counts[src_ip] if now - t < 60]
            self.write_counts[src_ip] = recent_writes
            if len(recent_writes) > 20:
                self.alerts.append(ModbusAlert(
                    severity="HIGH",
                    alert_type="WRITE_FLOOD",
                    src_ip=src_ip, dst_ip=dst_ip,
                    unit_id=modbus["unit_id"],
                    func_code=modbus["func_code"],
                    description=f"过高的写入速率: {src_ip} 在60秒内 {len(recent_writes)} 次写入",
                    mitre_technique="T0836 - Modify Parameter",
                ))

        # 检测规则3:异常功能码
        if session_key in self.allowed_function_codes:
            if modbus["func_code"] not in self.allowed_function_codes[session_key]:
                self.alerts.append(ModbusAlert(
                    severity="HIGH",
                    alert_type="ANOMALOUS_FUNCTION_CODE",
                    src_ip=src_ip, dst_ip=dst_ip,
                    unit_id=modbus["unit_id"],
                    func_code=modbus["func_code"],
                    description=(
                        f"功能码 {modbus['func_code']} ({MODBUS_FUNC_NAMES.get(modbus['func_code'], 'Unknown')}) "
                        f"不在 {session_key} 的基线中"
                    ),
                    mitre_technique="T0855 - Unauthorized Command Message",
                ))

        # 检测规则4:广播写入(单元ID 0)
        if modbus["unit_id"] == 0 and modbus["func_code"] in MODBUS_WRITE_FUNCTIONS:
            self.alerts.append(ModbusAlert(
                severity="CRITICAL",
                alert_type="BROADCAST_WRITE",
                src_ip=src_ip, dst_ip=dst_ip,
                unit_id=0,
                func_code=modbus["func_code"],
                description="广播写入命令(单元ID 0)影响网段上的所有Modbus设备",
                mitre_technique="T0855 - Unauthorized Command Message",
            ))

        # 检测规则5:越界寄存器访问
        if "start_address" in modbus and session_key in self.allowed_register_ranges:
            addr = modbus["start_address"]
            qty = modbus.get("quantity", 1)
            in_range = any(
                start <= addr and addr + qty <= end
                for start, end in self.allowed_register_ranges[session_key]
            )
            if not in_range:
                self.alerts.append(ModbusAlert(
                    severity="HIGH",
                    alert_type="OUT_OF_RANGE_REGISTER",
                    src_ip=src_ip, dst_ip=dst_ip,
                    unit_id=modbus["unit_id"],
                    func_code=modbus["func_code"],
                    description=f"寄存器访问 {addr}-{addr+qty} 超出基线范围",
                    mitre_technique="T0836 - Modify Parameter",
                ))

        # 检测规则6:诊断/重启命令
        if modbus["func_code"] in MODBUS_DIAGNOSTIC_FUNCTIONS:
            self.alerts.append(ModbusAlert(
                severity="HIGH",
                alert_type="DIAGNOSTIC_COMMAND",
                src_ip=src_ip, dst_ip=dst_ip,
                unit_id=modbus["unit_id"],
                func_code=modbus["func_code"],
                description=f"检测到诊断功能码 {modbus['func_code']} - 潜在的拒绝服务或侦察",
                mitre_technique="T0814 - Denial of Service",
            ))

    def print_report(self):
        """Print detection report."""
        print(f"\n{'='*70}")
        print(f"MODBUS命令注入检测报告")
        print(f"{'='*70}")
        print(f"分析时间: {datetime.now().isoformat()}")
        print(f"分析的数据包总数: {self.packet_count}")
        print(f"Modbus数据包数: {self.modbus_count}")
        print(f"生成的告警数: {len(self.alerts)}")

        if self.alerts:
            severity_counts = defaultdict(int)
            for alert in self.alerts:
                severity_counts[alert.severity] += 1

            print(f"\n严重级别分布:")
            for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
                if sev in severity_counts:
                    print(f"  {sev}: {severity_counts[sev]}")

            print(f"\n详细告警:")
            for alert in self.alerts:
                print(f"\n  [{alert.severity}] {alert.alert_type}")
                print(f"    时间: {alert.timestamp}")
                print(f"    来源: {alert.src_ip} -> {alert.dst_ip}")
                print(f"    单元ID: {alert.unit_id}")
                print(f"    功能码: {alert.func_name} (FC {alert.func_code})")
                print(f"    详情: {alert.description}")
                if alert.mitre_technique:
                    print(f"    MITRE ATT&CK ICS: {alert.mitre_technique}")

    def start_live_monitoring(self, interface: str, duration: int = 0):
        """Start live Modbus traffic monitoring."""
        print(f"[*] 正在 {interface} 上启动Modbus监控...")
        print(f"[*] 按Ctrl+C停止")
        try:
            sniff(
                iface=interface,
                filter="tcp port 502",
                prn=self.analyze_packet,
                timeout=duration if duration > 0 else None,
            )
        except KeyboardInterrupt:
            pass
        self.print_report()


if __name__ == "__main__":
    detector = ModbusInjectionDetector(
        baseline_file=sys.argv[2] if len(sys.argv) > 2 else None
    )

    if len(sys.argv) >= 2:
        if sys.argv[1].endswith(".pcap") or sys.argv[1].endswith(".pcapng"):
            from scapy.all import rdpcap
            print(f"[*] 正在分析捕获文件: {sys.argv[1]}")
            packets = rdpcap(sys.argv[1])
            for pkt in packets:
                detector.analyze_packet(pkt)
            detector.print_report()
        else:
            detector.start_live_monitoring(sys.argv[1])
    else:
        print("用法:")
        print("  实时:    python modbus_detector.py <interface> [baseline.json]")
        print("  离线: python modbus_detector.py <capture.pcap> [baseline.json]")

Related Skills

testing-for-xxe-injection-vulnerabilities

9
from killvxk/cybersecurity-skills-zh

在授权的渗透测试中发现和利用 XML 外部实体(XXE)注入漏洞,以读取服务器文件、执行 SSRF 并外泄数据。

testing-for-xml-injection-vulnerabilities

9
from killvxk/cybersecurity-skills-zh

测试 Web 应用程序中的 XML 注入漏洞,包括 XXE(XML 外部实体注入)、XPath 注入和 XML 实体攻击,以识别数据泄露和服务器端请求伪造(SSRF)风险。

testing-for-host-header-injection

9
from killvxk/cybersecurity-skills-zh

测试 Web 应用程序的 HTTP Host 头部注入漏洞,以识别密码重置中毒、Web 缓存投毒、SSRF 以及虚拟主机路由操控风险。

testing-for-email-header-injection

9
from killvxk/cybersecurity-skills-zh

测试 Web 应用程序邮件功能中的 SMTP 头部注入漏洞,这些漏洞允许攻击者注入额外的邮件头部、修改收件人,并通过联系表单实施垃圾邮件中继。

performing-packet-injection-attack

9
from killvxk/cybersecurity-skills-zh

在授权安全评估中使用 Scapy、hping3 和 Nemesis 构造并注入自定义网络数据包, 测试防火墙规则、IDS 检测、协议处理能力以及网络协议栈对畸形和伪造流量的抵御能力。

hunting-for-process-injection-techniques

9
from killvxk/cybersecurity-skills-zh

通过 Sysmon 事件 ID 8 和 10 以及 EDR 进程遥测,检测进程注入技术(T1055),包括 CreateRemoteThread、进程空洞化和 DLL 注入。

hunting-for-ntlm-relay-attacks

9
from killvxk/cybersecurity-skills-zh

通过分析 Windows 事件 4624 中的 NTLMSSP 认证、IP 与主机名不匹配、Responder 流量签名、SMB 签名状态及跨域可疑认证模式,检测 NTLM 中继攻击。

hunting-for-dcsync-attacks

9
from killvxk/cybersecurity-skills-zh

通过分析 Windows 事件 ID 4662,检测非域控制器账户发起的未授权 DS-Replication-Get-Changes 请求,从而发现 DCSync 攻击。

hunting-for-command-and-control-beaconing

9
from killvxk/cybersecurity-skills-zh

通过频率分析、抖动检测和域名信誉评估,检测网络流量中的 C2 信标(Beaconing)模式,识别与攻击者基础设施通信的失陷终端。

hunting-credential-stuffing-attacks

9
from killvxk/cybersecurity-skills-zh

通过分析认证日志中的登录速率异常、ASN 多样性、密码喷洒(password spray)模式和失败登录的地理分布,检测凭据填充(credential stuffing)攻击。对 Splunk 或原始日志数据进行统计分析。适用于调查账户接管活动或为认证滥用构建检测规则。

exploiting-template-injection-vulnerabilities

9
from killvxk/cybersecurity-skills-zh

检测并利用 Jinja2、Twig、Freemarker 等模板引擎中的服务器端模板注入(SSTI)漏洞,实现远程代码执行。

exploiting-sql-injection-with-sqlmap

9
from killvxk/cybersecurity-skills-zh

在授权渗透测试中使用 sqlmap 检测并利用 SQL 注入漏洞以提取数据库内容。