IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    工控系统蜜罐建设与协议仿真技术分享

    admin发表于 2016-05-25 02:43:44
    love 0

    0x1、简介

    随着Eripp、Shodan、Zoomeye类似的网络空间搜索引擎先后的出现,网络扫描技术的发展和探测的增多,工控设备、物联网设备、基础设 施等作为互联网的一部分,已逐渐被攻击者所重视。从2010年的震网到如今的Havex,工控网络作为一个相对封闭的网络则出现了越来越多更具有针对性的 攻击事件和恶意程序。有针对的模拟工控系统某些特征的蜜罐出现也变得必要,蜜罐作为一种相对主动的安全检测手段,相信在未来使用的会越来越广泛。

    0x2、工控蜜罐日志分析

    Link:工控蜜罐日志收集项目链接地址

    博主于6月低在香港和大陆外网节点搭建了设备协议仿真程序,用于发现主动扫描和来源IP的操作行为,根据积累的日志得出了如下结论:
    A、目前针对特定端口(tcp/102、tcp/502)的扫描和识别的源IP均来自于国外。
    11z
    B、对协议的识别使用的是已知公开的技术。(由ScadaStrangeLove发布的plcscan和Digitalbond移植的基于nmap的 nse识别枚举脚本,这一点可以根据目前接收到的交互报文断定,如读取西门子PLC设备时使用的是SZL请求,识别Modbus设备型号信息使用了43号 功能码)。
    22z
    C、协议仿真程序并未收到恶意攻击和写入指令(例如西门子PLC CPU的stop指令和数据写入指令等,modbus的05、06等具有写入功能的功能码,详请参考日志)。

    0x3、快速实现协议仿真

    案例一

    Modbus协议介绍

    Modbus协议是工控网络中最常见的一种协议之一,该协议由莫迪康于1979年,为使用可编程逻辑控制器(PLC)而发表,因为简单易用至今使用已经相当广泛,如RTU、PLC、传感器等,协议可以适用于以太网、串口。同样因为使用广泛,公网暴露数量较多(Shodan查询连接)。

    Modbus协议服务端实例脚本
    Link:Modbus协议资料

    #!/usr/bin/python  
    # version 2.7
    # Source Code form https://github.com/tecpal/PyModbus
    # change Z-0ne
    
    import os
    import sys
    import socket,thread
    from array import array
    from time import sleep, ctime
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('',502))
    s.listen(10)
    F = open('c:\modbus.log','a',0)
    sys.stdout = F
    def TCP(conn,addr,F):
      buffer = array('B',[0]*300)
      while 1:
        try:
          conn.recv_into(buffer)
          ID = buffer[6]
          FC = buffer[7]
          mADR = buffer[8]
          lADR = buffer[9]
          ADR = mADR*256+lADR
          LEN = buffer[10]*256+buffer[11]
          BYT = LEN*2
          print "Received = ",buffer[0:13+buffer[12]]
          if (FC < 5 and FC > 0):   #Read Inputs or Registers
            DAT = array('B')
            if FC < 3: 
              BYT = (lambda x: x/8 if (x%8==0) else x/8+1)(LEN)     #Round off the no. of bytes
              v = 85          #send 85,86.. for bytes.
              for i in range(BYT): 
                DAT.append(v)
                v = (lambda x: x+1 if (x<255) else 85)(v)
            else:
              for i in range(LEN):  #Sends back the address as data
                DAT.append(mADR)
                DAT.append(lADR)
                if (lADR == 255):
                  lADR = 0
                  mADR = mADR + 1
                else: lADR = lADR + 1
            print "ID= %d,  Fun.Code= %d,  Address= %d,  Length= %d" %(ID, FC, ADR, LEN)
            conn.send(array('B', [0,0,0,0,0, BYT+3, ID, FC, BYT]) + DAT )
          elif (FC == 15 or FC == 16 or FC == 6 or FC == 43 or FC == 17):    #Write Registers
            BYT = buffer[12]
            conn.send(array('B', [0,0,0,0,0, 6, ID, FC, mADR, lADR, buffer[10], buffer[11] ] ) )
            buf = buffer[13:(13+BYT)]
            message = ': ADR:'+str(ADR)+' '
            if FC == 15:
              print "ID= %d,  Fun.Code= %d,  Address= %d,  Length= %d,  Bytes= %d" %(ID, FC, ADR, LEN, BYT)
              for j in range(BYT):  message = message+('Byte:'+str(j)+'='+str(buf[j])+', ')
            elif FC == 16:
              print "ID= %d,  Fun.Code= %d,  Address= %d,  Length= %d,  Bytes= %d" %(ID, FC, ADR, LEN, BYT)
              for j in range(BYT/2): message = message+('Reg:'+str(j)+'='+str((buf[j*2]<<8)+(buf[j*2+1]))+', ')
            elif FC == 6:
              print "ID= %d,  Fun.Code= %d,  Address= %d, Bytes= %d" %(ID, FC, ADR, LEN)
              message = message+('Reg:'+str(LEN))
            elif FC == 43:
              print "ID= %d,  Fun.Code= %d,  Address= %d, Bytes= %d" %(ID, FC, ADR, LEN)
              message = message+('Reg:'+str(LEN))
              conn.send(bytes(bytearray([0x00, 0x00, 
    										0x00, 0x00, 
    										0x00, 0x32, 
    										0x00, 
    										0x2b, #43 FC
    										0x0e, 0x01, 0x81, 0x00, 0x00, 0x03, 
    										0x00, 0x14, 0x53, 0x63, 0x68, 0x6e, #Schneider Electric
    										0x65, 0x69, 0x64, 0x65, 0x72, 0x20, 
    										0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 
    										0x69, 0x63, 0x20, 0x20, 0x01, 0x0c, 
    										0x42, 0x4d, 0x58, 0x20, 0x50, 0x33, #BMX P34 20 20
    										0x34, 0x20, 0x32, 0x30, 0x32, 0x30, 
    										0x02, 0x04, 0x76, 0x32, 0x2e, 0x32	#V2.2
    										])))
            elif FC == 17:
              print "ID= %d,  Fun.Code= %d,  Address= %d, Bytes= %d" %(ID, FC, ADR, LEN)
              message = message+('Reg:'+str(LEN))
              conn.send(array('B', [0,0,0,0,0,3,FC,171,1] ) )#Illegal function
            F.write(ctime() + message + "\n")
          else:
            conn.send(array('B', [0,0,0,0,0,3,FC,171,1])) #Illegal function
            print "Funtion Code %d Not Supported" %FC
            F.write(ctime() + message + "\n")
            exit()
          sleep(1)
        except Exception, e:
          print e, "\nConnection with Client terminated"
          F.write(ctime() + "\n")
          exit()
    while 1:
      conn, addr = s.accept()
      print "Connected by", addr[0]
      thread.start_new_thread(TCP,(conn,addr,F))

    33z

    案例二

    西门子S7协议介绍

    西门子S7系列或CP模块使用以太网通信时主要基于ISO TCP (RFC1006)和西门子自有S7协议实现,并且协议详细实现由厂商自行持有,官方并未公开,外部应用与西门子S7系列PLC使用以太网通讯时可以使用 西门子官方组件或者非官方实现的的开源通讯库实现,目前第三方开源的通讯库同样也实现了较多功能,如libnodave,snap7等均已解码了西门子 PLC的较多操作功能。

    44z

    西门子S7协议服务端介绍

    西门子S7协议实现较为较为复杂,并且功能较多,实现高交互需要利用西门子的开源协议栈实现,以SNAP7项目为例,该项目中提供了PLC服务端的部分功能仿真,使用时只需对固定特征进行修改即可实现PLC服务端的功能。

    0x4、注意事项

    1、 使用Conpot类似开源工控蜜罐系统需要注意本地化,使用默认配置是不可取的。如下图

    55z

    【via@ICS安全团队】



沪ICP备19023445号-2号
友情链接