其他分享
首页 > 其他分享> > 工控安全入门(三)—— 再解S7comm

工控安全入门(三)—— 再解S7comm

作者:互联网

之前的文章我们都是在ctf的基础上学习工控协议知识的,显然这样对于S7comm的认识还不够深刻,这次就做一个实战补全,看看S7comm还有哪些值得我们深挖的地方。

本篇是对S7comm的补全和实战,阅读本篇之前一定要先阅读以下文章,掌握基本的S7comm知识:

工控安全入门(二)—— S7comm协议

本篇文章依然有很多笔者自己摸索的内容,可能存在错误,同时也发现了wireshark对于s7comm解析的几个模糊之处,希望大家能帮忙指正。本次选用的流量包来自于github的w3h,有兴趣的同学可以下载下来仔细研究。

https://github.com/w3h/icsmaster/tree/master/pcap

 

s7comm_reading_setting_plc_time

首先来大体上看一下流量包

可以看到,主机是192.168.1.10,设备为192.168.1.40,首先是tcp包,但传输失败了,之后调用arp解析到了40的mac地址,接下来通过tcp的三次握手来建立连接,通过COPT的CR和CC建立了通信,最后再利用S7comm的job建立了通信。这是s7comm在两个设备建立通信的一般流程,到此为止,利用我们上一篇文章的知识都可以完美解决,可是再往下就出了问题,这个userdata的包是个啥?

我们打开看看具体的细节

可以看到大体结构和我们上一篇文章所讲的无异,最主要的就是PDU的类型不一样导致后面的跟的参数我们看不明白了。不要慌,这个时候就要慢慢来分析了。

首先ROSCTR变为了0x07,wireshark给出的是Userdata,翻译过来是用户数据?那就不对了,如果我们把设备比作电脑的话,我们之前用的job更像是一般用户的操作,而这个Userdata嘛更像是程序员的操作,比如说它可以用来读取SZL(后面会说这是个啥)、调试、安全等等方面。(是不是感觉有点像modbus的0x5a操作?)

也正因为它包含了复杂的功能,所以Parameter比起job更为复杂,这里主要要关注两项。

那么szl到底是个啥呢?其实它是系统状态列表的缩写,本来标准缩写应该是SSL(system status list官方手册上也是这么写的),但因为西门子是德国的,在德语中”状态“是z开头的,于是就成了szl。简单说就是当前设备的状态信息,包括很多重要的信息。可以看到Data中有SZL-ID和Index两个字段,这俩就用来指示要读取的内容

ID占两个字节,意思分别如下:

这里SZL-ID的完整意思就是,从编号为0x32的局部列表(写作W#16#xy32)中提取0x01的内容(写作W#160132),0x01意思是通用通信数据,index是0x04,即对象管理系统(写作W#16#0004,这个index旧版的手册上没有),这就组成了完整的请求。根据我们写作的格式查找手册即可查询具体作用。

再看看这个pdu的回复,可以看到data tree就是读取到的信息

接着看下一个PDU,对应的包是第16个

还是和上面一个套路,不过这次的SZL-ID和index都是0,这可怪了,按照上面我们给出的分析方法,似乎找不到这个特殊的家伙,这时候我们就需要结合response来进行逆推了

可以看到返回了一大堆数据,而这些很眼熟啊,正好就是SZL-ID啊,所以我们就可以推断出,上面一个request的作用是检查所有存在的局部列表。

后面的pdu就不再展开分析了,都是在读取cpu类的相关信息,只不过是指定的局部列表不同、index不同罢了,大家有兴趣的可以自行往下看。

 

step7_s300_stop.pcapng && snap7_s300_stop.pcapng

stop绝对是对工控设备最重要也是最容易出事的指令了,一个不小心就会酿成大祸。这里给大家分析两种stop的包,首先简单说一下背景:

首先来看snap7的吧,流量包就两个pdu,是我们已经熟悉的job和ack_data,我们打开job来详细看一下

这里可以看到整体十分简单,唯一一个有疑惑的地方就是PI Service,这个PI Service是程序调用服务的意思,它标识包括启动、停止等等的服务,它本身最常用在0x28的function中,0x28标识PI service包,再通过PI service这个字段来标识具体的功能。因为stop的重要性,所以在s7comm中把它从PI service包中单独拿了出来,作为stop包,并将PI service字段置为P_PROGRAM。

再看step 7的stop

比起snap 7的stop,step 7在其基础上又额外有两个pdu,都是userdata,我们一个一个看

结合上面的知识我们知道:直接看function,0000标识push,0100标识是cpu类的方法,子方法说明这是诊断信息。而下面data中的cpu diagnostic message也就是诊断信息的具体内容,给了俩理由,一个是stop操作导致停止;一个是SFB 20停止。SFB的全称是system function block,系统功能块,这是为用户提供的程序集合,可以理解为设备系统的一部分,它是不能被修改删除的。

第二个包就有意思了,首先和上面一样是一个push,但这次的类是mode-transition,这个可是个新东西。

该类的方法用来切换设备的工作状态,在该类的包中没有Data段了,仅仅靠Current mode来决定要做什么。图中为stop,即停止运行的意思。除此之外,常用到的还有Warm Restart(暖启动,重启程序,但数据不变)、Hot Restart(完全从停止的状态开始运行)、Cold Restart(重启,并重置数据)、RUN(运行)等等。

 

step7_s300_AuthPassword.pcapng

该流量包是step 7 软件对plc进行密码修改,如下:

前两个包是读取CPU状态的,和上面分析的相同,不再废话,直接从第三个开始看

这次的类又变了,成了security,这个类实现的功能非常少,就是一个设置密码,一个清除密码,如图这里subfunction标识了是设置密码,data就是加密后的新密码。

加密过程非常的简单:

首先对前两个数与0x55进行异或操作0x21^0x55 = t ,0x3a^0x55 = o

接着对剩下的数操作,操作为与自己距离为-2的数进行异或,再与0x55异或,如:0x1b^t = o

脚本如下:

list = [0x21,0x3a,0x1b,0x1d,0x6e,0x68,0x1b,0x1d]
passwd = []
for i in range(0,len(list)):
    if i==0 or i==1:
        passwd.append(chr(list[i]^0x55))
    else:
        passwd.append(chr(list[i]^0x55^list[i-2]))
print passwd

解得新设置的密码为toor 。第四个包为回应,没有特别之处,剩下两个同样是查询信息。

 

s7comm_downloading_block_db1

大体看一下流量包

一开始是建立通信的操作,之后对设备信息进行了查询,关键是后面,出现了block function和resquest download,我们一点点看

可与看到为块方法类,这就需要详细解释一下西门子设备里的块到底是怎么样一个概念了。西门子的plc程序采用的是结构化设计,就像是我们的c语言,我们会分成很多模块进行编写(包括程序、数据),最后在main函数里调用,而块这个概念就是对应着我们c语言里的不同模块。

可以看到系统函数和自己写的函数都有C和B的区分,最开始接触时,就简单的理解为,C的就是没有全局变量的函数,没有存下来什么;而B就是带全局变量的函数,它修改了全局变量。

当然,上面的理解有些简单粗暴,但却很有道理。B就是block,而这个block是指DB,B不光是有函数,它还有自己的数据块用来存放数据,这个数据块也叫做背景数据块;而C呢则是使用的共享的数据块,调用时临时分配给他一块数据块,用完就收回来,它不会对数据进行持续性的保留。

举个栗子,如果你设备上有1、2两个器件,他们各自有各自的运行状态和收集到的数据,如果你执行的操作并没有涉及到数据,那可以用B,但如果你要将各自的数据收集,那就要用有背景数据块的C了。

聊完了什么是块之后我们继续看这个包,子方法是1,意思是列出所有的块,除了该方法外,该类还有:

我们之后还会看到,先看看这包的回复

可以看到和上一篇文章中的read var同样使用了item来组织内容,item包含块的类型和数量,OB作为s7的main,必然是1个,FC、FB数量则取决于用户的定义,而SFC、SFB的数量则和系统挂钩。

之后几组(一个request和一个response组成)包都是重复操作,直到37,使用了Get block info的方法

很显然block type指定了块的类型,而number则是块的编号,这里涉及到了一个新的概念,文件系统

这里的意思就是读取第一个DB块

接着看response,可以看到最关键的字段为Error code,根据上面item我们知道DB块实际上是0,根本没有number=1的情况,所以不能回复相应的数据,这时就会回应error code,这里的error code即没有对应的块。data中的内容也是”对象不存在“。

相应的如果数据块存在的话,回复的数据就应该是DB的数据。

往下走看到了request download,它是job包的一种,所以一定是(job,ack_data)这样的组合。主要关注的是

filename,由几个字段共同组成,用来唯一指定一个块

response很简单,这里就不再看了。接着往下走download block包

可以看到和request download差不多,主要看看response的包

可以见到,这次带回了数据,还有个关键地方,Function status,它提示我们,还有更多的数据在后面。所以我们可以看到,后续的流量中一直再重复job、ack_data直到出现ended标识才算是真正的完成了一组数据的下载。

在之后,会调用PI-Service的_INSE来激活下载的块,这样才算是真正完成了下载的任务。

总结一下,当要对一个块进行下载操作时,往往需要如下几步:

查看块是否存在(Block类中的Get block info) —> 请求下载(request download) —> 下载(由于数据过多可能重复多次) —> 激活块(PI-Service的_INSE)

除此之外我们还可以看到,在激活块后主机10发起来一个RST的请求与设备断开了连接,然后又重新连接,我们并不清楚流量包抓取过程中的实际情况,只能推测有可能是因为更新了数据,所以主机与设备重新建立了通信。

 

总结

某大佬说过,安全不是ctf。我们可以看到比赛题目中的S7comm并没有涉及到文章中的很多知识,但是这些东西我认为也十分重要,实际过程中我们接触的更多也是这样的流量包,只有掌握好这些最基础的知识,才能更进一步。

标签:再解,工控,stop,S7comm,PI,数据,我们,block
来源: https://www.cnblogs.com/nongchaoer/p/11975960.html