Ethernet/IP协议是由控制网国际有限公司(ControlNet International)的技术工作组联合ODVA(Open DeviceNet Vendor Association)于二十世纪九十年代构建的,相较于Modbus协议它是一个很年轻的协议,制定这个协议的原因是在二十世纪九十年代随着现场总线控制技术的成熟,PLC等智能设备更多的也更方便的接入,现场总线控制技术(FCS)就应运而生了,但是在当时的技术条件下,商业以太网有着较高的延迟性,商业以太网在工业应用中传输延滞在2~30ms之间,这是影响以太网进入工业领域的重要原因,从而开始了对工业以太网的研究。时至今日,随着技术的发展工业以太网已经成功的应用到工业的生产当中,但是也出现了多种现场总线标准,现场总线国际标准IEC 61158承认的现在有10种类型总线的国际标准,其中就有着Modbus-IDA、Ethernet-IP等,本次我们就来介绍一下Ethernet/IP协议。
上文中我们也提到过Ethernet/IP协议是由ControlNet International和ODVA两个组织推出的,所以它和DeviceNet和ControlNet一样是基于CIP(Common Industrial Protocol通用工业协议)作为应用层协议基础上开发的,这是一种面向对象的协议,可以提供一系列标准服务,包括通过隐式和显示的方式对网络设备进行控制,后文我们再对CIP进行详细介绍。回到正题,Ethernet/IP协议的流行是因为主要的自动化系统制造商Rockwell公司和Allen-Bradley公司围绕Ethernet/IP协议对设备进行了标准化,此后如Omron等公司也对设备添加了支持,自此Ethernet/IP协议开始流行(主要在美国)。
Ethernet/IP数据包的组成结构可以分成两部分,CIP协议部分和Ethernet/IP协议部分
我们从CIP开始讲起,Ethernet/IP协议中的CIP帧包括了命令、数据点和报文信息,CIP帧主要包括CIP设备概要信息层、应用层、表示层和会话层,这里多提几句,CIP数据包在通过以太网发送前必须要经过封装,而且要根据请求的不同给不同的报头,报头体现了数据的重要性,CIP数据包在以太网中传输的时候具有特殊以太网报头(IP头、TCP头和封装头)这种方式使得CIP数据包通过TCP或UDP传输并能够由接收方解包但是对比于DeviceNet和ControlNet协议,这种方法使得协议效率降低,报文头长度可能比数据还要长,占用网络负担增大,所以Ethernet/IP更适合传输大的数据包。
这里我们先对上图中的一些名词做一下解释,设备概要信息就不多说了,就是对象结构和行为的一个完整的说明,通过这个来实现设备的互操作性和互换性。主要来看一下这个CIP应用层对象库,在CIP协议里定义了大量的对象**,一共定义了一系列46个对象,但之前提到的三个协议都存在部分不通用的对象,其中Ethernet/IP协议由一个,ControlNet有三个,DeviceNet有一个,CIP对象类可以分为三类,这种专有的叫做网络特定对象,第二种是通用对象比如标识对象、报文路由对象都属于这一类,最后一种是应用特定对象如寄存器对象。之后的CIP数据管理服务显示报文、I/O报文(隐式报文)和后面的CIP报文路由管理可以合成报文协议和数据管理来讲,报文协议就是在CIP和应用间连接建立后发送节点和接受节点通过双方的连接标识符对连接和报文进行确认。而数据管理是对对象的数据结构和编址类型进行定义。最后我们再区分一下显示报文和隐式报文
显示报文:用于在传输时不追求实时性,对时间要求不严格内容,比如程序的上传下载,设备配置信息等。
隐式报文:传输I/O数据。
首先我们来看一下CIP协议封装数据包的结构
那我们就从头来看,
命令:两字节整数,要求与通用工业协议规范中某条特定指令相关,在CIP协议中,即使设备不认识这个命令是什么也必须接收,而且异常处理时必须确保连接不中断,这种设计很好的保证了协议的稳定性,降低了中断连接的风险。CIP协议本身有着实时性、确定性、可重复性、可靠性等特点。
长度:两字节整数,表示数据部分长度,不存在则为0,比如说请求数据包就不存在数据部分
会话句柄:由目标设备生成,返回至会话发起方,作为一种凭证,证明你可以和我进行对话,这个凭证在接下来的对话里还会用到。
状态码:反应了接收方对设备发送的命令的执行能力,0为成功执行,在请求数据包中永远为0,下面列出其他几种状态码。
0×0001无效或不支持的命令(这种命令异常处理时不中断运行)
0×0002接收方处理命令资源不足(这里我没找到相关资料描述,但根据CIP协议高稳定性原则推测应该会进行不中断运行等待资源足的时候进行执行)
0×0003数据格式不正确或数据不正确
0×0065接收到无效数据长度
最大延迟:由于工业以太网对实时性要求高,这里的内容是这个包经历的最大延迟。
发送方上下文:命令发送方会生成六个字节的值,接收方要原封不动的发回去,这个内容可以理解为回复的一种编号,发送方知道接收方收到了对应编号的报文,回复的是对相应报文的回复。
选项:始终为0,不为0的话包会被抛弃。
命令相关数据:这就和我们接受和发送的命令和自身情况有关了。
这一部分我并没有找到源码,但是找到了一份用python3实现的模仿版本,就这份代码简单分析一Ethernet/IP的工作流程,下面进行重要部分的介绍。
先看一下初始变量。
首先出现的就是目标ip和端口,可以看到默认的设置中目的ip是空目的端口是44818,这就解释了为什么Ethernet/IP协议一般运行在44818端口上,接着往下看,会话句柄session_handle,因为这个是在会话发生后目标设备生成,所以初始为空,后面是保持会话时间60s。第二部分就我们知道的来推断应该是最大延迟部分,设置两个列猜测是用来进行出入,上面内容是数据流和数据报。再看第三部分,分别设立ignoring_sender_context和internal_sender_context来进行上下文处理,下面开辟缓存区来用于接收数据。最后一部分是存在目标端口时候进行下一步。
这一部分就是将目标ip进行连接,不行就报错。行就执行start函数。下面还有一部分是端口是2222情况下的处理,基本相似就不提了。
我们再来看一下连接管理
可以看到这一部分主要是控制连接时间和延迟,如果在连接状态就开始发送内容,所有来自TCP流的数据的内容都将被封装,然后封包发送到队列里进行等待,在连接时如果没完成将会延迟关闭时间好让内容可以完成传输。下面看看发包。
简单来介绍,这一部分就是对所有处理完内容的一个整合,按照顺序将内容以协议规定的形式进行组合,填装报头,命令码会话句柄和数据内容然后进行发送。还有一些注册会话部分我们就不分析了,其中的内容只是根据是否为有会话句柄的来将内容加入包中。
简单的总结一下,ENIP协议的工作流程就是先对目标进行判断,然后开启连接,生成包所需数据组合起来然后再将包发送出去。
下面我们来看看实际的数据包内容。
我们直接来看封装头的内容
Command:命令,list identify表示检测到Ethernet/IP列表身份的命令。
Length:长度为0,因为这个是一个请求包,没有数据长度。
Session Handle:记住这里为0,后续的返回包我们看看是不是一样的。
Status: Success(0×00000000)和我们之前看到的一样。
Max Response Delay:0,很好,这个包发送是实时的。
Sender Context:0000c1debed1,正好6个字节的内容,等看看是不是一样的。
Options:0,上文也提到过这里为始终为0。
再来看看这个包的返回包。
由于内容都差不多,我们就看几个点就好了。
Length:长度不是0了,因为后面有了数据内容。
Session Handle:还是0,和请求包是一样的。
Sender Context:和请求包一样,证明是对应的返回包。
Command Specific Data:数据内容,这部分不需要细看了,里面是一些设备信息,这些在工控渗透时可能会用到。
在文章最开始的时候我们提到过,Ethernet/IP协议是一个现代化程度很高的协议,但是现代化程度再高也会有安全隐患,Ethernet/IP协议一般运行在44818端口上,还有2222端口来传送显式报文和隐式报文,可以通过这两个端口对相关设备进行扫描。
这里简单介绍一下常见的攻击方式
通过使用脚本进行扫描,再将扫描结果和nmap得到的内容进行对比,可以判断出设备是否在防火墙内,之后就可以使用内置的命令进行会话句柄的构造,进行下一步的攻击。
这一部分不细说了,中间人攻击大家应该多少有些了解,在上面已经构建了会话句柄,可以直接更改序列号就行了。
Ethernet/IP协议终止CPU攻击
Modbus协议有一个90功能码可以终止CPU运行,Ethernet/IP协议中也可以通过软件实现相同效果,通过使用相关软件可以达到同样的效果,甚至可以达到令以太网卡崩溃的效果。
我知道的内容大概就是这些,如果文中有什么不对的地方希望能告诉我一下,大家一起学习。QQ:434483362
*本文原创作者:zhangshuo,本文属于FreeBuf原创奖励计划,未经许可禁止转载