六个可运行的脚本 — 真实的 EtherNet/IP 协议流量(测试 1)、真实的 TLS/PKI 机制 (测试 3–6)— 整个链路中零 Rockwell 软件或许可。构建目的是在撰写报告之前测试一项声明, 而非仅凭信念为其辩护。
来源:构建于 2026-07-31,与对明尼苏达州布拉汉姆(Braham, MN)污水处理厂(WWTF)的平行调查同步进行 — 该厂是 2026 年 7 月 26–27 日明尼苏达州水务部门协同事件中公开披露的四家公用事业机构之一。 该事件的背景信息见 CISA 公告 AA26-097A(FBI/CISA/NSA/EPA/DOE/USCYBERCOM/财政部联合发布; 2026-04-07 发布,2026-07-22 扩展),涵盖正在进行的与 IRGC 关联的 CyberAv3ngers 活动。 归因声明,严格限定: 没有任何机构正式将明尼苏达事件本身归因于该组织 — 仅归因于更广泛的持续活动。 本文件夹是技术修复部分,有意与事件调查分开。
Rockwell PSIRT([email protected])和 RA Secure Mail([email protected])于 2026-07-31 被联系,早于本仓库和报告上线(按文件时间戳,约提前 30 分钟)。Rockwell 的安全架构团队 审阅了该仓库并于 2026-08-03 回复。直接引用,而非转述成比他们所述更强的声明:
Rockwell Automation 不认可或验证您的解释、您的 IEC 62443-4-2 映射,或从概念验证中得出的任何结论。 请勿将该工作表述为经 Rockwell Automation 审阅、批准或认可……这些脚本演示的是通用的密码学和 认证原理,而非任何特定于 CIP Security 或 CVE-2021-22681 的内容。
本工作未经 Rockwell Automation 审阅、批准或认可,到此为止。 他们的技术定性 — 通用原理,
而非特定于 CIP Security — 与下方“硬边界”表格对本仓库自身声明所做的区分相同;他们的审阅独立地
确认了这一点,而非提出异议。对于无法达到 CIP Security 的硬件上的公用事业机构,Rockwell 指向了
他们自己的 Converged Plantwide Ethernet (CPwE) 设计和实施指南(引用自 PHASED_ROLLOUT.md
第 1 阶段)— 本项目试图引导人们使用的现有供应商资源,而非重复造轮子。
测试 1 的发现 — 协议默认状态下无认证 — 并非 EtherNet/IP 独有。Modbus TCP 仍是水/废水控制系统中 部署最广泛的协议之一,其协议规范中完全没有认证概念;它起源于 1979 年的串行通信,从未以安全为设计目标。 CISA 在多个 ICS 公告中反复指出这一缺陷(例如三菱电机的 MELQC iQ-F 系列:“MODBUS/TCP 缺乏适当的认证”, 允许未经授权的读/写/停止)。Modbus 组织自己的答案是 Modbus/TCP Security,即带 X.509 证书的 TLS 封装 — 在结构上与测试 3 在此演示的修复类别相同,只是在协议组织层面标准化,而非单一供应商层面。 DNP3 有一个可选的 Secure Authentication 扩展(SAv5,2012 年标准化);独立分析和实施者报告均描述 其在实际中很少配置,原因是 OT 制造商之间的互操作性差距和协议本身的复杂性 — 留下了与测试 1 为 EtherNet/IP 演示的相同的暴露面。
架构要点,精确表述以免过度推销: 测试 3 的修复 — 双向 TLS、绑定在证书中的每设备身份(不仅仅是 CA 有效性)、通过 CRL 撤销 — 作用于传输层,而非 ICS 应用协议。该原理同样适用于 Modbus、DNP3 或 专有协议之下;变化的是封装,而非修复的形态。本仓库未构建或运行 Modbus 或 DNP3 特定的 PoC — 这是基于公开文档的架构性推广,与这里所有其他内容一样遵循“已演示 vs. 已引用”的分层,而非新的已测试声明。
来源:CISA 关于 Modbus/TCP 认证缺口的 ICS 公告 — Industrial Cyber · Modbus/TCP Security 概述 — Veridify · DNP3 SAv5/SAv6 采用挑战 — Step Function I/O
一份披露文件成败取决于不将这三者混为一谈,因为每种都有不同的修复方式:
此处演示的修复 — 每设备身份绑定 (测试 3) — 解决的是机群密钥失败。
请勿混淆两行。原理已证明。供应商对该原理的具体实现是可信的(这是他们自己声明的设计意图), 但我们未针对真实设备进行测试。
test1_baseline_vulnerable.py — 无认证基线,实时启动一个真实的 EtherNet/IP PLC 模拟器(cpppo,模拟 Allen-Bradley ControlLogix),并以
零凭据读取和写入一个控制标签。(范围:这是该活动所依赖的广泛无认证基线 — 并非
CVE-2021-22681 的特定硬编码密钥机制。有意保持区分;见上文“三种不同的失败”。)
python test1_baseline_vulnerable.py
test2_shared_secret_fails.py — 机群密钥缺陷的形态(叙事桥梁,非测试)两个端点持有一个静态密钥;来自设备 A 的凭据原样打开设备 B — 这是 CVE-2021-22681 一钥通吃缺陷的 最接近的结构类比。但它是同义反复: 两个处理器都是构造为接受该密钥的,因此不存在它可能失败的 执行路径。它没有演示代码未定义存在的任何内容。保留为从测试 1 到测试 3 的叙事桥梁;它不承载 任何证据权重,且有意不作为证明腿。
python test2_shared_secret_fails.py
test3_mutual_tls_fix.py — 修复,含阴性对照和双向真实 CA,两个各自唯一的设备证书 — 身份绑定在 SubjectAlternativeName 中,而非已弃用的 CommonName。五个案例,全部执行:
device-a 的客户端拒绝(针对真实 SAN 的 check_hostname)。双向 — 两端都绑定身份。python test3_mutual_tls_fix.py
test4_revocation.py — 生命周期腿:撤销一个真实 CA 签名的 CRL。客户端凭据(engineer-1)被授予;然后其序列号被添加到 CRL,
同一仍然有效、未过期、CA 签名的凭据被拒绝 — 这是 CR 1.8 / 1.9 撤销条款的经验内容。
唯一性(测试 3)≠ 可撤销性;这展示了凭据可以被收回。
python test4_revocation.py
test5_rotation.py — 测试 4 未闭合的生命周期腿:轮换为已持有有效凭据(v1)的同一身份(engineer-1)签发替换凭据(v2)。
对照(案例 3): 在 v2 存在之后但 v1 被明确退役之前再次呈现 v1 → 仍被授予 —
证明仅重新签发并不会使旧凭据退役。只有在 v1 被明确添加到 CRL(案例 4)后才被拒绝;
v2 在整个过程中不受影响(案例 5)— 身份在过渡期间从不失去访问权限。CR 1.8 的“签发替换凭据并
退役先前凭据”是两个操作,本测试分别展示了这两者。
python test5_rotation.py
test6_tamper_injection.py — CR 3.1 仅以“按构造”命名的腿:专门的完整性测试一个记录级中继位于真实的双向 TLS 客户端和服务器之间,通过仅解析 5 字节头部来转发 TLS 记录 —
它永远看不到加密负载的明文。对照: 每个字节原样转发 → 消息完整送达。篡改: 在实时
Application Data 记录的密文中翻转一位 → 接收端 TLS 栈的 AEAD 检查失败
(SSLV3_ALERT_BAD_RECORD_MAC),连接被拆除 — 损坏的数据永远不会被当作有效数据送达。
哪个字节,以及为何如此指定: TLS 1.2 AEAD 记录体为
explicit_nonce(8) ‖ ciphertext ‖ auth_tag(16),因此字节 0 是nonce,而非负载。翻转 nonce
也会触发 AEAD 检查 — 但通过扰乱解密而非标签捕获被更改的负载。CR 3.1 涉及未经授权的传输信息修改,
因此翻转目标是密文中部的字节,测试随后精确演示了其所声称的句子。
python test6_tamper_injection.py
maximum_version = TLSv1_2
有两个与可观测性而非安全相关的原因:在 TLS 1.2 下,缺失或被拒绝的客户端证书在握手期间失败,
因此测试获得确定性的、可归因的错误,而非 TLS 1.3 的握手后失败;且记录内容类型在明文中保持可见,
测试 6 的中继需要它来识别 Application Data 记录。部署您的设备支持的最高 TLS 版本 — 可用时使用
TLS 1.3。本仓库中没有任何内容应被解读为建议将生产系统限制在 1.2。presented == KEY、
identity in SAN)不是恒定时间的。此处不可利用 — 被比较的值是公开性质的身份字符串,且 TLS 在比较
运行之前已完成真正的密码学认证 — 但被标记是因为该模式会被复制到确实重要的地方。python -m venv venv
venv\Scripts\activate # 或:source venv/bin/activate
pip install -r requirements.txt
62443-4-2_SL2_MAPPING.md(CR 1.2 / 1.8 / 1.9 / 1.14 / 3.1,
诚实分层,每个缺口均已命名;CR 1.2、CR 1.9 和 CR 1.8 的签发/验证/撤销腿现已演示)。提交 PSIRT
([email protected] / [email protected])之前:对照购买的 IEC 62443-4-2:2019 副本
验证每个 CR 的规范性文本。PHASED_ROLLOUT.md(第 0 阶段止血 · 1 分段 · 2 补偿控制 · 3 CIP
Security/PKI,硬件允许时 · 4 运营)。适合小型公用事业机构的规模;诚实说明 CIP Security 受硬件限制,
因此第 0–2 阶段无论如何都承担风险降低。PHASE0_INVENTORY_WORKSHEET.md(可填写的设备清单,而非仅指示制作清单)、RESOURCES.md
(免费的 CISA / EPA / WaterISAC / AWWA 援助,已验证可用,非凭记忆)、以及
INCIDENT_RESPONSE_QUICK_REFERENCE.md(首 60 分钟卡片,明确非完整 IR 计划 — 运营安全始终优先)。test5_rotation.py 将 CR 1.8 从“已启用”
推进到完全演示(轮换,带自己的对照);test6_tamper_injection.py 将 CR 3.1 从“按构造”推进到
已演示(真实的位翻转,被 TLS 的 AEAD 检查拒绝)。两者在加入此处前均反复运行无抖动。另新增:
PHASE3_CA_QUICKSTART.md(“几行代码”的 CA,以真实测试过的 openssl 命令形式)以及
第 1 阶段中的具体允许列表示例。此处的每个外部标识符均于 2026-07-31 从实时来源拉取,而非凭训练记忆:AA26-097A(多来源,
包括 WaterISAC / Tenable / SecurityWeek)、布拉汉姆作为四个披露受害者之一、CyberAv3ngers/IRGC、
PN1550 确认为真实的 Rockwell 公告,其第 2 行引用逐字核对(“当正确部署时,CIP Security 可修复
此漏洞”+“不使用任何硬编码密钥”)、“无法通过补丁缓解”逐字、CVSS 10.0 / 严重(v3.1)、CISA 跟踪
ICSA-21-056-03、以及 62443-4-2 CR 1.8(PKI)+ CR 3.1(通信完整性)确认精确。
披露文件的纪律: 在提交时从主要来源重新拉取每个标识符。公告会被重新编号、扩展和取代 —
AA26-097A 已显示一次扩展 — 因此“2026-07-31 已验证”不等于“提交时已验证”。完整的正式逐 CR
62443-4-2 映射现已撰写(62443-4-2_SL2_MAPPING.md)— 剩余工作是在提交前对照购买的标准副本重新
拉取其引用的规范性文本,而非撰写映射本身。
l0gic — Patrick Crosby · 2026-07-31.
加固日志,2026-08-03: 新增 test5_rotation.py 和 test6_tamper_injection.py,闭合了自
2026-07-31 以来命名的两个开放技术项目 — 每个在写入此处前均运行三次无抖动。PHASE3_CA_QUICKSTART.md
(真实、已测试的 openssl 命令)、第 1 阶段具体的防火墙允许列表示例、PHASE0_INVENTORY_WORKSHEET.md、
RESOURCES.md、INCIDENT_RESPONSE_QUICK_REFERENCE.md 以及 Modbus/DNP3 推广也在同一天新增。
加固日志,2026-08-03(续)— 两轮独立审阅,均已应用: 安全红队发现并修复了 CA 快速入门中的真实 PKI
漏洞(-copy_extensions copyall 允许恶意证书请求自声明 CA:TRUE;通过显式 -extfile 修复,并针对
故意恶意的请求双向验证),并纠正了测试 6 以专门翻转密文而非字节 0(nonce),另加真实的线程安全修复和
依赖固定。另一次独立的结构性审计 — 将各阶段作为跨时间的系统而非清单来阅读 — 发现并修复了:第 3 阶段
的“几行”标题掩盖了撤销/轮换并不简单;CRL 故障开放/故障关闭从未决定(现已决定,带默认值和推理);
证书过期是一种新的、未命名的宕机模式(第 4 阶段现承载测试 5 自身的安全重叠教训);第 3 阶段静默地
使第 2 阶段的写入告警器失效(现已命名,并建议替换方案);NTP 是未声明的前置条件;CR 1.14 被表述为
“未满足”,而“不适用于已修复的设计”才是准确表述;且 INTEGRATOR_CHECKLIST.md/GLOSSARY.md 是
无链接的孤立文档(现已从第 3 阶段和本计划顶部链接)。每个修复均通过重新运行受影响的测试验证,而非仅
重读差异。已推送并上线 — 部署计划全部五个阶段已完成、经两轮审阅,今天没有任何内容仍留在本地。
加固日志: 测试 3 被加强以包含阴性对照(案例 4,证明必要性而非仅触发)、反向案例(案例 5, 双向绑定)和基于 SAN 的身份(而非 CN),然后通过重新运行全部五个案例重新验证;新增测试 4 (CRL 撤销)。测试 1/2 的说明被调整大小以保持三类失败的区别;测试 2 从“测试”降级为叙事桥梁; 新增撤销和恒定时间范围边界。引用链从主要来源检索,并标记为提交时重新拉取。
| 声明 | 层级 | 原因 |
|---|
| 架构原理 | “机群中的单一共享密钥因一次泄露即被整个机群攻破;每设备身份绑定认证可堵住这一漏洞” | 已证明 | 用真实运行的代码演示,包括证明该检查是必要的而非仅仅会触发的阴性对照:在严格端点设备 B 上,真正 CA 有效的证书因身份问题被拒绝(测试 3 · 案例 3),但在仅验证 CA 有效性的端点上,同一证书被接受(案例 4 — 对照)→ “仅 CA 签名有效 == 机群级访问 == 披着 TLS 外衣的测试 2。”该绑定在反方向同样成立:呈现有效机群证书的恶意服务器被客户端拒绝(案例 5)。测试 1 单独展示了更广泛的无认证基线。 |
| Rockwell 特定的 CIP Security 实现行为相同 | “在真实 Rockwell 硬件上启用 CIP Security 以这种方式修复 CVE-2021-22681” | 领先,有来源但未验证 | 这是 Rockwell 自己的公告(PN1550)措辞 — “当正确部署时,CIP Security 可修复此漏洞……不使用任何硬编码密钥” — 并非我们针对真实 Logix 硬件独立确认的内容。我们测试的是他们公告所描述的原理,而非他们确切的线级实现。 |
PHASED_ROLLOUT.mdGLOSSARY.md、INTEGRATOR_CHECKLIST.md、
PHASE3_CA_QUICKSTART.md)现已实际从计划中链接,而非未被引用。此列表上没有任何内容仍为 DRAFT —
上述和下述所有内容均已推送并在公共仓库上线。