performing-s7comm-protocol-security-analysis

对西门子SIMATIC S7 PLC使用的S7comm和S7CommPlus协议进行安全分析,识别漏洞,包括重放攻击、完整性绕过、未授权的CPU停止命令以及针对S7-300、S7-400、S7-1200和S7-1500控制器弱点的程序下载操控。

9 stars

Best use case

performing-s7comm-protocol-security-analysis is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

对西门子SIMATIC S7 PLC使用的S7comm和S7CommPlus协议进行安全分析,识别漏洞,包括重放攻击、完整性绕过、未授权的CPU停止命令以及针对S7-300、S7-400、S7-1200和S7-1500控制器弱点的程序下载操控。

Teams using performing-s7comm-protocol-security-analysis 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/performing-s7comm-protocol-security-analysis/SKILL.md --create-dirs "https://raw.githubusercontent.com/killvxk/cybersecurity-skills-zh/main/skills/performing-s7comm-protocol-security-analysis/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/performing-s7comm-protocol-security-analysis/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How performing-s7comm-protocol-security-analysis Compares

Feature / Agentperforming-s7comm-protocol-security-analysisStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

对西门子SIMATIC S7 PLC使用的S7comm和S7CommPlus协议进行安全分析,识别漏洞,包括重放攻击、完整性绕过、未授权的CPU停止命令以及针对S7-300、S7-400、S7-1200和S7-1500控制器弱点的程序下载操控。

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

# 执行S7comm协议安全分析

## 适用场景

- 评估西门子SIMATIC S7 PLC环境的安全态势时
- 为针对S7-300/400/1200/1500控制器的S7comm攻击构建检测规则时
- 对西门子Step 7/TIA Portal通信进行安全审计时
- 调查疑似对西门子PLC程序的未授权访问时
- 评估S7CommPlus完整性机制及其绕过可能性时

**不适用于**未经授权和测试计划扫描生产西门子PLC(可能导致控制器崩溃)、非西门子协议分析(Modbus请参见detecting-modbus-command-injection-attacks),或在生产环境中修改PLC程序。

## 前置条件

- 对S7comm通信网段(TCP端口102)的网络访问
- 带有S7comm解析器的Wireshark或带有S7comm协议分析器的Zeek
- 安全测试的授权访问(切勿在没有授权的情况下扫描生产PLC)
- 了解范围内的西门子PLC型号和固件版本
- 理解S7comm协议结构(COTP、S7 PDU、功能码)

## 工作流程

### 步骤 1:分析S7comm流量并识别漏洞

```python
#!/usr/bin/env python3
"""S7comm协议安全分析器。

分析西门子S7comm协议流量,识别安全
漏洞、未授权访问模式以及针对SIMATIC S7 PLC的
潜在攻击指示符。
"""

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

try:
    from scapy.all import rdpcap, IP, TCP
except ImportError:
    print("请安装scapy: pip install scapy")
    sys.exit(1)


# S7comm ROSCTR(PDU类型)定义
S7_ROSCTR = {
    0x01: "Job(请求)",
    0x02: "Ack",
    0x03: "Ack_Data(响应)",
    0x07: "Userdata",
}

# S7comm功能码
S7_FUNCTIONS = {
    0x00: "CPU服务",
    0x04: "读取变量",
    0x05: "写入变量",
    0x1A: "请求下载(程序)",
    0x1B: "下载块",
    0x1C: "下载结束",
    0x1D: "开始上传(读取程序)",
    0x1E: "上传块",
    0x1F: "上传结束",
    0x28: "PI服务(启动/停止CPU)",
    0x29: "PLC停止",
    0xF0: "建立通信",
}

# 安全相关的关键操作
CRITICAL_FUNCTIONS = {0x1A, 0x1B, 0x1C, 0x28, 0x29, 0x05}
PROGRAM_FUNCTIONS = {0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}


class S7commSecurityFinding:
    """表示S7comm流量中的安全发现。"""

    def __init__(self, severity: str, finding_type: str, src_ip: str,
                 dst_ip: str, function: str, description: str,
                 cve: str = "", recommendation: str = ""):
        self.timestamp = datetime.now().isoformat()
        self.severity = severity
        self.finding_type = finding_type
        self.src_ip = src_ip
        self.dst_ip = dst_ip
        self.function = function
        self.description = description
        self.cve = cve
        self.recommendation = recommendation


class S7commAnalyzer:
    """分析S7comm协议流量中的安全漏洞。"""

    def __init__(self):
        self.findings: List[S7commSecurityFinding] = []
        self.sessions: Dict[str, dict] = defaultdict(lambda: {
            "packets": 0,
            "functions_seen": set(),
            "writes": 0,
            "program_downloads": 0,
            "cpu_commands": 0,
            "first_seen": None,
            "last_seen": None,
        })
        self.authorized_engineering: set = set()
        self.packet_count = 0

    def set_authorized_stations(self, ips: List[str]):
        """设置授权工程师工作站IP列表。"""
        self.authorized_engineering = set(ips)

    def parse_s7comm(self, payload: bytes) -> Optional[dict]:
        """从TCP有效载荷解析S7comm协议数据。"""
        # TPKT报头: version(1) + reserved(1) + length(2)
        if len(payload) < 4:
            return None

        tpkt_version = payload[0]
        if tpkt_version != 3:
            return None

        tpkt_length = struct.unpack(">H", payload[2:4])[0]

        # COTP报头紧跟TPKT之后
        if len(payload) < 7:
            return None

        cotp_length = payload[4]
        cotp_type = payload[5]

        # S7comm在COTP之后开始
        s7_offset = 4 + 1 + cotp_length
        if len(payload) < s7_offset + 10:
            return None

        # S7comm报头
        protocol_id = payload[s7_offset]
        if protocol_id != 0x32:  # S7comm魔术字节
            return None

        rosctr = payload[s7_offset + 1]
        redundancy = struct.unpack(">H", payload[s7_offset + 2:s7_offset + 4])[0]
        pdu_ref = struct.unpack(">H", payload[s7_offset + 4:s7_offset + 6])[0]
        param_length = struct.unpack(">H", payload[s7_offset + 6:s7_offset + 8])[0]
        data_length = struct.unpack(">H", payload[s7_offset + 8:s7_offset + 10])[0]

        result = {
            "rosctr": rosctr,
            "rosctr_name": S7_ROSCTR.get(rosctr, f"未知 (0x{rosctr:02x})"),
            "pdu_ref": pdu_ref,
            "param_length": param_length,
            "data_length": data_length,
        }

        # 从参数中解析功能码
        param_offset = s7_offset + 10
        if rosctr in (0x01, 0x03) and param_length > 0 and len(payload) > param_offset:
            func_code = payload[param_offset]
            result["function_code"] = func_code
            result["function_name"] = S7_FUNCTIONS.get(func_code, f"未知 (0x{func_code:02x})")

        return result

    def analyze_packet(self, pkt):
        """分析数据包中的S7comm安全问题。"""
        self.packet_count += 1

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

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

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

        s7 = self.parse_s7comm(payload)
        if not s7:
            return

        src_ip = pkt[IP].src
        dst_ip = pkt[IP].dst
        session_key = f"{src_ip}->{dst_ip}"
        session = self.sessions[session_key]
        session["packets"] += 1

        if session["first_seen"] is None:
            session["first_seen"] = float(pkt.time)
        session["last_seen"] = float(pkt.time)

        func_code = s7.get("function_code")
        if func_code is not None:
            session["functions_seen"].add(func_code)

            # 检查1:未授权工程师工作站
            if tcp.dport == 102 and func_code in CRITICAL_FUNCTIONS:
                if self.authorized_engineering and src_ip not in self.authorized_engineering:
                    self.findings.append(S7commSecurityFinding(
                        severity="CRITICAL",
                        finding_type="UNAUTHORIZED_ENGINEERING_ACCESS",
                        src_ip=src_ip, dst_ip=dst_ip,
                        function=s7.get("function_name", "未知"),
                        description=(
                            f"来自未授权源 {src_ip} 的关键S7comm操作。"
                            f"功能: {s7.get('function_name')}。只有授权的TIA Portal "
                            f"工作站才能发出这些命令。"
                        ),
                        recommendation="在工业防火墙处阻止未授权源。调查源主机是否被攻陷。",
                    ))

            # 检查2:CPU停止命令
            if func_code == 0x29:
                session["cpu_commands"] += 1
                self.findings.append(S7commSecurityFinding(
                    severity="CRITICAL",
                    finding_type="CPU_STOP_COMMAND",
                    src_ip=src_ip, dst_ip=dst_ip,
                    function="PLC CPU停止 (0x29)",
                    description=f"已向 {dst_ip} 处的PLC发送CPU停止命令。这会停止PLC程序执行。",
                    cve="MITRE T0881 — 服务停止",
                    recommendation="验证这是否是授权的维护操作。如果不是,立即隔离源。",
                ))

            # 检查3:程序下载
            if func_code in (0x1A, 0x1B, 0x1C):
                session["program_downloads"] += 1
                self.findings.append(S7commSecurityFinding(
                    severity="CRITICAL",
                    finding_type="PROGRAM_DOWNLOAD",
                    src_ip=src_ip, dst_ip=dst_ip,
                    function=s7.get("function_name", "下载"),
                    description=(
                        f"向 {dst_ip} 执行PLC程序下载操作。"
                        f"这修改了PLC上运行的控制逻辑。"
                    ),
                    cve="MITRE T0843 — 程序下载",
                    recommendation="对照变更管理记录验证。与已知良好的程序备份进行比对。",
                ))

            # 检查4:写入变量操作
            if func_code == 0x05:
                session["writes"] += 1

            # 检查5:程序上传(PLC代码泄露)
            if func_code in (0x1D, 0x1E, 0x1F):
                self.findings.append(S7commSecurityFinding(
                    severity="HIGH",
                    finding_type="PROGRAM_UPLOAD_EXFILTRATION",
                    src_ip=src_ip, dst_ip=dst_ip,
                    function=s7.get("function_name", "上传"),
                    description=f"从 {dst_ip} 进行PLC程序上传(读取)。源 {src_ip} 正在提取PLC控制逻辑。",
                    recommendation="验证这是否是授权的维护操作。未授权的上传表示侦察行为。",
                ))

    def check_known_vulnerabilities(self):
        """基于观察到的行为检查已知的西门子S7漏洞。"""
        vuln_checks = [
            {
                "name": "S7-300/400重放攻击漏洞",
                "cve": "CVE-2019-13945",
                "description": "S7-300/400 PLC缺乏对S7comm会话的完整性检查,允许重放攻击",
                "affected": "S7-300、S7-400(所有固件版本)",
                "severity": "HIGH",
            },
            {
                "name": "S7CommPlus完整性绕过",
                "cve": "研究发现(Biham等人)",
                "description": "S7CommPlusV3完整性机制可被能够观察到一个合法会话的攻击者绕过",
                "affected": "S7-1200(< V4.5)、S7-1500(< V2.9)",
                "severity": "HIGH",
            },
            {
                "name": "不可修补的硬件信任根",
                "cve": "CVE-2022-38773",
                "description": "硬件漏洞允许绕过受保护的引导程序并持久修改固件",
                "affected": "S7-1500(特定硬件版本)",
                "severity": "CRITICAL",
            },
            {
                "name": "通过端口102的远程DoS",
                "cve": "CVE-2019-10929",
                "description": "TCP端口102上的特制数据包可以远程崩溃S7 PLC",
                "affected": "S7-300、S7-400、S7-1200、S7-1500(特定固件)",
                "severity": "HIGH",
            },
        ]
        return vuln_checks

    def generate_report(self):
        """生成综合S7comm安全分析报告。"""
        print(f"\n{'='*70}")
        print("S7COMM协议安全分析报告")
        print(f"{'='*70}")
        print(f"分析时间: {datetime.now().isoformat()}")
        print(f"已分析数据包: {self.packet_count}")
        print(f"S7comm会话: {len(self.sessions)}")
        print(f"安全发现: {len(self.findings)}")

        print(f"\n--- 会话摘要 ---")
        for key, session in self.sessions.items():
            funcs = [S7_FUNCTIONS.get(f, f"0x{f:02x}") for f in session["functions_seen"]]
            print(f"\n  {key}")
            print(f"    数据包: {session['packets']}")
            print(f"    功能: {', '.join(funcs)}")
            print(f"    写入次数: {session['writes']}")
            print(f"    程序下载次数: {session['program_downloads']}")
            print(f"    CPU命令次数: {session['cpu_commands']}")

        if self.findings:
            print(f"\n--- 安全发现 ---")
            for f in self.findings:
                print(f"\n  [{f.severity}] {f.finding_type}")
                print(f"    源: {f.src_ip} -> {f.dst_ip}")
                print(f"    功能: {f.function}")
                print(f"    详情: {f.description}")
                if f.cve:
                    print(f"    参考: {f.cve}")
                if f.recommendation:
                    print(f"    措施: {f.recommendation}")

        print(f"\n--- 已知漏洞评估 ---")
        for vuln in self.check_known_vulnerabilities():
            print(f"\n  [{vuln['severity']}] {vuln['name']}")
            print(f"    CVE: {vuln['cve']}")
            print(f"    受影响: {vuln['affected']}")
            print(f"    详情: {vuln['description']}")


if __name__ == "__main__":
    analyzer = S7commAnalyzer()
    analyzer.set_authorized_stations(["10.10.2.50", "10.10.2.51"])

    if len(sys.argv) >= 2:
        print(f"[*] 正在分析捕获文件: {sys.argv[1]}")
        packets = rdpcap(sys.argv[1])
        for pkt in packets:
            analyzer.analyze_packet(pkt)
        analyzer.generate_report()
    else:
        print("用法: python s7comm_analyzer.py <capture.pcap>")
        print("  分析S7comm流量中的安全漏洞")
```

## 核心概念

| 术语 | 定义 |
|------|------|
| S7comm | 西门子专有协议,用于通过TCP端口102与SIMATIC S7 PLC通信,基于COTP/TPKT协议栈 |
| S7CommPlus | S7comm的增强版本,用于S7-1200/1500,具有完整性保护机制 |
| ROSCTR | S7comm报头中的远程操作服务控制字段,指示PDU类型(Job、Ack、Ack_Data、Userdata) |
| TIA Portal | 全集成自动化门户(Totally Integrated Automation Portal)——西门子用于编程S7 PLC的工程软件 |
| CPU停止(0x29) | 停止PLC程序执行的S7comm功能,是一种关键的拒绝服务操作 |
| 程序下载(0x1A) | 启动向PLC传输新控制逻辑的S7comm功能,代表最高风险操作 |

## 常见场景

### 场景:未授权PLC程序修改

**背景**:Dragos传感器对来自非授权TIA Portal工程师工作站IP地址的S7comm程序下载流量发出警报。

**方法**:
1. 捕获完整的S7comm会话进行取证分析
2. 识别源主机并确定其是否被攻陷或为恶意设备
3. 将当前PLC程序与最后一个已知良好备份进行对比
4. 检查PLC CPU模式是否发生变化(运行→停止→编程)
5. 如果程序被修改,从已验证的备份中恢复
6. 调查攻击链——攻击者如何到达S7comm网段
7. 在所有PLC上实施S7comm访问保护(知识保护、访问密码)

**注意事项**:S7-300/400 PLC没有密码学完整性保护——任何能够到达TCP端口102的设备都可以发送命令。不要仅依赖PLC密码,因为它们在S7comm中以明文传输(非S7CommPlus)。网络分段是主要防御手段。

## 输出格式

```
S7COMM安全分析报告
===================================
日期: YYYY-MM-DD
范围: [已分析的网段]

会话清单:
  工程师工作站: [数量和IP]
  通信中的PLC: [数量和IP]
  未授权源: [数量]

关键发现:
  CPU停止命令: [数量]
  程序下载: [来自未授权源的数量]
  重放攻击可能性: [评估]

漏洞评估:
  S7-300/400(无完整性保护): [受影响PLC数量]
  S7-1200/1500(S7CommPlus): [固件评估]
  适用的已知CVE: [列表]

建议:
  1. [最高优先级修复措施]
  2. [网络分段改进]
  3. [监控增强]
```

Related Skills

triaging-security-incident

9
from killvxk/cybersecurity-skills-zh

使用 NIST SP 800-61r3 和 SANS PICERL 框架对安全事件进行初始分类,确定严重性、范围和所需响应行动。 按类型对事件分类,根据业务影响分配优先级,并路由到相应的响应团队。适用于事件分类、 安全告警分类、严重性评估、事件优先级排序或初始事件分析等请求场景。

triaging-security-incident-with-ir-playbook

9
from killvxk/cybersecurity-skills-zh

使用结构化 IR Playbook 对安全事件进行分类和优先排序,确定严重性、分配响应团队并启动适当的响应程序。

triaging-security-alerts-in-splunk

9
from killvxk/cybersecurity-skills-zh

在 Splunk Enterprise Security 中对安全告警进行分类,通过 SPL 查询和事件审查(Incident Review) 仪表板对重要事件进行严重性分类、调查、关联相关遥测并做出升级或关闭决策。 适用于 SOC 分析师需要处理关联搜索产生的告警队列、确定调查优先级, 或需要为交接给二/三级分析师记录分类决策时。

testing-websocket-api-security

9
from killvxk/cybersecurity-skills-zh

测试 WebSocket API 实现中的安全漏洞,包括 WebSocket 升级时缺少身份认证、跨站 WebSocket 劫持(Cross-Site WebSocket Hijacking,CSWSH)、通过 WebSocket 消息进行的注入攻击、输入校验不足、通过消息泛洪实施拒绝服务,以及通过 WebSocket 帧造成的信息泄露。测试人员使用 Burp Suite 拦截 WebSocket 握手和消息,构造恶意 payload,并测试 WebSocket 通道上的授权绕过。适用于 WebSocket 安全测试、WS 渗透测试、CSWSH 攻击或实时 API 安全评估相关请求。

testing-jwt-token-security

9
from killvxk/cybersecurity-skills-zh

在安全测试活动中,评估 JSON Web Token(JWT)实现中的密码学弱点、算法混淆攻击和授权绕过漏洞。

testing-api-security-with-owasp-top-10

9
from killvxk/cybersecurity-skills-zh

使用自动化和手工测试技术,针对 OWASP API 安全 Top 10 风险对 REST 和 GraphQL API 端点进行系统性评估。

performing-yara-rule-development-for-detection

9
from killvxk/cybersecurity-skills-zh

通过识别可执行文件中的唯一字节模式、字符串和行为指标,开发精准的 YARA 恶意软件检测规则,同时将误报率降至最低。

performing-wireless-security-assessment-with-kismet

9
from killvxk/cybersecurity-skills-zh

使用 Kismet 通过被动射频监控进行无线网络安全评估,检测流氓接入点(Rogue AP)、隐藏 SSID、弱加密和未授权客户端。

performing-wireless-network-penetration-test

9
from killvxk/cybersecurity-skills-zh

执行无线网络渗透测试,通过捕获握手包、破解 WPA2/WPA3 密钥、检测流氓接入点以及使用 Aircrack-ng 和相关工具测试无线网络分段,评估 WiFi 安全性。

performing-windows-artifact-analysis-with-eric-zimmerman-tools

9
from killvxk/cybersecurity-skills-zh

使用 Eric Zimmerman 的开源 EZ Tools 套件(包括 KAPE、MFTECmd、PECmd、LECmd、JLECmd 和 Timeline Explorer)执行全面的 Windows 取证制品分析,解析注册表 hive、预取文件、事件日志和文件系统元数据。

performing-wifi-password-cracking-with-aircrack

9
from killvxk/cybersecurity-skills-zh

在授权无线安全评估中捕获 WPA/WPA2 握手包,并使用 aircrack-ng、hashcat 和字典攻击进行离线密码破解, 以评估密码短语强度和无线网络安全状况。

performing-web-cache-poisoning-attack

9
from killvxk/cybersecurity-skills-zh

在授权安全测试期间,通过未纳入缓存键的头部和参数毒化缓存响应,利用 Web 缓存机制向其他用户投递恶意内容。