工控系统通常是由互联设备所构成的大型复杂系统,这些设备包括类似于人机界面(HMI)、PLC、传感器、执行器以及其他使用协商好的协议进行相互通信的设备。所有交互背后的驱动力都是软件,软件为工控系统中几乎所有部分的运行提供支撑。很多工控系统安全漏洞都归结于软件问题也证明了软件的无所不在。
据统计,超过40%的工控系统网络安全漏洞都被归结为由“不正确的输入验证”所导致,不正确的输入验证是指程序员假设对数据进行了某种限制,但是事实并非如此。在使用数据之前应确保所使用的数据正是程序员所期望的数据,但现实情况中工控系统软件安全的现状与期望值还有很大差距,不正确的输入验证并不是导致软件出错的唯一方式,工控系统应用程序还可能因为安全问题出错。除了硬件上的漏洞(如芯片设计与布局等)、协议中的漏洞和系统配置中的漏洞之外,工控系统应用程序中也存在大量漏洞。
(1)缓冲区溢出的漏洞缓冲区溢出漏洞在计算机安全史中可以追潮到很久之前。缓冲区是内存中用于填充数据的一块连续区域,缓冲区的常见用法是存储字符串,如“Hello world!”。缓冲区具有固定的长度,如果想将更多的数据填充到缓冲区,那么缓冲区就会“溢出”到相邻的内存区域。这些都是内存,但是特殊的内存区域可能存储了重要的内容,例如当前函数执行完成后将要执行下一条指今的内存地址。
程序经过编译后包含不同的段,其中很多段都包含了接收外部系统(文件、网络、键盘等)填充数据的缓冲区。一些比较有趣的段是栈段、堆段、未初始化的静态变量段向下生长(用于静态初始化的变量)以及环境段。每个段的作用各不相同,因此程序和操作系统对不同段的处理方式也不一样。有大量文献介绍如何向这些段注入数据以控制应用程序的后续执行,因此这种攻击形式又称为软件攻击。
现代系统以及编译器所具有的特性使栈攻击变得难度更高,但是很多工控系统软件并未使用栈保护机制。因为很多技术是近些年提出的,而工控系统的安全性往往落后于前沿安全技术。
(2)整型溢出:上溢、下溢、截断与符号失配
整型是编程中基本的数据类型。整型数据中仅仅包含数字,而数字可以解释为任何内容:可以是一个单纯的数字、一个 ASCI 字符,也可以是应用于某些数据的比特掩码或内存地址。因此,如果要对整型的不正确使用进行漏洞利用,那么理解整型如何工作是非常重要的。首先需要认识到,不同于数学中的整数,计算机中的整型数据不能一直增长到无穷大。基于CPU的架构,整型通常是固定长度的。现代的个人计算机大多基于64位体系结构,但是较早期的系统(工控系统中的大部分系统仍然如此)基于32位体系结构,部分小型嵌入式系统甚至还在使用基于16位体系结构的系统。无论整型数据长度是多少,其原理都是一样的,那就是固定长度的整型数据只有有限的表示能力,存在可以表示的最大数以及可以表示的最小数。对于32位无符号整型(非负整数)而言,所能存储的最大值是。转换为二进制形式,就是32个1,这也就是4GB所包含的字节数,正好是32位操作系统中内存容量的最大值。
鉴于这种类型的漏洞非常微妙并且非常底层,可以想象到在工控系统应用程序中很可能存在这些漏洞。整型相关的漏洞难以发现和调试,它们可能存在于应用程序中多年而未被发现。应对输入进行测试,看看意外输入是否会引起异常。了解二进制如何表示有助于选择“有趣”的值进行测试。
(3)指针操纵
借助指针都实现各种有趣的数据结构和优化,但是如果指针处理不当,则可能导致崩溃。我们在讨论EBP、EIP 和 ESP等存储器时,其中的P就是指这种类型的“指针”。尽管通用指针也可以存储在内存中,而不仅仅存储在CPU寄存器中,但是内存地址可以指向内存中的任何地方。有些地址可以指向正在运行的程序内存区域,有些地址可以指向多个程序使用的共享库内存区域,还有些地址可以指向不属于任何程序的内存区域。如果在C语言中使用过指针,并出现了一个分段错误,那么很可能是因为“间接引用”了一个无效指针(也就是说,试图获取存储在指针中的地址的值,即内存中的值),它们很容易让整个程序崩溃。
攻击者还可以借助某个数值来绕过安全保护(通过跳转到函数中间;该安全保护在敏感的值)。也可以借助某个数值使其指向攻击者控制的数据缓冲区,从而导致任意代码执行漏洞。更高级的语言,如Python、PHP、C#等往往没有指针所带来的问题(尽管这些语言并不一定具备对抗上述问题的能力),因为这些语言自身对指针进行了隐藏以防止程序员直接访问。这正是它们经常被提到的优势之一。这种便利的特性被称为托管代码(Managed Code),这意味着由开发语言自己管理内存的分配和释放。但是许多工控系统应用软件,特别是为嵌入式系统编写的软件,大多采用C语言。
(4)格式化字符串
格式化字符串是用于指示数据格式的具有特殊结构的字符串。与之前提到的某些漏洞一样,格式化字符串漏洞源于直接使用了用户提供的数据,而不对其进行验证。在这种情况下,用户据就成了格式化字符串的参数。
一旦攻击者知道缓冲区在哪里(无论是某一个内存地址的绝对地址还是相对地址),攻击者就可以将执行流导向那里。如果缓冲区中包含恶意指令,那么在某种程度上就能够实现对机器有效漏洞利用,从而导致该机器完全处于攻击者的控制之下。攻击者也可以通过覆盖指针将执流重定向到含有恶意指令的缓冲区。尽管攻击者可以在目标机器上执行任意指令,但对于工控系统攻击仅仅更改变量的值就足够了。修改一个字节的信息就足以使进程崩溃,完成一次Dos 攻击。
在IT场景中,程序如果崩溃就会立即重启,被攻击的目标可能只是略微觉得有些尴尬,停机一段时间就没事了。但在工控系统中,DoS攻击是一个大问题。它可能导致的不只是经济损失,还可能是环境破坏甚至人身伤亡。DOS攻击还能阻止操作人员查看甚至控制设备。所以永远不要低估比特和字节的价值。
格式化字符串主要是(虽然不限于)C、C+编程语言的问题,但在工控系统的开发中C语言大受欢迎,因此工控系统软件易于发现大量已公布的格式化字符串漏洞。
(5)目录遍历
出于这样或那样的原因,有很多程序需要访问文件系统,有些程序可能需存储。目录遍历漏洞就是典型漏洞中的一种。无论出于哪种原因,程序员都需要使用字符串来指示文件的路径。当用户输入的数据对于路径字符可能造成影响时,是因为访问大多数文件系统时遇到了特殊的字符和字符串。
工业控制系统中有大量人机界面和其他设备开始使用基于Web的访问方式,尽管.NET和Java在人机界面的实现中很常见,但是PHP仍然是最受欢迎的服务器端 web编程语言之一。如果攻击者自己也购买了一套应用程序或设备,对应用程序、设备固件进行了深入研究,实现了对关键文件的篡改,并最终对用户的应用程序或设备固件进行了替换,使得程序或设备在执行其所有正常功能的同时,还在后台执行其他任务。该文件就可能一直运行,而没有人注意到数据已经泄露或者已经被植入了黑客稍后会用到的后门。
任何能够导致攻击者代码在目标系统上运行的漏洞都是危险的。设计行之有效的过滤器对数据进行清洗、保证其安全性是非常困难的,这也是为什么作为程序员根本不在这方面花费精力的原因。即便是在远程的数据中发现了一些可疑的东西,把这些数据全部扔掉,并在日志中将其记录为潜在威胁。但令人担忧的是,许多程序员并不会采取这样的做法。
(6)DLL劫持
作为软件设计工作的一部分,通常会对常用组件进行分解,从而实现组件的多处复用。这一原则无论对于代码片段,还是对于操作系统级别的软件,抑或是介于两者之间的其他软件都是适用的。减小程序规模、提高代码模块化的常用方法之一是使用动态链接库(Dynamic Linked Library,DLL)文件。DLL文件在很大程度上就像一个完整的程序,其中包含一系列可以调用的函数。但是,DLL文件不能独立运行,必须借助其他程序才能加载。借助DLL文件能够对应用程序的部分功能进行分离,从而在不对程序进行改动的情况下实现对整个程序的更新。DLL的许多优点使得 DLL 文件用起来非常方便,但若未对 DLL进行正确处理,DLL的使用可能会给应用程序带来非常严重的安全漏洞。
从攻击者的角度来看,DLL劫持是一种获得程序访问权限的好方法,并且同缓冲区溢出、格式化字符串等方法相比,DLL劫持更加可靠、稳定。在这种情况下,程序可以有意加载攻击者的代码并直接对其进行调用,程序只需要将执行流程转交给恶意代码就可以了。除了根据搜索顺序提前创建DLL文件之外,如果攻击者能够覆盖DLL文件,那么显然也可以执行恶意代码。在部分保护不够严密的系统中,覆盖DLL文件的难点在于,除非手动包含原始代码,否则可能会对原始代码造成破坏。但是如果先加载真正的DLL文件,再将函数调用传递给恶意代码,那么保证原文件中代码的正确执行就容易多了。这实际上是一种中间人攻击。应用程序的功能都会实现,程序的预期效果也都会达到,然而,攻击者的代码也会在中间悄然运行,可以记录信息、修改参数,以及其他自己想做的事情。
(7)暴力攻击
暴力通常意味着尝试每一个可能的选项,从中找出最终能够奏效的选项。此方法通常用于口令破解。在某种程度上,DoS攻击也可以应用暴力攻击,因为它们倾向于持续发送一段代码,或者不断消耗内存直到目标崩溃。
从历史记录来看,由于需要耗费大量时间,暴力攻击并不是一个可行性较高的选项。然而现在随着 CPU 速度的不断提高以及分布式计算、并行计算、云计算的广泛应用,暴力攻击已经成为一个更为可行的选择。如果用户的PLC只使用8字符的口令,那么即便用一台低端的笔记本计算机也可以在一两天内穷举完所有的口令。应对暴力攻击的对抗措施之一是使用更大的状态空间,也就是增大可能值的集合。这可能涉从16位值到64位值的转换。有时,这就足够了。但是如果采用随机数生成器(RNG)生成值,那么RNG算法中的弱点可能导致所生成的随机数被预测到,从而导致状态空间的规模大幅削减。
在许多情况下,特别是针对登录进程的攻击,暴力攻击会在线进行,有时则可以取出数据进行离线攻击,这样的话系统管理员对于攻击行为无从知晓,这正是破解WPA2密码的方式。
如有有人在对无线信道进行嗅探时拦截到一个特殊值(握手信息),然后使用离线破解软件(如aircrack-ng或reaver)可以握手信息发起暴力攻击。无论何时,只要经过散列、加密或编码的值,并且该值是安全方面的敏感信息,那么就存在通过暴力攻击发现原值的风险。对于Dos攻击,只是重复进行TCP连接,甚至只是ping一个端口,就可能足以降低设备的运行速度或导致设备中断运行一一曾经有过仅仅通过Windows令行下的ping命令就使得PLC设备无法使用的例子。
除非系统设计者采用专门的设计来阻止暴力攻击,否则在系统中的某个位置就很可能存在暴力破解漏洞,很多工控系统软件均是如此。