前言
Codesys是全球最著名的软PLC内核软件研发厂家德国的3S(SMART,SOFTWARE,SOLUTIONS)公司发布的一款与制造商无关IEC 61131-1编程软件及工控设备内核(runtime SDK)。CodeSys被大量的应用在工控领域中,市场占有率35%,被誉为“工控界的安卓”。
CodeSys属于私有协议,官方从未披露其报文格式和实现细节。为此,研究人员不得不从逆向工程的角度去分析该协议(除非购买昂贵的授权获取到相关源码)。如今CodeSys V2版本逐步被淘汰,取而代之是V3版本,本文将会从逆向角度介绍该协议的格式以及授权机制。
CodeSys覆盖的客户如下所示(只列出一部分,详细情况参考官网 http://www.codesys.cn/list-Partner.html):
协议格式
V2与V3
CodeSys V2版本的协议相对简单,如下图所示。报文头是以“\xbb\xbb”打头,紧接着就是长度字段,后面接着功能码,图中的功能码为92,最后就是数据部分,从结构上来说,V2的协议相对简单。
V3 与V2的协议完全不同,该协议架构如下图所示,该协议栈分为四层:
1.块驱动层——Block Driver layer
2.数据报层——Datagram layer
3.通道层——Channel layer
4.服务层——Services layer
研究方法
通过获取到了某个使用CodeSys V3 PLC固件,使用IDA进行分析,分析出协议的处理流程和功能码含义。
CodeSys V3通过ServerRegisterServiceHandler注册对应service_group处理的handler(相当于传统意义上上的功能码):
所有注册的服务如下图所示(图示为本人编写的LUA解析插件的内容),可以看到CodeSys协议包含相当多的功能,包括用户管理,日志管理,文件传输等功能。
CodeSys V3
为了帮助理解和研究CodeSys协议,本人不得不实现一个Wireshark解析插件来解析数据报文,下面简要介绍每个layer来分析V3数据包格式。
块驱动层
块驱动层的组件的主要任务是创建通过物理或软件接口进行通信的能力。任何块驱动程序组件都是一个“输入点”,用于接收数据包和传输数据包的点。
数据报层
该层的主要目的是路由数据包,检测 CODESYS 网络中的节点,并将数据传输到下一层。
简单介绍上面关键字段的含义:
Magic 字段:
通过 CODESYS协议生成的数据包的魔数。该字段的大小为一个字节。
Hop count 字段:
表示在网络上接收到的数据包所经过的CodeSys节点数。每当一个 CODESYS 网络节点收到一个数据包并将其重定向到另一个 CODESYS 网络节点时,它就会递减hop_count的值。如果一个节点收到了一个数据包但不是它的最终接收者并且hop_count字段的值等于 0,则节点将丢弃此数据包。本质上,该字段让 CODESYS 节点的网络免于无休止地转发数据包。
packet_info字段:
表示数据包相关设置。
priority字段:
指定处理数据包的优先级。以下数值用于指定优先级:0 – 低,1 – 正常,2 – 高,3 – 紧急
length_data_block字段:
表示接收者可以接受的最大数据大小。
Service id 字段:
服务ID,指示特定服务器必须处理接收到的数据。
通道层
服务层
该层的主要任务是查询请求的服务并传输其操作设置。服务层的任务包括对在该层传输的数据进行编码、解码、加密和解密。
protocol_id字段:
使用的协议的ID。此ID指示哪个协议处理程序修改了数据以及应使用哪个协议将数据传输到服务。
Header size 字段:
protocol_header的大小。该字段的值不包含先前字段和当前字段的大小。
Service group字段:
被查询服务的ID。如果在ID中设置了最高有效位,则意味着该消息是来自服务的响应。
Service ID 字段:
命令的ID,这个ID决定了服务执行的功能。
session_id 字段:
会话的ID,包含接收到的会话或空会话的值。
data size 字段:
protocol_data字段中数据的大小。
授权流程
CodeSys 在新版V3通讯协议中已经默认开启授权机制,用户必须设置账号密码才可以进一步使用。在设置密码成功后,用户必须登录后才能对PLC进行控制管理以及编程组态等操作。但是具体的算法之前从未有人披露过,下面我将简单介绍其流程和相关算法。
密码哈希存储
新版CodeSys V3通讯协议账号相关信息默认存放于UserDatabase文件中,旧版本的CodeSys V3的密码是MD5加密的,新版做了改进,使用了scrypt算法来进行存放。该算法在CryptoDeriveKey中实现,如下所示:
UserDatabase文件内容如下,其中“admin”为用户名,后面接着的是密码哈希信息。
About RCE
密码保护是保护CodeSys PLC的重要防线,一旦攻击者突破这层防线,就可以利用注入代码的方法插入任意shellcode执行RCE。这是由于CodeSys为了实现可编程功能,会将用户编写的代码统一编译成机器码,并且由RunTime调度执行,执行的过程没有在VM或者sandbox中,这就导致攻击者可以插入任意的机器码(例如LINUX平台上的反弹shell)来完全接管PLC。
如下所示,使用该方法在PLC开启了Vxworks上的Telnet服务(原先是关闭的):
总结
本文详细介绍了CodeSys V3的协议格式和授权流程,并分析了密码保护机制的安全性问题,然而设计是美好的,但是工业现场实践过程中往往存在很多问题。
1.由于很多厂商购买的CodeSys 需要每年花费高额的费用来获取更新支持,绝大多数厂商在购买源码后不在进行更新,这导致目前很多PLC使用的CodeSys版本仍然停留在非常老的版本,仍然受N-day的影响。
2.电气工程师安全意识缺乏,很多部署在现场的PLC根本没设置密码。
3.在厂家都已经停止维护的情况下,CodeSys V2 版本仍然被大量使用,该版本的RCE漏洞更多(N-day以及0-day)。
参考链接:
https://ics-cert.kaspersky.com/reports/2019/09/18/security-research-codesys-runtime-a-plc-control-framework-part-2/
更多【[工控安全]CodeSys V3 协议及授权机制分析】相关视频教程:www.yxfzedu.com