作者:漏洞研究人员Flavian Dola(@_ceax)

编译:天地和兴研发中心

2010年7月暴出的“震网”(Stuxnet)蠕虫攻击事件,引发了国内外网络安全行业的极大关注。多年以后,这一里程碑式的网络攻击事件仍然是业界,特别是在关键信息基础设施保护行业,必然会提及的经典事件。其重大意义不仅在于其实施攻击的复杂性、APT特性,更在于其通过网络攻击手段达成了对传统物理空间的打击效果,具备典型的网络战特征。在后期的跟踪分析过程中,业界逐渐认识到震网、毒曲、高斯(Gauss)、火焰,这些高度复杂的恶意代码间存在着同样的背景联系。

在2019年4月新加坡举行安全分析师峰会上有研究者披露了与Stuxnet关联的APT组织--GossipGirl Supra Threat Actor,其使用了Flowershop恶意软件框架和一个名为Stuxshop的组件。研究人员认为Flowershop是与Duqu、Flame和与NSA关联的Equation Group存在某种关联的。种种迹象表明,“震网”不死,“火焰”未灭,业界对这组高级威胁的认识远未清澈见底。

空中客车网络安全部门联合施耐德电气对类似震网的网络攻击进行研究复现,用类似的战术、技术和过程运用于其它类型的PLC设备,达成了既定的目的和意图。这表明大量老旧的PLC对“类Stuxnet”攻击仍然很难免疫,“类Stuxnet”攻击对于工业领域的网络安全仍然是头顶高悬的“达摩克利斯之剑”。

本研究报告由空中客车网络安全部门施耐德电气联合审查。详情请参见https://airbus-cyber-security.com/security-notification-schneider-electric-airbus/的安全警告。

本文将介绍对施耐德Modicon M340 PLC进行 "Stuxnet类型 "攻击的过程。最终结果是,使用C语言设计了一个自动化程序,并成功在M340 PLC上执行了此恶意代码

该项工作在旧版本的Unity(Unity Pro S V7.0 - 2012)和M340(BMX P34 2020 2.50 - 2012)进行,是空中客车网络安全部门为提供ICS高级安全培训课程而开发的。

重温Stuxnet

Stuxnet攻击的目标是西门子S7-300和S7-400 PLC。为了感染设备,Stuxnet病毒从一台计算机传播到另一台计算机,直到它找到一个工程师站为止。工程师站主要的作用是用来设计自动化程序,并将程序上传到PLC上运行。Stuxnet攻击的特定软件是西门子PLC的STEP7。

图1 :STEP7/PLC之间的通信

从图1中可以看到,STEP7使用s7otbxdx.dll导出的函数与PLC进行通信。例如:

  • s7db_open和s7db_close 允许与PLC建立一条连接;

  • s7blk_findfirst和s7blk_findnext 允许获取块代码列表;

  • s7blk_read s7blk_write s7blk_delete 允许以块形式写入/修改程序代码;

  • s7ag_bub_read_var s7ag_bub_write_var 允许修改程序变量。

Stuxnet将原始的STEP7 DLL文件(名为 "s7otbxdx.dll")替换成自己的恶意版本。这种方法被称为 "反射DLL"。

图2:在STEP7中反射Stuxnet DLL

反射DLL的目的是为了拦截所有与PLC通信的函数,以方便攻击者将他们的字节码注入PLC。

PLC执行的字节码格式为MC7,这是西门子开发的专有汇编代码格式。

Stuxnet通过写入特定的PLC变量来控制MC7恶意字节代码的执行。Stuxnet的MC7代码可以拒绝或允许执行合法的自动化程序。

现在已经了解Stuxnet的基本工作原理,将尝试在Schneider Electric PLC上执行类似的操作。

工程软件分析

施耐德客户使用EcoStruxure Control Expert工程软件,以前被称为Unity Pro。下面描述如何开发一个自动化程序。

图3:工作流程

首先,自动化工程师在Unity中设计一个逻辑自动化过程。可以用几种不同编程语言来设计,例如Grafcet、梯形图、结构化文本和指令列表。然后在Unity中进行编译。

Unity将程序转换为一种汇编语言。在CPU 340-20的例子中,处理器是ARM920T,它将生成ARM汇编代码。然后Unity对其进行编译以生成相关的字节码。

自动化工程师将程序上传到PLC,然后Unity传输字节码。一旦工程师将PLC置于“运行模式”,PLC的OS(VxWorks)将周期性执行自动化程序的字节码。

其最终目标是构建自己的代码并注入PLC。

如果在编译程序时监控所有文件访问,会发现Unity试图创建一个扩展名为 "C:\punitemp\BackendSection1.asm "的文件。

图4:编译阶段的监视窗口

接下来,创建目录c:\ punitemp,查看重新启动编译程序时发生的情况。如上所见,创建了3个文件:BackendSection1.asm(图4),BackendSection2.asm和BackendScheduled3.asm。

这是BackendSection1.asm的摘录:

现在已有ASM源码产生的日志, Unity 添加了一些有用的注释。

例如:

  • I_B0是梯形图程序中的助记符;

  • $ LINK67 是我们的梯形图程序中引用的一个网络;

  • 在地址0x28000168处,有一个指向输入状态存储器映像的指针。

哪个DLL生成此日志文件?通过一些简单命令,如grep和string,可以使用明确名称(例如asmArm.dll)来找到对应二进制文件。

图5:asmArm 的导出表

asmArm.dll 仅导出3个函数,可以在上面添加一些断点。

图6:执行MyAsmArmSttream前的断点

当运行到MyAsmArmStream断点时,查看堆栈中的参数,可以看到第一个参数指向ASM源码文件。

现在,执行函数MyAsmArmStream, 看看会发生什么。

图7:执行MyAsmArmSttream后的断点

这是函数运行的结果:

  • 该函数返回一个缓冲区地址,该地址包含带有标签的结构,字节码的偏移量,最后是字节码本身;

  • 第二个参数包含一个指向整数(0x103e)的指针,该整数是返回缓冲区的长度。

因此,在标签DebugLabel2(bytecode+ 0x90)处反汇编了字节码,用来获取ASM源代码。

恢复原来的ASM源代码,故MyAsmArmStream负责编译过程。所以,如果拦截此函数,便可以注入自己的“恶意”代码。

感染Unity

为了拦截已编译函数,需依赖于 Reflective DLL方法。此方法尝试将asmArm.dll重命名为另一个文件(例如:asm_Arm.dll),并使用原名称(asmArm.dll)创建一个新的DLL。

为了使它正常工作,DLL必须执行以下操作:

  1. 导出与原文件相同的接口(本例中为3个函数);

  2. 加载原始DLL;

  3. 将执行流程转移至原始函数,但要拦截的函数除外(本例中为MyAsmArmStream)。

最后代码如下所示:

像之前看到的那样,MyAsmArmStream将缓冲区作为输入参数。这其中包含了ASM源代码。

下面是修改后的MyAsmArmStream函数:

  1. 在ASM源代码中知道用户自动化代码的结尾;

  2. 在ASM源码中加入我们的指令;

  3. 我们选择指令:submit r4,r2,#0x20000004;

  4. 调用原始的MyAsmArmStream函数;

  5. 在生成的字节码中搜索模式0x4242424242424242(submit r4,r2,#0x20000004的编译形式),并将其替换为另一个模式0xe1a08008e1a08008(NOP(无操作)指令的编译形式:mov r8,r8)。

现在有了新的DLL模板。必须将其生成想在PLC上执行的字节码。可以通过把模板中模式为0xe1a08008e1a08008的字节码替换成自己的字节码来创建自己的DLL。

因此,现在要在PLC上执行自己的代码,必须:

  1. 将原始asmArm.dll重命名为asm_Arm.dll;

  2. 将我们的恶意asmArm.dll放入Unity文件夹;

  3. 运行Unity并加载自动化项目;

  4. 编译此项目;

  5. 停止PLC运行当前的自动化程序;

  6. 将新的自动化程序上传到PLC;

  7. 运行PLC自动化程序。

m340固件和自动化程序的逆向

现在能够在PLC上执行自己的C代码。将要开发的首批PLC程序之一是内存转储器。

在经历某些崩溃之后(由于访问未映射的地址),获得了以下内存映射:

  • 开始:0x0 –结束:0x4000;

  • 开始:0x100000 –结束:0x120000;

  • 开始:0x200000 –结束:0x204000;

  • 开始:0x10200000 –结束:0x10201000;

  • 开始:0x10400000 –结束:0x10401000;

  • 开始:0x20000000 –结束:0x20B00000;

  • 开始:0x21000000 –结束:0x21200000;

  • 开始:0x28000000 –结束:0x28400000;

  • 开始:0x30000000 –结束:0x30400000;

  • 开始:0x40000000 –结束:0x40001000;

  • 开始:0x80000000 –结束:0x80100000;

  • 开始:0xFF000000 –结束:0xFF100000。

经过分析,确定了一些有用的段:

  • 在0x0处有复位/中断向量数组;

  • 在0x20000000处有OS代码。

确定自动化程序代码地址

地址为0x20043bdc的函数sas_UserCodeExec负责定期执行自动化程序代码。因此,需要了解如何获取将要加载代码的地址。

可以看到自动化程序的段地址为0x28000000。

还有一个包含许多自动化程序入口点的数组。该数组由任务编号索引。它可能对应于任务优先级(MAST,FAST等)。在例子中任务号是4。

可见自动化程序的入口点是:0x28000000 + 0x52 * 8 => 0x28000290。

识别操作系统功能

在VxWorks SDK文档的帮助下,可以很容易地识别OS相关的函数。例如,可以通过socket引用的字符串来识别Socket函数。

以下是在有效载荷中使用的一些有用的函数:

设计有效载荷

更改“恶意”有效负载的步骤如下:

  • 编译有效负载;

  • 修改asmArm.dll;

  • 将asmArm.dll复制到Unity安装文件夹中;

  • 启动Unity;

  • 加载自动化项目;

  • 重新编译项目;

  • 将PLC设置为停止模式;

  • 将程序上传到PLC;

  • 将PLC设置为启动模式。

这些步骤非常耗时。如果上传一个能够按需执行有效载荷的程序,则可以在线更改PLC行为。一个下载执行程序将完全符合需求。下面是它的工作原理:

  1. 为将要执行的有效载荷分配一个缓冲区;

  2. 将缓冲区地址发送到C&C(命令和控制)的IP;

  3. 从C&C下载有效负载;

  4. 执行有效载荷;

  5. 返回步骤3。

通过此程序可以修改自动化程序,然后将PLC转换为网络扫描器。又例如,PLC可以充当本地网络代理。

结论

可以对其他制造商的PLC进行Stuxnet类型的攻击。对于Modicon M340,此移植更容易,因为PLC本地执行ARM字节码(而不是专有的汇编代码)。

该练习可以有机会直接用C语言开发自动化代码来扩展M340的功能。可以执行底层操作,其他语言(例如Ladder,Grafcet)很难做到。

目前已开发了一个程序,可以随时更改逻辑程序(无需重复编译–停止–上传–在Unity中启动的整个步骤)。

如前所述,本文档中执行的任务是在较旧版本的Unity(Unity Pro S V7.0 – 2012)和M340(BMX P34 2020 2.50 – 2012)执行的。如今,该PLC仍被使用,但并未测试本文中所描述的方法在更新的软件版本上是否有效。

参考文献

1、https://airbus-cyber-security.com/applying-a-stuxnet-type-attack-to-a-modicon-plc/

2、https://threatpost.com/stuxnet-apts-gossip-girl/143595/

声明:本文来自天地和兴,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。