From 4f8b10d8d16d29346bdd7862787958bc5550e04b Mon Sep 17 00:00:00 2001 From: Lukas Rist Date: Sun, 8 Nov 2020 21:49:08 +0100 Subject: [PATCH 1/4] Adding triconex protocol --- conpot/core/attack_session.py | 9 +- conpot/protocols/IEC104/IEC104_server.py | 11 +- conpot/protocols/bacnet/bacnet_server.py | 3 +- conpot/protocols/enip/enip_server.py | 9 +- conpot/protocols/ftp/ftp_base_handler.py | 7 +- conpot/protocols/ftp/ftp_handler.py | 9 +- .../guardian_ast/guardian_ast_server.py | 5 +- .../kamstrup_management_server.py | 9 +- .../meter_protocol/kamstrup_server.py | 9 +- conpot/protocols/modbus/modbus_server.py | 15 +- conpot/protocols/s7comm/s7_server.py | 9 +- conpot/protocols/tftp/tftp_server.py | 9 +- conpot/protocols/triconex/triconex.xsd | 32 ++ conpot/protocols/triconex/triconex_server.py | 363 ++++++++++++++++++ conpot/templates/triconex/template.xml | 13 + .../templates/triconex/triconex/triconex.xml | 8 + conpot/tests/test_triconex.py | 18 + requirements.txt | 1 + 18 files changed, 496 insertions(+), 43 deletions(-) create mode 100644 conpot/protocols/triconex/triconex.xsd create mode 100644 conpot/protocols/triconex/triconex_server.py create mode 100644 conpot/templates/triconex/template.xml create mode 100644 conpot/templates/triconex/triconex/triconex.xml create mode 100644 conpot/tests/test_triconex.py diff --git a/conpot/core/attack_session.py b/conpot/core/attack_session.py index b0cbdc96..adad3080 100644 --- a/conpot/core/attack_session.py +++ b/conpot/core/attack_session.py @@ -23,9 +23,16 @@ logger = logging.getLogger(__name__) -# one instance per connection +NEW_CONNECTION = "NEW_CONNECTION" +CONNECTION_LOST = "CONNECTION_LOST" +CONNECTION_CLOSED = "CONNECTION_CLOSED" +CONNECTION_FAILED = "CONNECTION_FAILED" +CONNECTION_TERMINATED = "CONNECTION_TERMINATED" +CONNECTION_QUIT = "CONNECTION_QUIT" +CONNECTION_TIMEOUT = "CONNECTION_TIMEOUT" +# one instance per connection class AttackSession(object): def __init__( self, diff --git a/conpot/protocols/IEC104/IEC104_server.py b/conpot/protocols/IEC104/IEC104_server.py index 95a9b60d..bc625892 100644 --- a/conpot/protocols/IEC104/IEC104_server.py +++ b/conpot/protocols/IEC104/IEC104_server.py @@ -19,6 +19,7 @@ from .frames import struct, TESTFR_act, socket, errno import logging import conpot.core as conpot_core +from conpot.core import attack_session from gevent.server import StreamServer import gevent from .errors import Timeout_t3 @@ -52,7 +53,7 @@ def handle(self, sock, address): address[1], session.id, ) - session.add_event({"type": "NEW_CONNECTION"}) + session.add_event({"type": attack_session.NEW_CONNECTION}) iec104_handler = IEC104(self.device_data_controller, sock, address, session.id) try: while True: @@ -65,7 +66,7 @@ def handle(self, sock, address): request = sock.recv(6) if not request: logger.info("IEC104 Station disconnected. (%s)", session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack.session.CONNECTION_LOST}) iec104_handler.disconnect() break while request and len(request) < 2: @@ -124,18 +125,18 @@ def handle(self, sock, address): except gevent.Timeout: logger.warning("T1 timed out. (%s)", session.id) logger.info("IEC104 Station disconnected. (%s)", session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) iec104_handler.disconnect() break except socket.timeout: logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) except socket.error as err: if isinstance(err.args, tuple): if err.errno == errno.EPIPE: # remote peer disconnected logger.info("IEC104 Station disconnected. (%s)", session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) else: # determine and handle different error pass diff --git a/conpot/protocols/bacnet/bacnet_server.py b/conpot/protocols/bacnet/bacnet_server.py index 3368b336..e96e9fdc 100644 --- a/conpot/protocols/bacnet/bacnet_server.py +++ b/conpot/protocols/bacnet/bacnet_server.py @@ -28,6 +28,7 @@ import conpot.core as conpot_core from conpot.protocols.bacnet.bacnet_app import BACnetApp from conpot.core.protocol_wrapper import conpot_protocol +from conpot.core import attack_session import logging logger = logging.getLogger(__name__) @@ -69,7 +70,7 @@ def handle(self, data, address): logger.info( "New Bacnet connection from %s:%d. (%s)", address[0], address[1], session.id ) - session.add_event({"type": "NEW_CONNECTION"}) + session.add_event({"type": attack_session.NEW_CONNECTION}) # I'm not sure if gevent DatagramServer handles issues where the # received data is over the MTU -> fragmentation if data: diff --git a/conpot/protocols/enip/enip_server.py b/conpot/protocols/enip/enip_server.py index 97d570ef..d3be4e2c 100644 --- a/conpot/protocols/enip/enip_server.py +++ b/conpot/protocols/enip/enip_server.py @@ -29,6 +29,7 @@ from cpppo.server.enip import parser from cpppo.server.enip import device from conpot.core.protocol_wrapper import conpot_protocol +from conpot.core import attack_session import conpot.core as conpot_core logger = logging.getLogger(__name__) @@ -133,7 +134,7 @@ def handle(self, conn, address, enip_process=None, delay=None, **kwds): "enip", host, port, conn.getsockname()[0], conn.getsockname()[1] ) logger.debug("ENIP server %s begins serving client %s", name, address) - session.add_event({"type": "NEW_CONNECTION"}) + session.add_event({"type": attack_session.NEW_CONNECTION}) tcp = conn.family == socket.AF_INET and conn.type == socket.SOCK_STREAM udp = conn.family == socket.AF_INET and conn.type == socket.SOCK_DGRAM @@ -332,7 +333,7 @@ def handle_tcp( cpppo.timer() - begun, delayseconds, ) - session.add_event({"type": "CONNECTION_CLOSED"}) + session.add_event({"type": attack_session.CONNECTION_CLOSED}) except: logger.error("Failed request: %s", parser.enip_format(data)) enip_process(address, data=cpppo.dotdict()) # Terminate. @@ -491,7 +492,7 @@ def handle_udp(self, conn, name, enip_process, session, **kwds): logger.debug( "Transaction complete after %7.3fs", cpppo.timer() - begun ) - session.add_event({"type": "CONNECTION_CLOSED"}) + session.add_event({"type": attack_session.CONNECTION_CLOSED}) stats["processed"] = source.sent except: # Parsing failure. Suck out some remaining input to give us some context, but don't re-raise @@ -512,7 +513,7 @@ def handle_udp(self, conn, name, enip_process, session, **kwds): where, "".join(traceback.format_exception(*sys.exc_info())), ) - session.add_event({"type": "CONNECTION_FAILED"}) + session.add_event({"type": attack_session.CONNECTION_FAILED}) def set_tags(self): typenames = { diff --git a/conpot/protocols/ftp/ftp_base_handler.py b/conpot/protocols/ftp/ftp_base_handler.py index 1a10ecb8..3cb8e44f 100644 --- a/conpot/protocols/ftp/ftp_base_handler.py +++ b/conpot/protocols/ftp/ftp_base_handler.py @@ -21,6 +21,7 @@ from gevent import select import conpot.core as conpot_core from conpot.core.filesystem import FilesystemError +from conpot.core import attack_session import logging import errno import time @@ -241,7 +242,7 @@ def setup(self): self.client_address[0], self.client_address[1], self.session.id ) ) - self.session.add_event({"type": "NEW_CONNECTION"}) + self.session.add_event({"type": attack_session.NEW_CONNECTION}) # send 200 + banner -- new client has connected! self.respond(b"200 " + self.config.banner.encode()) # Is there a delay in command response? < gevent.sleep(0.5) ? @@ -297,7 +298,7 @@ def handle_cmd_channel(self): self.client_address, self.session.id ) ) - self.session.add_event({"type": "CONNECTION_LOST"}) + self.session.add_event({"type": attack_session.CONNECTION_LOST}) self.finish() return socket_read, socket_write, _ = gevent.select.select( @@ -354,7 +355,7 @@ def handle_cmd_channel(self): self.client_address, self.session.id, se ) ) - self.session.add_event({"type": "CONNECTION_LOST"}) + self.session.add_event({"type": attack_session.CONNECTION_LOST}) self.finish() def respond(self, response): diff --git a/conpot/protocols/ftp/ftp_handler.py b/conpot/protocols/ftp/ftp_handler.py index 4e3e23cb..772fb3dd 100644 --- a/conpot/protocols/ftp/ftp_handler.py +++ b/conpot/protocols/ftp/ftp_handler.py @@ -13,6 +13,7 @@ from gevent import socket from fs import errors from conpot.core.filesystem import FilesystemError, FSOperationNotPermitted +from conpot.core import attack_session from conpot.protocols.ftp.ftp_utils import FTPPrivilegeException, get_data_from_iter logger = logging.getLogger(__name__) @@ -114,7 +115,7 @@ def do_SYST(self, arg): def do_QUIT(self, arg): self.respond(b"221 Bye.") - self.session.add_event({"type": "CONNECTION_TERMINATED"}) + self.session.add_event({"type": attack_session.CONNECTION_TERMINATED}) self.disconnect_client = True def do_SITE_HELP(self, line): @@ -879,7 +880,7 @@ def _process_command(self, cmd, *args, **kwargs): if self.invalid_login_attempt >= self.max_login_attempts: self.respond(b"421 Too many connections. Service temporarily unavailable.") self.disconnect_client = True - self.session.add_event({"type": "CONNECTION_TERMINATED"}) + self.session.add_event({"type": attack_session.CONNECTION_TERMINATED}) else: try: method = getattr(self, "do_" + cmd.replace(" ", "_")) @@ -937,7 +938,7 @@ def process_ftp_command(self): ) # TODO: what to respond here? For now just terminate the session self.disconnect_client = True - self.session.add_event({"type": "CONNECTION_TERMINATED"}) + self.session.add_event({"type": attack_session.CONNECTION_TERMINATED}) elif not (self.metrics.timeout() < self.config.timeout) and ( not self._data_channel ): @@ -946,7 +947,7 @@ def process_ftp_command(self): self.client_address, self.session.id ) ) - self.session.add_event({"type": "CONNECTION_TIMEOUT"}) + self.session.add_event({"type": attack_session.CONNECTION_TIMEOUT}) self.respond(b"421 Timeout.") self.disconnect_client = True else: diff --git a/conpot/protocols/guardian_ast/guardian_ast_server.py b/conpot/protocols/guardian_ast/guardian_ast_server.py index 6f6c861e..3c328c71 100644 --- a/conpot/protocols/guardian_ast/guardian_ast_server.py +++ b/conpot/protocols/guardian_ast/guardian_ast_server.py @@ -25,6 +25,7 @@ import logging import random import conpot.core as conpot_core +from conpot.core import attack_session from conpot.core.protocol_wrapper import conpot_protocol from conpot.helpers import str_to_bytes @@ -55,7 +56,7 @@ def handle(self, sock, addr): logger.info( "New GuardianAST connection from %s:%d. (%s)", addr[0], addr[1], session.id ) - session.add_event({"type": "NEW_CONNECTION"}) + session.add_event({"type": attack_session.NEW_CONNECTION}) current_time = datetime.datetime.utcnow() fill_start = self.fill_offset_time - datetime.timedelta(minutes=313) fill_stop = self.fill_offset_time - datetime.timedelta(minutes=303) @@ -507,7 +508,7 @@ def I20500(): logger.info( "GuardianAST client disconnected %s:%d. (%s)", addr[0], addr[1], session.id ) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) def start(self, host, port): connection = (host, port) diff --git a/conpot/protocols/kamstrup/management_protocol/kamstrup_management_server.py b/conpot/protocols/kamstrup/management_protocol/kamstrup_management_server.py index 601f999d..0d2795a6 100644 --- a/conpot/protocols/kamstrup/management_protocol/kamstrup_management_server.py +++ b/conpot/protocols/kamstrup/management_protocol/kamstrup_management_server.py @@ -19,6 +19,7 @@ import gevent from gevent.server import StreamServer import conpot.core as conpot_core +from conpot.core import attack_session from conpot.protocols.kamstrup.management_protocol.command_responder import ( CommandResponder, ) @@ -52,7 +53,7 @@ def handle(self, sock, address): address[1], session.id, ) - session.add_event({"type": "NEW_CONNECTION"}) + session.add_event({"type": attack_session.NEW_CONNECTION}) try: sock.send( @@ -67,7 +68,7 @@ def handle(self, sock, address): data = sock.recv(1024) if not data: logger.info("Kamstrup client disconnected. (%s)", session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) break request = data.decode() logdata = {"request": request} @@ -83,7 +84,7 @@ def handle(self, sock, address): gevent.sleep(0.25) # TODO measure delay and/or RTT if response is None: - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) break # encode data before sending reply = str_to_bytes(response) @@ -91,7 +92,7 @@ def handle(self, sock, address): except socket.timeout: logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) sock.close() diff --git a/conpot/protocols/kamstrup/meter_protocol/kamstrup_server.py b/conpot/protocols/kamstrup/meter_protocol/kamstrup_server.py index 3cadfde1..d9df3f3e 100644 --- a/conpot/protocols/kamstrup/meter_protocol/kamstrup_server.py +++ b/conpot/protocols/kamstrup/meter_protocol/kamstrup_server.py @@ -23,6 +23,7 @@ import gevent from conpot.helpers import chr_py3 import conpot.core as conpot_core +from conpot.core import attack_session from conpot.protocols.kamstrup.meter_protocol import request_parser from conpot.protocols.kamstrup.meter_protocol.command_responder import CommandResponder from conpot.core.protocol_wrapper import conpot_protocol @@ -65,7 +66,7 @@ def handle(self, sock, address): address[1], session.id, ) - session.add_event({"type": "NEW_CONNECTION"}) + session.add_event({"type": attack_session.NEW_CONNECTION}) self.server_active = True @@ -76,7 +77,7 @@ def handle(self, sock, address): if not raw_request: logger.info("Kamstrup client disconnected. (%s)", session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) break for x in raw_request: @@ -85,7 +86,7 @@ def handle(self, sock, address): while True: request = parser.get_request() if not request: - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) break else: logdata = { @@ -113,7 +114,7 @@ def handle(self, sock, address): except socket.timeout: logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) sock.close() diff --git a/conpot/protocols/modbus/modbus_server.py b/conpot/protocols/modbus/modbus_server.py index 3af50f71..1d6c285b 100644 --- a/conpot/protocols/modbus/modbus_server.py +++ b/conpot/protocols/modbus/modbus_server.py @@ -17,6 +17,7 @@ from conpot.core.protocol_wrapper import conpot_protocol from conpot.protocols.modbus import slave_db import conpot.core as conpot_core +from conpot.core import attack_session logger = logging.getLogger(__name__) @@ -105,7 +106,7 @@ def handle(self, sock, address): logger.info( "New Modbus connection from %s:%s. (%s)", address[0], address[1], session.id ) - session.add_event({"type": "NEW_CONNECTION"}) + session.add_event({"type": attack_session.NEW_CONNECTION}) try: while True: @@ -121,17 +122,17 @@ def handle(self, sock, address): if not request: logger.info("Modbus client disconnected. (%s)", session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) break if request.strip().lower() == "quit.": logger.info("Modbus client quit. (%s)", session.id) - session.add_event({"type": "CONNECTION_QUIT"}) + session.add_event({"type": attack_session.CONNECTION_QUIT}) break if len(request) < 7: logger.info( "Modbus client provided data {} but invalid.".format(session.id) ) - session.add_event({"type": "CONNECTION_TERMINATED"}) + session.add_event({"type": attack_session.CONNECTION_TERMINATED}) break _, _, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): @@ -169,7 +170,7 @@ def handle(self, sock, address): logger.info( "Modbus connection terminated with client %s.", address[0] ) - session.add_event({"type": "CONNECTION_TERMINATED"}) + session.add_event({"type": attack_session.CONNECTION_TERMINATED}) sock.shutdown(socket.SHUT_RDWR) sock.close() break @@ -179,13 +180,13 @@ def handle(self, sock, address): "Modbus client ignored due to invalid addressing." " (%s)", session.id, ) - session.add_event({"type": "CONNECTION_TERMINATED"}) + session.add_event({"type": attack_session.CONNECTION_TERMINATED}) sock.shutdown(socket.SHUT_RDWR) sock.close() break except socket.timeout: logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) def start(self, host, port): self.host = host diff --git a/conpot/protocols/s7comm/s7_server.py b/conpot/protocols/s7comm/s7_server.py index a815e0f0..da89166f 100644 --- a/conpot/protocols/s7comm/s7_server.py +++ b/conpot/protocols/s7comm/s7_server.py @@ -27,6 +27,7 @@ from conpot.protocols.s7comm.cotp import COTP_ConnectionConfirm from conpot.protocols.s7comm.s7 import S7 import conpot.core as conpot_core +from conpot.core import attack_session from conpot.core.protocol_wrapper import conpot_protocol from lxml import etree @@ -82,14 +83,14 @@ def handle(self, sock, address): address[0], address[1], session.id ) ) - session.add_event({"type": "NEW_CONNECTION"}) + session.add_event({"type": attack_session.NEW_CONNECTION}) try: while True: data = sock.recv(4, socket.MSG_WAITALL) if len(data) == 0: - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) break _, _, length = unpack("!BBH", data[:4]) @@ -283,12 +284,12 @@ def handle(self, sock, address): ) except socket.timeout: - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) logger.debug( "Socket timeout, remote: {0}. ({1})".format(address[0], session.id) ) except socket.error: - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) logger.debug( "Connection reset by peer, remote: {0}. ({1})".format( address[0], session.id diff --git a/conpot/protocols/tftp/tftp_server.py b/conpot/protocols/tftp/tftp_server.py index ef9c4379..4a884504 100644 --- a/conpot/protocols/tftp/tftp_server.py +++ b/conpot/protocols/tftp/tftp_server.py @@ -24,6 +24,7 @@ from conpot.protocols.tftp import tftp_handler from gevent.server import DatagramServer import conpot.core as conpot_core +from conpot.core import attack_session from conpot.core.protocol_wrapper import conpot_protocol from gevent import event from tftpy import TftpException, TftpTimeout @@ -117,7 +118,7 @@ def handle(self, buffer, client_addr): client_addr[0], client_addr[1] ) ) - session.add_event({"type": "NEW_CONNECTION"}) + session.add_event({"type": attack_session.NEW_CONNECTION}) logger.debug("Read %d bytes", len(buffer)) context = tftp_handler.TFTPContextServer( client_addr[0], client_addr[1], self.timeout, self.root, None, None @@ -125,13 +126,13 @@ def handle(self, buffer, client_addr): context.vfs, context.data_fs = self.vfs, self.data_fs if self.shutdown: logger.info("Shutting down now. Disconnecting {}".format(client_addr)) - session.add_event({"type": "CONNECTION_TERMINATED"}) + session.add_event({"type": attack_session.CONNECTION_TERMINATED}) try: context.start(buffer) context.cycle() except TftpTimeout as err: logger.info("Timeout occurred %s: %s" % (context, str(err))) - session.add_event({"type": "CONNECTION_TIMEOUT"}) + session.add_event({"type": attack_session.CONNECTION_TIMEOUT}) context.retry_count += 1 # TODO: We should accept retries from the user. if context.retry_count >= self.TIMEOUT_RETRIES: @@ -149,7 +150,7 @@ def handle(self, buffer, client_addr): context, str(err) ) ) - session.add_event({"type": "CONNECTION_LOST"}) + session.add_event({"type": attack_session.CONNECTION_LOST}) logger.info("TFTP: terminating connection: {}".format(context)) session.set_ended() context.end() diff --git a/conpot/protocols/triconex/triconex.xsd b/conpot/protocols/triconex/triconex.xsd new file mode 100644 index 00000000..98bdc39e --- /dev/null +++ b/conpot/protocols/triconex/triconex.xsd @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conpot/protocols/triconex/triconex_server.py b/conpot/protocols/triconex/triconex_server.py new file mode 100644 index 00000000..3dcf87ef --- /dev/null +++ b/conpot/protocols/triconex/triconex_server.py @@ -0,0 +1,363 @@ +# BSD 3-Clause License +# +# Copyright (c) 2018, Nozomi Networks +# Copyright (c) 2020, MushMush +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import socket +import struct +from lxml import etree +from gevent.server import DatagramServer +import conpot.core as conpot_core +from conpot.utils.ext_ip import get_interface_ip +from conpot.core.protocol_wrapper import conpot_protocol +from conpot.core import attack_session +import crcmod +import time +import logging + +logger = logging.getLogger(__name__) +cf = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0, xorOut=0) + + +def build_slot(leds0, leds1, model, color): + slotfmt = "<" + 32 * "B" + return struct.pack( + slotfmt, + leds0, + leds1, + model, + color, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ) + + +# Construct slots +mps = { + "active": build_slot(0x15, 0x21, 0xF0, 0x01), + "passive": build_slot(0x02, 0x01, 0xF0, 0x02), +} + +slotsdesc = { + "empty": build_slot(0, 0, 0, 0), + "com": build_slot(5, 33, 55, 1), + "do": build_slot(5, 16, 20, 1), + "di": build_slot(5, 32, 11, 1), + "him": build_slot(5, 22, 53, 1), + "ddo": build_slot(0x4F, 0x21, 0x5C, 0x2), +} + + +def build_chassis_status_response( + triconId=0, + seq=0, + node=2, + projname="FIRSTPROJ", + activemp=0, + mpmodel=1, + slots=["com"], +): + # Project segment + data = struct.pack( + " + + + + + + diff --git a/conpot/templates/triconex/triconex/triconex.xml b/conpot/templates/triconex/triconex/triconex.xml new file mode 100644 index 00000000..829b77bc --- /dev/null +++ b/conpot/templates/triconex/triconex/triconex.xml @@ -0,0 +1,8 @@ + + + empty + empty + empty + empty + + \ No newline at end of file diff --git a/conpot/tests/test_triconex.py b/conpot/tests/test_triconex.py new file mode 100644 index 00000000..8de01c8b --- /dev/null +++ b/conpot/tests/test_triconex.py @@ -0,0 +1,18 @@ +import unittest + +from conpot.utils.greenlet import spawn_test_server, teardown_test_server +from conpot.protocols.triconex.triconex_server import TriconexServer + + +class TestTriconexServer(unittest.TestCase): + def setUp(self): + self.triconex_server, self.greenlet = spawn_test_server( + TriconexServer, template="default", protocol="triconex" + ) + + def tearDown(self): + teardown_test_server(self.triconex_server, self.greenlet) + + +if __name__ == "__main__": + unittest.main() diff --git a/requirements.txt b/requirements.txt index bae411d1..2b1d0445 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,3 +37,4 @@ pytest tox pycrypto sphinx_rtd_theme +crcmod==1.7 From 59a3e6808f85a508be1da655f6e0aa8c1df990ce Mon Sep 17 00:00:00 2001 From: Lukas Rist Date: Sun, 8 Nov 2020 21:52:43 +0100 Subject: [PATCH 2/4] black fixes --- conpot/protocols/ftp/ftp_handler.py | 4 +++- conpot/protocols/modbus/modbus_server.py | 8 ++++++-- conpot/tests/test_triconex.py | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/conpot/protocols/ftp/ftp_handler.py b/conpot/protocols/ftp/ftp_handler.py index 772fb3dd..d43a069f 100644 --- a/conpot/protocols/ftp/ftp_handler.py +++ b/conpot/protocols/ftp/ftp_handler.py @@ -938,7 +938,9 @@ def process_ftp_command(self): ) # TODO: what to respond here? For now just terminate the session self.disconnect_client = True - self.session.add_event({"type": attack_session.CONNECTION_TERMINATED}) + self.session.add_event( + {"type": attack_session.CONNECTION_TERMINATED} + ) elif not (self.metrics.timeout() < self.config.timeout) and ( not self._data_channel ): diff --git a/conpot/protocols/modbus/modbus_server.py b/conpot/protocols/modbus/modbus_server.py index 1d6c285b..7bdd58c7 100644 --- a/conpot/protocols/modbus/modbus_server.py +++ b/conpot/protocols/modbus/modbus_server.py @@ -170,7 +170,9 @@ def handle(self, sock, address): logger.info( "Modbus connection terminated with client %s.", address[0] ) - session.add_event({"type": attack_session.CONNECTION_TERMINATED}) + session.add_event( + {"type": attack_session.CONNECTION_TERMINATED} + ) sock.shutdown(socket.SHUT_RDWR) sock.close() break @@ -180,7 +182,9 @@ def handle(self, sock, address): "Modbus client ignored due to invalid addressing." " (%s)", session.id, ) - session.add_event({"type": attack_session.CONNECTION_TERMINATED}) + session.add_event( + {"type": attack_session.CONNECTION_TERMINATED} + ) sock.shutdown(socket.SHUT_RDWR) sock.close() break diff --git a/conpot/tests/test_triconex.py b/conpot/tests/test_triconex.py index 8de01c8b..ee599634 100644 --- a/conpot/tests/test_triconex.py +++ b/conpot/tests/test_triconex.py @@ -9,7 +9,7 @@ def setUp(self): self.triconex_server, self.greenlet = spawn_test_server( TriconexServer, template="default", protocol="triconex" ) - + def tearDown(self): teardown_test_server(self.triconex_server, self.greenlet) From 2155b0917b95e14ae7c9579bbc6b2b0bdcadeb0e Mon Sep 17 00:00:00 2001 From: Lukas Rist Date: Sun, 20 Feb 2022 12:36:14 +0100 Subject: [PATCH 3/4] merge fixes --- .../protocols/kamstrup_management/kamstrup_management_server.py | 1 + conpot/protocols/kamstrup_meter/kamstrup_server.py | 1 + 2 files changed, 2 insertions(+) diff --git a/conpot/protocols/kamstrup_management/kamstrup_management_server.py b/conpot/protocols/kamstrup_management/kamstrup_management_server.py index b1547f65..80746ea0 100644 --- a/conpot/protocols/kamstrup_management/kamstrup_management_server.py +++ b/conpot/protocols/kamstrup_management/kamstrup_management_server.py @@ -21,6 +21,7 @@ import conpot.core as conpot_core from .command_responder import CommandResponder from conpot.core.protocol_wrapper import conpot_protocol +from conpot.core import attack_session from conpot.utils.networking import str_to_bytes logger = logging.getLogger(__name__) diff --git a/conpot/protocols/kamstrup_meter/kamstrup_server.py b/conpot/protocols/kamstrup_meter/kamstrup_server.py index 82b3d183..df565eab 100644 --- a/conpot/protocols/kamstrup_meter/kamstrup_server.py +++ b/conpot/protocols/kamstrup_meter/kamstrup_server.py @@ -26,6 +26,7 @@ from .request_parser import KamstrupRequestParser from .command_responder import CommandResponder from conpot.core.protocol_wrapper import conpot_protocol +from conpot.core import attack_session logger = logging.getLogger(__name__) From c0e9f0d7d67fc6c7754199d6f69928f7728602d8 Mon Sep 17 00:00:00 2001 From: Lukas Rist Date: Sun, 20 Feb 2022 12:39:42 +0100 Subject: [PATCH 4/4] import fixed --- conpot/protocols/triconex/triconex_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conpot/protocols/triconex/triconex_server.py b/conpot/protocols/triconex/triconex_server.py index 3dcf87ef..93ba646c 100644 --- a/conpot/protocols/triconex/triconex_server.py +++ b/conpot/protocols/triconex/triconex_server.py @@ -35,7 +35,7 @@ from lxml import etree from gevent.server import DatagramServer import conpot.core as conpot_core -from conpot.utils.ext_ip import get_interface_ip +from conpot.utils.networking import get_interface_ip from conpot.core.protocol_wrapper import conpot_protocol from conpot.core import attack_session import crcmod