Пример #1
0
    def get_list_of_instances(self, class_id):
        """Use CIP service 0x4b to get a list of instances of the specified class"""
        start_instance = 0
        inst_list = []
        while True:
            cippkt = CIP(service=0x4b, path=CIP_Path.make(class_id=class_id, instance_id=start_instance))
            self.send_rr_cm_cip(cippkt)
            if self.sock is None:
                return
            resppkt = self.recv_enippkt()

            # Decode a list of 32-bit integers
            data = str(resppkt[CIP].payload)
            for i in range(0, len(data), 4):
                inst_list.append(struct.unpack('<I', data[i:i + 4])[0])

            cipstatus = resppkt[CIP].status[0].status
            if cipstatus == 0:
                return inst_list
            elif cipstatus == 6:
                # Partial response, query again from the next instance
                start_instance = inst_list[-1] + 1
            else:
                logger.error("Error in Get Instance List response: %r", resppkt[CIP].status[0])
                return
Пример #2
0
    def read_full_tag(self, class_id, instance_id, total_size):
        """Read the content of a tag which can be quite big"""
        data_chunks = []
        offset = 0
        remaining_size = total_size

        while remaining_size > 0:
            cippkt = CIP(service=0x4c,
                         path=CIP_Path.make(class_id=class_id,
                                            instance_id=instance_id))
            cippkt /= CIP_ReqReadOtherTag(start=offset, length=remaining_size)
            self.send_rr_cm_cip(cippkt)
            if self.sock is None:
                return
            resppkt = self.recv_enippkt()

            cipstatus = resppkt[CIP].status[0].status
            received_data = str(resppkt[CIP].payload)
            if cipstatus == 0:
                # Success
                assert len(received_data) == remaining_size
            elif cipstatus == 6 and len(received_data) > 0:
                # Partial response (size too big)
                pass
            else:
                logger.error("Error in Read Tag response: %r",
                             resppkt[CIP].status[0])
                return

            # Remember the chunk and continue
            data_chunks.append(received_data)
            offset += len(received_data)
            remaining_size -= len(received_data)
        return b''.join(data_chunks)
Пример #3
0
def fuzz_instanceid(client, classid):
    status = {}
    data = "\x01\x00"
    for instanceid in range(0xffff):
        # Symbol Instanc Addressing
        cippkt = CIP(service=0x4c,
                     path=CIP_Path.make(class_id=classid,
                                        instance_id=instanceid,
                                        word_size=3)) / data

        # print("class id: " + str(hex(classid)) + " | instance id: " + str(hex(instanceid)), end='\r')
        try:
            client.send_unit_cip(cippkt)
        except:
            pass
        # Receive the response and show it
        resppkt = client.recv_enippkt()
        # print("class id: " + str(hex(classid)) + " | instance id: " + str(hex(instanceid)) + " Status: " + str(resppkt[CIP].status))
        if resppkt is not None:
            stat = str(resppkt[CIP].status)
            if stat in status:
                status.get(stat).append(str(hex(instanceid)))
            else:
                status[stat] = [str(hex(instanceid))]
    # print all status
    for key, value in status.items():
        print("Status: " + key)
        for v in value:
            print("        " + v)
Пример #4
0
    def get_list_of_instances(self, class_id):
        """Use CIP service 0x4b to get a list of instances of the specified class"""
        start_instance = 0
        inst_list = []
        while True:
            cippkt = CIP(service=0x4b,
                         path=CIP_Path.make(class_id=class_id,
                                            instance_id=start_instance))
            self.send_rr_cm_cip(cippkt)
            if self.sock is None:
                return
            resppkt = self.recv_enippkt()

            # Decode a list of 32-bit integers
            data = str(resppkt[CIP].payload)
            for i in range(0, len(data), 4):
                inst_list.append(struct.unpack('<I', data[i:i + 4])[0])

            cipstatus = resppkt[CIP].status[0].status
            if cipstatus == 0:
                return inst_list
            elif cipstatus == 6:
                # Partial response, query again from the next instance
                start_instance = inst_list[-1] + 1
            else:
                logger.error("Error in Get Instance List response: %r",
                             resppkt[CIP].status[0])
                return
Пример #5
0
def fuzz_classid(client, instanceid):
    status = {}
    for classid in range(0x64, 0xc8):
        data = "\x01\x00"
        # Symbol Instanc Addressing
        cippkt = CIP(service=0x4c,
                     path=CIP_Path.make(class_id=classid,
                                        instance_id=instanceid,
                                        word_size=3)) / data

        print("class id: " + str(hex(classid)) + " | instance id: " +
              str(hex(instanceid)),
              end='\r')
        try:
            client.send_unit_cip(cippkt)
        except:
            pass
        # Show the response only if it does not contain data
        resppkt = client.recv_enippkt()
        if resppkt is not None:
            stat = str(resppkt[CIP].status)
            if stat in status:
                status.get(stat).append(str(hex(classid)))
            else:
                status[stat] = [str(hex(classid))]
    # print all status
    for key, value in status.items():
        print("Status: " + key)
        for v in value:
            print("        " + v)
Пример #6
0
def main():
    # Connect to PLC
    client = plc.PLCClient('192.168.9.227')
    if not client.connected:
        sys.exit(1)

    # Creating Connections Through the Connection Manager Object
    if not client.forward_open():
        sys.exit(1)

    # Get_Instance_Attribute_List
    # Set initial instance to 0x0
    instanceid = 0x0
    # status
    status = ''
    # Number of attributes to retrieve (2 bytes) + Attribute 1 - Symbol Name (2 bytes) + Attribute 2 - Symbol Type (2 bytes)
    data = "\x02\x00\x01\x00\x02\x00"

    while ("Success" not in status):
        cippkt = CIP(service=0x55,
                     path=CIP_Path.make(class_id=0x6b,
                                        instance_id=instanceid,
                                        word_size=3)) / data
        client.send_unit_cip(cippkt)
        resppkt = client.recv_enippkt()
        status = str(resppkt[CIP].status)
        instanceid = parse_attributes(resppkt[CIP].load) + 1

    client.forward_close()
Пример #7
0
def fuzz_timeout(client):
    for i in range(0xff):
        # i = 0x1
        print("Fuzzing timeout: " + str(hex(i)))
        # Construct an enip packet from raw
        enippkt = ENIP_TCP(session=client.session_id)
        # Symbol Instanc Addressing
        cippkt = CIP(service=0x4c,
                     path=CIP_Path.make(class_id=0x6b, instance_id=0x227))
        # interface handle, timeout, count, items
        enippkt /= ENIP_SendUnitData(
            timeout=i,
            items=[
                # type_id, length, connection id
                ENIP_SendUnitData_Item() /
                ENIP_ConnectionAddress(connection_id=client.enip_connid),
                # type_id, length, sequence
                ENIP_SendUnitData_Item() /
                ENIP_ConnectionPacket(sequence=client.sequence) / cippkt
            ])
        client.sequence += 1
        if client.sock is not None:
            client.sock.send(str(enippkt))
        # Show the response only if it does not contain data
        resppkt = client.recv_enippkt()
        if resppkt is not None:
            print("Status: " + str(resppkt[ENIP_TCP].status))
            print("TImeout: " + str(hex(resppkt[ENIP_SendUnitData].timeout)))
Пример #8
0
    def read_full_tag(self, class_id, instance_id, total_size):
        """Read the content of a tag which can be quite big"""
        data_chunks = []
        offset = 0
        remaining_size = total_size

        while remaining_size > 0:
            cippkt = CIP(service=0x4c, path=CIP_Path.make(class_id=class_id, instance_id=instance_id))
            cippkt /= CIP_ReqReadOtherTag(start=offset, length=remaining_size)
            self.send_rr_cm_cip(cippkt)
            if self.sock is None:
                return
            resppkt = self.recv_enippkt()

            cipstatus = resppkt[CIP].status[0].status
            received_data = str(resppkt[CIP].payload)
            if cipstatus == 0:
                # Success
                assert len(received_data) == remaining_size
            elif cipstatus == 6 and len(received_data) > 0:
                # Partial response (size too big)
                pass
            else:
                logger.error("Error in Read Tag response: %r", resppkt[CIP].status[0])
                return

            # Remember the chunk and continue
            data_chunks.append(received_data)
            offset += len(received_data)
            remaining_size -= len(received_data)
        return b''.join(data_chunks)
Пример #9
0
def simple_read_tag(client, pathsize, classid, instanceid):
    # Symbol Instanc Addressing
    data = "\x01\x00"
    cippkt = CIP(service=0x4c,
                 path=CIP_Path.make(class_id=classid,
                                    instance_id=instanceid,
                                    word_size=pathsize)) / data

    # Construct an enip packet from raw
    enippkt = ENIP_TCP(session=client.session_id)
    # interface handle, timeout, count, items
    enippkt /= ENIP_SendUnitData(
        interface_handle=0x0,
        items=[
            # type_id, length, connection id
            ENIP_SendUnitData_Item() /
            ENIP_ConnectionAddress(connection_id=client.enip_connid),
            # type_id, length, sequence
            ENIP_SendUnitData_Item() /
            ENIP_ConnectionPacket(sequence=client.sequence) / cippkt
        ])
    client.sequence += 1
    if client.sock is not None:
        client.sock.send(str(enippkt))

    enippkt.show()

    # Show the response only if it does not contain data
    resppkt = client.recv_enippkt()
    if resppkt is not None:
        print("Status: " + str(resppkt[CIP].status))
Пример #10
0
 def set_attribute(self, class_id, instance, attr, value):
     """Set the value of attribute class/instance/attr"""
     path = CIP_Path.make(class_id=class_id, instance_id=instance)
     # User CIP service 4: Set_Attribute_List
     cippkt = CIP(service=4, path=path) / scapy_all.Raw(load=struct.pack('<HH', 1, attr) + value)
     self.send_rr_cm_cip(cippkt)
     if self.sock is None:
         return
     resppkt = self.recv_enippkt()
     cippkt = resppkt[CIP]
     if cippkt.status[0].status != 0:
         logger.error("CIP set attribute error: %r", cippkt.status[0])
         return False
     return True
Пример #11
0
 def set_attribute(self, class_id, instance, attr, value):
     """Set the value of attribute class/instance/attr"""
     path = CIP_Path.make(class_id=class_id, instance_id=instance)
     # User CIP service 4: Set_Attribute_List
     cippkt = CIP(service=4, path=path) / scapy_all.Raw(load=struct.pack('<HH', 1, attr) + value)
     self.send_rr_cm_cip(cippkt)
     if self.sock is None:
         return
     resppkt = self.recv_enippkt()
     cippkt = resppkt[CIP]
     if cippkt.status[0].status != 0:
         logger.error("CIP set attribute error: %r", cippkt.status[0])
         return False
     return True
Пример #12
0
def fuzz_pathsize(client, classid, instanceid):
    data = "\x01\x00"
    for pathsize in range(0xff):
        # Symbol Instanc Addressing
        cippkt = CIP(service=0x4c,
                     path=CIP_Path.make(class_id=classid,
                                        instance_id=instanceid,
                                        word_size=pathsize)) / data

        try:
            client.send_unit_cip(cippkt)
        except:
            pass

        # Show the response only if it does not contain data
        resppkt = client.recv_enippkt()
        if resppkt is not None:
            print("Status: " + str(resppkt[CIP].status))
Пример #13
0
 def get_attribute(self, class_id, instance, attr):
     """Get an attribute for the specified class/instance/attr path"""
     # Get_Attribute_Single does not seem to work properly
     # path = CIP_Path.make(class_id=class_id, instance_id=instance, attribute_id=attr)
     # cippkt = CIP(service=0x0e, path=path)  # Get_Attribute_Single
     path = CIP_Path.make(class_id=class_id, instance_id=instance)
     cippkt = CIP(path=path) / CIP_ReqGetAttributeList(attrs=[attr])
     self.send_rr_cm_cip(cippkt)
     if self.sock is None:
         return
     resppkt = self.recv_enippkt()
     cippkt = resppkt[CIP]
     if cippkt.status[0].status != 0:
         logger.error("CIP get attribute error: %r", cippkt.status[0])
         return
     resp_getattrlist = str(cippkt.payload)
     assert resp_getattrlist[:2] == b'\x01\x00'  # Attribute count must be 1
     assert struct.unpack('<H', resp_getattrlist[2:4])[0] == attr  # First attribute
     assert resp_getattrlist[4:6] == b'\x00\x00'  # Status
     return resp_getattrlist[6:]
Пример #14
0
 def get_attribute(self, class_id, instance, attr):
     """Get an attribute for the specified class/instance/attr path"""
     # Get_Attribute_Single does not seem to work properly
     # path = CIP_Path.make(class_id=class_id, instance_id=instance, attribute_id=attr)
     # cippkt = CIP(service=0x0e, path=path)  # Get_Attribute_Single
     path = CIP_Path.make(class_id=class_id, instance_id=instance)
     cippkt = CIP(path=path) / CIP_ReqGetAttributeList(attrs=[attr])
     self.send_rr_cm_cip(cippkt)
     if self.sock is None:
         return
     resppkt = self.recv_enippkt()
     cippkt = resppkt[CIP]
     if cippkt.status[0].status != 0:
         logger.error("CIP get attribute error: %r", cippkt.status[0])
         return
     resp_getattrlist = str(cippkt.payload)
     assert resp_getattrlist[:2] == b'\x01\x00'  # Attribute count must be 1
     assert struct.unpack('<H', resp_getattrlist[2:4])[0] == attr  # First attribute
     assert resp_getattrlist[4:6] == b'\x00\x00'  # Status
     return resp_getattrlist[6:]
Пример #15
0
def scan_one(class_name, instance_id, attribute_id=None):

    success_service = set()
    class_id = CLASS_CODES[class_name]

    for service_id in CLASS_SERVICE_MAP[class_name]:
        plc_client = plc.PLCClient(PLC_HOST)

        if not plc_client.connected:
            logging.error(("Cannot connect to server"))
            sys.exit(1)

        # Make packet detail
        cippkt = CIP(service=service_id,
                     path=CIP_Path.make(class_id=class_id,
                                        instance_id=instance_id,
                                        attribute_id=attribute_id))

        # Send a CIP request
        plc_client.send_rr_cip(cippkt)

        # Receive the response
        resppkt = plc_client.recv_enippkt()

        #resppkt.show()

        try:
            enip_tcp_status = resppkt["ENIP_TCP"].status
            cip_tcp_status = resppkt["CIP_ResponseStatus"].status
        except:
            cip_tcp_status = None

        if enip_tcp_status == 0x0 and cip_tcp_status == 0x0:  # SUCCESS
            success_service.add(service_id)

    logging.debug(("Class " + str(class_name) + " supports serives " +
                   str(success_service)))

    return success_service
Пример #16
0
 def send_rr_cm_cip(self, cippkt):
     """Encapsulate the CIP packet into a ConnectionManager packet"""
     cipcm_msg = [cippkt]
     cippkt = CIP(path=CIP_Path.make(class_id=6, instance_id=1))
     cippkt /= CIP_ReqConnectionManager(message=cipcm_msg)
     self.send_rr_cip(cippkt)
Пример #17
0
 def send_rr_cm_cip(self, cippkt):
     """Encapsulate the CIP packet into a ConnectionManager packet"""
     cipcm_msg = [cippkt]
     cippkt = CIP(path=CIP_Path.make(class_id=6, instance_id=1))
     cippkt /= CIP_ReqConnectionManager(message=cipcm_msg)
     self.send_rr_cip(cippkt)
Пример #18
0
        class_id = CLASS_CODES[class_name]

        for instance_id in range(INSTANCE_ID_RANGE[0], INSTANCE_ID_RANGE[1]):

            logging.basicConfig(format='[%(levelname)s] %(message)s',
                                level=logging.DEBUG)

            # Connect to PLC
            client = plc.PLCClient(PLC_HOST)
            if not client.connected:
                sys.exit(1)
            print("Established session {}".format(client.session_id))

            # Send a CIP ReadTag request
            cippkt = CIP(service=service_id,
                         path=CIP_Path.make(class_id=int(class_id),
                                            instance_id=instance_id))
            client.send_rr_cip(cippkt)

            # Receive the response and show it
            resppkt = client.recv_enippkt()

            enip_tcp_status = resppkt["ENIP_TCP"].status
            service_info = {
                "name": service_name + class_name + str(instance_id),
                "service_name": service_name,
                "class_name": class_name,
                "service_id": service_id,
                "class_id": class_id,
                "instance_id": instance_id
            }