[1] Ming Zhou, Haining Wang, Ke Li, and Hongsong Zhu, Limin Sun. 2024. Save the Bruised Striver: A Reliable Live Patching Framework for Protecting Real-World PLCs. In Nineteenth European Conference on Computer Systems (EuroSys’24), April 22–25, 2024, Athens, Greece. ACM, New York, NY, USA.
大家好,今天给大家介绍最新的一篇针对现实中的PLC的实时补丁工作。
1. 介绍
本文设计了RLPatch,无需源代码,对第三方二进制文件的可编程逻辑器件(PLC)进行实时修补,通过捕获PLC的实时状态和动态行为,来识别不可恢复故障(major non-recoverable fault, MNRF)漏洞,然后生成热补丁,随后通过一个更新代理,在原始MNRF代码上插入断点,然后执行补丁。
1.1 背景
现有较新版本的PLC,例如SIMATIC ET 200M 和ABB Pluto B20可以使用热插拔技术,但热插拔会增加PLC成本且不能作用于以往过时的PLC,本文针对传统PLC进行热补丁。
PLC供应商,例如罗克韦尔自动化、施耐德电气和西门子自动化等,都组建了安全团队来查找漏洞并发布安全建议,这些官方补丁侧重于解决原始代码中的功能和稳定性问题,而不是增强安全性,并且需要暂停目标控制器的操作。这种暂时的中断可能会导致服务中断,包括网络连接丢失或现场设备的潜在损坏。而实时补丁则不需要暂停操作。
PLC使用运行时内存使本文能够直接更改固件,而不是依赖专用的闪存补丁和断点(FPB)硬件。测试员可以通过逻辑指令和相关标签来定位运行内存中的易受攻击的代码。为了保证高实时性和可靠性,控制程序和固件均采用静态链接而不是运行时链接。
大多数嵌入式处理器都具有三个底层功能:调试接口、异常生成机制和异常处理程序。联合测试行动组 (JTAG) 和通用异步接收器/发送器(UART) 等调试接口使产品制造商能够通过内置调试通道与处理器进行通信。具体来说,本文的补丁插入方法依赖于调试通道来传输更新消息。本文的更新触发方法使用预取中止(Prefetch Abort)和未定义指令(Undefined Instruction)异常来触发更新操作。本文使用两个相应的自定义异常处理程序来重定向控制流。
1.2 贡献
RLPatch 是第一个针对闭源PLC 的实用实时修补框架。本文首先提出了一种精确的漏洞检测方法,利用控制器的控制程序和逻辑指令库之间的交互来捕获PLC的动态行为,该方法可以在没有源代码信息的情况下从固件中识别和定位易受攻击的代码。漏洞检测后,本文开发了一个更新代理,利用异常处理功能来修补在真实 PLC 上运行的固件,为了确保更新的代码版本之间的一致性,本文提供了一种集成机制,通过在扫描周期的末尾插入更新点来促进补丁的应用。总结而言,本文的主要技术创新在于三个方面:(1)精确的漏洞检测方法,(2)非侵入式更新服务,(3)自适应集成机制。
1.3 攻击模型
PLC有四种故障类型:严重不可恢复故障(MNRF)、主要可恢复故障、次要故障和I/O故障。除MNRF以外的三种故障均可被操作员处理来恢复,本文仅关注与内存损坏漏洞相关的 MNRF。通常,它们会遇到缺少越界检查或在释放或释放内存后访问内存位置的情况。MNRF 只能在固件级别进行完全修补。本文假设攻击者通过各种方式识别控制器固件的多个MNRF 漏洞,例如更新说明分析或固件模糊测试。
1.4 目标假设
本文对目标控制器做出以下假设。首先,它已经多年没有更新,但它仍然在关键环境(例如天然气管道系统和电力行业)中发挥着至关重要的作用。其次,本文假设其处理器支持实时调试逻辑单元和异常处理机制。第三,本文假设制造商已经发布了封装在固件更新中的二进制补丁以及一些额外的相关更新说明。更新报告是厂商随更新的固件一起发布的一组版本变更信息。
2. RLPatch架构
图2展示了RLPatch的整体架构
补丁开发将固件更新和更新注释作为输入并输出一组二进制补丁。之后,值得简要检查一下与硬件相关的配置。当在步骤 ➊ 中根据目标处理器规格正确配置适配器(协议转换器)时,DevOps 工程师在步骤 ➋ 中通过合适的调试访问接口将其连接到控制器板。然后,DevOps 在修补程序主机上安装并配置相应的驱动程序,然后在步骤 ➌ 将适配器连接到主机。一旦补丁通道设置完毕,DevOps 就会启动基本的补丁部署命令,并在步骤 ➍ 测试其是否运行良好。一旦建立了通信链路,补丁部署人员就会与更新代理通过植入的热补丁来修复关键的 MNRF 漏洞。
2.1 补丁开发
RLPatch的补丁开发有四个目标:识别漏洞信息、定位固件中存在漏洞的代码片段、构建热补丁以及验证二进制补丁。
2.1.1 漏洞识别
首先从PLC供应商网站手动下载更新报告。图三是一个示例,然后通过名称实体识别(NER)技术来提取漏洞信息,定义了13个漏洞实体,包括漏洞ID、漏洞类型、受影响版本、受影响控制器、逻辑指令和触发条件。触发条件包括约束类型、逻辑类型等多个子条件。从PLC制造商提供的编程手册中查询相关控制器特征,来获取漏洞的触发条件。
2.1.2漏洞定位
如图四中的示例所示,在控制器运行前,先插入一条易受攻击的指令,这里是ALMD,控制器存储其默认长度为0x11。在0x025a44和0x080b20处放置两个观察点监视,来定位ALMD的易受攻击的库代码。然后使用工程软件将固件处的0x11篡改为0xffffffff。当控制器运行时,便可定位到对应的库函数为0xf77b34处开始的库代码,也就是r2保存了ALMD的长度信息,而由于修改为0xffffffff,引发了内存损坏。最后,用一个漏洞重映射表来记录漏洞信息,每个漏洞有四项:固件版本号(FRN)、漏洞ID、涉及的逻辑指令和插入点(易受攻击指令的入口地址)。
2.1.3 补丁生成
图5展示了使用二进制挖掘工具BinDiff来比较漏洞代码和官方补丁之间的变化。使用支持向量机分类器将比较结果分为四类:添加、删除、未更改和更改。并记录了漏洞代码和官方补丁之间处理器的状态变化,例如序列(r0→r1,r1→r2,r2→r3)。然后调整补丁,改变的指令有两种情况:改变的操作数包含绝对地址,如图5中的sub_d7d570,并使用漏洞代码中的匹配寄存器来恢复官方补丁中的处理器状态,例如序列(r1→r0,r2→r1,r3→r2)。
2.1.4 补丁验证
首先,补丁行为不能包含功能升级系统中常见的数据结构和代码逻辑重构变化。其次,补丁行为不能包含更新代理和扫描周期过程,因为设置此类地址可能会导致递归陷阱。RLPatch可以识别三种漏洞类型:缓冲区溢出、释放后使用和可直接观察到的功能错误。为了确定补丁是否安全,本文在 PLC 运行时执行验证。通过编写一个控制程序,并用它来测量补丁部署后完整实时补丁过程的时间。
2.2 实时补丁
该代理的设计利用三个内置组件来实现对具有硬时间限制的 PLC 的实时修补。如图6所示,它们是①从补丁部署器传输修复信息的调试端口、②触发更新操作的异常生成机制以及③在未修改的运行时系统和更新代理之间共享异常的异常向量表(EVT)。
2.2.1 补丁插入
补丁部署程序使用实时调试界面将补丁配置文件传输到更新代理。通过降低传输例程的优先级,使其不会中断其他任务的正常执行。为了减少中断延迟,使用轮询模式的调试通道异步传输修复信息。此外,该通道有助于最大限度地减少对执行时间和内存使用的影响。更新代理维护一个补丁表,包含更新点、蹦床和返回点。为了避免在补丁插入期间影响控制器运行时间,选择看门狗时间和扫描时间之间的空闲时间作为插入补丁信息的安全更新点。具体来说,基于每个读或写操作的基线时间,代理在每个扫描周期的末尾接收并插入固定数量的补丁信息字到存储器中。
2.2.2 触发更新操作
更新代理使用有缺陷代码的入口点通过内存映射高级外设总线 (APB) 接口对断点寄存器进行编程。一旦基本配置完成,调试单元就会持续监视当前出现在地址总线上的值。一旦存在补丁插入条件 core.addr ∈ {i1, i2, …, in},即断点地址进入指令流水线的执行阶段,就会导致处理器暂时停止,调试组件生成预取中止异常(Prefetch Abort)。当然,也可以使用软件断点来触发更新操作,也就是ARM上的bkpt指令。
2.2.3 更新处理程序
为了实现更新操作,本文分别为预取中止和未定义指令异常设计了一个自定义更新处理程序。算法1 描述了更新处理程序的控制流重定向。
另外,为了避免更新代理和运行时系统上的异常共享冲突,提供了一个异常调度程序,优先考虑原始运行时异常而不是代理异常。
2.3 代理集成
将更新代理与控制器的原始运行时协同工作。尽管工业控制器涵盖了广泛的应用(从电梯操作到燃气管道),但它们的操作流程包含在三个基本的固件组件中:引导加载程序、执行加载程序和功能固件。
2.3.1 引导加载程序
包含异常向量表和硬件监控器。EVT 定义了一组连续的异常,每个异常的格式为“ldr pc, [pc,#xxx]”,xxx 表示异常处理程序相对于当前程序计数器的入口点。因此,通过使用这种形式在反汇编的引导加载程序中搜索异常处理程序。找到 EVT 后,通过上面提到的异常共享方法在其上安装自定义更新处理程序。而硬件监控器乐意通过查找立即值来发现,通过分析立即值之后的流程可以推断出其具体含义。
2.3.2 执行加载程序
包含一个验证算法,用于在加载新上传的固件之前确认其未损坏,包含CRC和MSUM算法。所以通过使用关键字 eor 和 add/adc 来搜索汇编代码中所有潜在的 CRC 和 MSUM 算法。本文使用 JTAG 接口将 RLPatch 植入控制器。在理解了验证算法之后,使用Schuett的方法来揭示固件的标头结构并重新计算其校验和数据
2.3.3 功能固件
检查功能固件内部的实时调度程序和扫描周期部分,以集成更新代理的四个功能:堆栈设置、代理初始化、补丁插入和补丁更新。在固件功能的启动序列中安装更新代理,然后初始化代理的状态机以启动通信链路。为了定位扫描周期,逐步调试处理器,直到控制器进入不间断状态。此时,通过从处理器规格中查询内存映射表来获取外设在内存中的映射间隔,然后在内存映射I/O(MMIO)操作中使用目标映射地址来识别扫描周期。根据处理器内存映射表中的MMIO端口,使用表示法ldr Rx,=addr来定位输入和输出操作,其中addr是MMIO端口中的任意内存地址。
3. 实验
本文实现了一个原型并将其部署在三个广泛使用的罗克韦尔 PLC 上。在控制器运行期间成功修复了 20 个现实世界中的漏洞,且开销恒定且可以忽略不计。
还通过启动三种攻击场景来评估 RLPatch 对现实世界天然气管道的系统影响。评估结果表明,RLPatch能够快速恢复并屏蔽整个控制系统免受攻击。
4.总结
本文设计了一款针对PLC的实时补丁机制。难点主要在于是针对无源代码的PLC,因此很多数据地址都是需要探测出来,无形地增加部署难度。而对于补丁本身,是用官方补丁进行地调整,所以本文的关键点不在于哪里有漏洞,漏洞是什么,以及漏洞该怎么补,这些官方都已经做了离线的补丁,本文关键难在于,针对无源代码的PLC,这就类似一个黑盒,需要一步步地探测出来漏洞的实际位置,挖掘真实补丁与官方补丁的差异,如何将官方补丁实时热补丁到运行中的PLC上。所以本文只针对一些正在运行不太好关闭的PLC,然后相当于是在另一个同样的PLC上,做了一遍热补丁的实验,论证了这个补丁可以这样热补上(见2.1.4,是需要手动补丁验证的),然后再实际中进行热补丁。
原文始发于微信公众号(COMPASS Lab):RLPatch:针对PLC的实时热补丁