def handle(self, sock, address): session = conpot_core.get_session('kamstrup_management_protocol', address[0], address[1]) logger.info('New connection from {0}:{1}. ({2})'.format(address[0], address[1], session.id)) try: sock.send(self.banner.format( conpot_core.get_databus().get_value("mac_address"))) while True: request = sock.recv(1024) if not request: logger.info('Client disconnected. ({0})'.format(session.id)) break logdata = {'request': request} response = self.command_responder.respond(request) logdata['response'] = response logger.debug('Kamstrup management traffic from {0}: {1} ({2})'.format(address[0], logdata, session.id)) session.add_event(logdata) gevent.sleep(0.25) # TODO measure delay and/or RTT if response is None: break sock.send(response) except socket.timeout: logger.debug('Socket timeout, remote: {0}. ({1})'.format(address[0], session.id)) sock.close()
def log(self, version, msg_type, addr, req_varBinds, res_varBinds=None, sock=None): session = conpot_core.get_session("snmp", addr[0], addr[1], get_interface_ip(addr[0]), sock.getsockname()[1]) req_oid = req_varBinds[0][0] req_val = req_varBinds[0][1] event_type = "SNMPv{0} {1}".format(version, msg_type) request = {"oid": str(req_oid), "val": str(req_val)} response = None logger.info("%s request from %s: %s %s", event_type, addr, req_oid, req_val) if res_varBinds: res_oid = ".".join(map(str, res_varBinds[0][0])) res_val = res_varBinds[0][1] logger.info("%s response to %s: %s %s", event_type, addr, res_oid, res_val) response = {"oid": str(res_oid), "val": str(res_val)} session.add_event({ "type": event_type, "request": request, "response": response })
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session('modbus', address[0], address[1]) self.start_time = time.time() logger.info('New connection from {0}:{1}. ({2})'.format(address[0], address[1], session.id)) try: while True: request = sock.recv(7) if not request: logger.info('Client disconnected. ({0})'.format(session.id)) break if request.strip().lower() == 'quit.': logger.info('Client quit. ({0})'.format(session.id)) break tr_id, pr_id, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): new_byte = sock.recv(1) request += new_byte query = modbus_tcp.TcpQuery() # logdata is a dictionary containing request, slave_id, function_code and response response, logdata = self._databank.handle_request(query, request) logdata['request'] = request.encode('hex') session.add_event(logdata) logger.debug('Modbus traffic from {0}: {1} ({2})'.format(address[0], logdata, session.id)) if response: sock.sendall(response) except socket.timeout: logger.debug('Socket timeout, remote: {0}. ({1})'.format(address[0], session.id))
def log(self, version, request_type, addr, request, response=None): session = conpot_core.get_session( 'http', addr[0], addr[1], self.connection._sock.getsockname()[0], self.connection._sock.getsockname()[1]) log_dict = { 'remote': addr, 'timestamp': datetime.utcnow(), 'data_type': 'http', 'dst_port': self.server.server_port, 'data': { 0: { 'request': '{0} {1}: {2}'.format(version, request_type, request) } } } logger.info('%s %s request from %s: %s. %s', version, request_type, addr, request, session.id) if response: logger.info('%s response to %s: %s. %s', version, addr, response, session.id) log_dict['data'][0]['response'] = '{0} response: {1}'.format( version, response) session.add_event({ 'request': str(request), 'response': str(response) }) else: session.add_event({'request': str(request)})
def handle(self, data, address): session = conpot_core.get_session('bacnet', address[0], address[1]) logger.info('New Bacnet connection from %s:%d. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) # I'm not sure if gevent DatagramServer handles issues where the # received data is over the MTU -> fragmentation if data: pdu = PDU() pdu.pduData = data apdu = APDU() npdu = NPDU() bvlpdu = BVLPDU() try: bvlpdu.decode(pdu) npdu.decode(bvlpdu) apdu.decode(npdu) except DecodingError as e: logger.error("DecodingError: %s", e) logger.error("PDU: " + format(pdu)) return self.bacnet_app.indication(apdu, address, self.thisDevice) self.bacnet_app.response(self.bacnet_app._response, npdu, bvlpdu, address) logger.info('Bacnet client disconnected %s:%d. (%s)', address[0], address[1], session.id)
def log(self, version, request_type, addr, request, response=None): session = conpot_core.get_session('http', addr[0], addr[1]) log_dict = { 'remote': addr, 'timestamp': datetime.utcnow(), 'data_type': 'http', 'data': { 0: { 'request': '{0} {1}: {2}'.format(version, request_type, request) } } } logger.info('{0} {1} request from {2}: {3}. {4}'.format( version, request_type, addr, request, session.id)) if response: logger.info('{0} response to {1}: {2}. {3}'.format( version, addr, response, session.id)) log_dict['data'][0]['response'] = '{0} response: {1}'.format( version, response) session.add_event({ 'request': str(request), 'response': str(response) }) else: session.add_event({'request': str(request)})
def handle(self, data, address): session = conpot_core.get_session( "bacnet", address[0], address[1], get_interface_ip(address[0]), self.server.server_port, ) logger.info("New Bacnet connection from %s:%d. (%s)", address[0], address[1], session.id) session.add_event({"type": "NEW_CONNECTION"}) # I'm not sure if gevent DatagramServer handles issues where the # received data is over the MTU -> fragmentation if data: pdu = PDU() pdu.pduData = bytearray(data) apdu = APDU() try: apdu.decode(pdu) except DecodingError: logger.warning("DecodingError - PDU: {}".format(pdu)) return self.bacnet_app.indication(apdu, address, self.thisDevice) # send an appropriate response from BACnet app to the attacker self.bacnet_app.response(self.bacnet_app._response, address) logger.info("Bacnet client disconnected %s:%d. (%s)", address[0], address[1], session.id)
def log(self, version, msg_type, addr, dst_host, dst_port, req_varBinds, res_varBinds=None): session = conpot_core.get_session('snmp', addr[0], addr[1], dst_host, dst_port) req_oid = req_varBinds[0][0] req_val = req_varBinds[0][1] event_type = 'SNMPv{0} {1}'.format(version, msg_type) request = {'oid': str(req_oid), 'val': str(req_val)} response = None logger.info('%s request from %s: %s %s', event_type, addr, req_oid, req_val) if res_varBinds: res_oid = ".".join(map(str, res_varBinds[0][0])) res_val = res_varBinds[0][1] logger.info('%s response to %s: %s %s', event_type, addr, res_oid, res_val) response = {'oid': str(res_oid), 'val': str(res_val)} session.add_event({ 'type': event_type, 'request': request, 'response': response })
def handle(self, sock, address): session = conpot_core.get_session(self.proxy_id, address[0], address[1]) logger.info('New connection from {0}:{1} on {2} proxy. ({3})'.format( address[0], address[1], self.proxy_id, session.id)) proxy_socket = socket() if self.keyfile and self.certfile: proxy_socket = wrap_socket(proxy_socket, self.keyfile, self.certfile) try: proxy_socket.connect((self.proxy_host, self.proxy_port)) except _socket.error as ex: logger.error( 'Error while connecting to proxied service at ({0}, {1}): {2}'. format(self.proxy_host, self.proxy_port, ex)) self._close([proxy_socket, sock]) return sockets = [proxy_socket, sock] while len(sockets) == 2: gevent.sleep() sockets_read, _, sockets_err = select.select( sockets, [], sockets, 10) if len(sockets_err) > 0: self._close([proxy_socket, sock]) break for s in sockets_read: data = s.recv(1024) if len(data) is 0: self._close([proxy_socket, sock]) if s is proxy_socket: logging.info( 'Closing proxy connection because the proxied socket closed.' ) sockets = [] break elif s is sock: logging.info( 'Closing proxy connection because the remote socket closed' ) sockets = [] break else: assert False if s is proxy_socket: self.handle_out_data(data, sock, session) elif s is sock: self.handle_in_data(data, proxy_socket, session) else: assert False proxy_socket.close() sock.close()
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session('modbus', address[0], address[1]) self.start_time = time.time() logger.info('New connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) try: while True: request = sock.recv(7) if not request: logger.info('Client disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) break if request.strip().lower() == 'quit.': logger.info('Client quit. (%s)', session.id) session.add_event({'type': 'CONNECTION_QUIT'}) break tr_id, pr_id, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): new_byte = sock.recv(1) request += new_byte query = modbus_tcp.TcpQuery() # logdata is a dictionary containing request, slave_id, function_code and response response, logdata = self._databank.handle_request(query, request, self.mode) logdata['request'] = request.encode('hex') session.add_event(logdata) logger.info('Modbus traffic from {0}: {1} ({2})'.format(address[0], logdata, session.id)) if response: sock.sendall(response) logger.info('Modbus response sent to {0}'.format(address[0])) else: # MB serial connection addressing UID=0 if (self.mode == 'serial' and logdata['slave_id'] == 0): time.sleep(self.delay/1000) # millisecs logger.debug('Modbus server\'s turnaround delay expired.') logger.info('Connection terminated with client {0}.'.format(address[0])) session.add_event({'type': 'CONNECTION_TERMINATED'}) sock.shutdown(socket.SHUT_RDWR) sock.close() break # Invalid addressing else: logger.info('Client ignored due to invalid addressing. ({0})'.format(session.id)) session.add_event({'type': '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'})
def handle(self, sock, address): session = conpot_core.get_session( "kamstrup_management_protocol", address[0], address[1], sock.getsockname()[0], sock.getsockname()[1], ) logger.info( "New Kamstrup connection from %s:%s. (%s)", address[0], address[1], session.id, ) session.add_event({"type": "NEW_CONNECTION"}) try: sock.send( str_to_bytes( self.banner.format( conpot_core.get_databus().get_value("mac_address") ) ) ) while True: data = sock.recv(1024) if not data: logger.info("Kamstrup client disconnected. (%s)", session.id) session.add_event({"type": "CONNECTION_LOST"}) break request = data.decode() logdata = {"request": request} response = self.command_responder.respond(request) logdata["response"] = response logger.info( "Kamstrup management traffic from %s: %s (%s)", address[0], logdata, session.id, ) session.add_event(logdata) gevent.sleep(0.25) # TODO measure delay and/or RTT if response is None: session.add_event({"type": "CONNECTION_LOST"}) break # encode data before sending reply = str_to_bytes(response) sock.send(reply) except socket.timeout: logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id) session.add_event({"type": "CONNECTION_LOST"}) sock.close()
def setup(self): """Connect incoming connection to a FTP session""" self.session = conpot_core.get_session('ftp', self.client_address[0], self.client_address[1], self.request._sock.getsockname()[0] ,self.request._sock.getsockname()[1]) logger.info('New FTP connection from {}:{}. ({})'.format(self.client_address[0], self.client_address[1], self.session.id)) self.session.add_event({'type': '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) ? return socketserver.BaseRequestHandler.setup(self)
def handle(self, sock, address): session = conpot_core.get_session('kamstrup_protocol', address[0], address[1]) logger.info('New connection from {0}:{1}. ({2})'.format( address[0], address[1], session.id)) session.add_event({'type': 'NEW_CONNECTION'}) server_active = True parser = request_parser.KamstrupRequestParser() try: while server_active: raw_request = sock.recv(1024) if not raw_request: logger.info('Client disconnected. ({0})'.format( session.id)) session.add_event({'type': 'CONNECTION_LOST'}) break for x in raw_request: parser.add_byte(x) while True: request = parser.get_request() if not request: session.add_event({'type': 'CONNECTION_LOST'}) break else: logdata = { 'request': binascii.hexlify(bytearray(request.message_bytes)) } response = self.command_responder.respond(request) # real Kamstrup meters has delay in this interval gevent.sleep(random.uniform(0.24, 0.34)) if response: serialized_response = response.serialize() logdata['response'] = binascii.hexlify( serialized_response) logger.debug( 'Kamstrup traffic from {0}: {1} ({2})'.format( address[0], logdata, session.id)) sock.send(serialized_response) session.add_event(logdata) else: session.add_event(logdata) break except socket.timeout: logger.debug('Socket timeout, remote: {0}. ({1})'.format( address[0], session.id)) session.add_event({'type': 'CONNECTION_LOST'}) sock.close()
def handle(self, buffer, client_addr): session = conpot_core.get_session( "tftp", client_addr[0], client_addr[1], get_interface_ip(client_addr[0]), self.server._socket.getsockname()[1], ) logger.info( "New TFTP client has connected. Connection from {}:{}. ".format( client_addr[0], client_addr[1])) session.add_event({"type": "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) 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"}) 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"}) context.retry_count += 1 # TODO: We should accept retries from the user. if context.retry_count >= self.TIMEOUT_RETRIES: logger.info("TFTP: Hit max {} retries on {}, giving up".format( self.TIMEOUT_RETRIES, context)) else: logger.info("TFTP: resending on session %s" % context) context.state.resendLast() except TftpException as err: logger.info( "TFTP: Fatal exception thrown from session {}: {}".format( context, str(err))) session.add_event({"type": "CONNECTION_LOST"}) logger.info("TFTP: terminating connection: {}".format(context)) session.set_ended() context.end() # Gathering up metrics before terminating the connection. metrics = context.metrics if metrics.duration == 0: logger.info("Duration too short, rate undetermined") else: logger.info("Transferred %d bytes in %.2f seconds" % (metrics.bytes, metrics.duration)) logger.info("Average rate: %.2f kbps" % metrics.kbps) logger.info("%.2f bytes in resent data" % metrics.resent_bytes) logger.info("%d duplicate packets" % metrics.dupcount) del context
def setup(self): """Connect incoming connection to a FTP session""" self.session = conpot_core.get_session( 'ftp', self.client_address[0], self.client_address[1], self.request._sock.getsockname()[0], self.request._sock.getsockname()[1]) logger.info('New FTP connection from {}:{}. ({})'.format( self.client_address[0], self.client_address[1], self.session.id)) self.session.add_event({'type': '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) ? return socketserver.BaseRequestHandler.setup(self)
def handle(self, sock, address): session = conpot_core.get_session(self.proxy_id, address[0], address[1]) logger.info('New connection from {0}:{1} on {2} proxy. ({3})'.format(address[0], address[1], self.proxy_id, session.id)) proxy_socket = socket() if self.keyfile and self.certfile: proxy_socket = wrap_socket(proxy_socket, self.keyfile, self.certfile) try: proxy_socket.connect((self.proxy_host, self.proxy_port)) except _socket.error as ex: logger.error('Error while connecting to proxied service at ({0}, {1}): {2}' .format(self.proxy_host, self.proxy_port, ex)) self._close([proxy_socket, sock]) return sockets = [proxy_socket, sock] while len(sockets) == 2: gevent.sleep() sockets_read, _, sockets_err = select.select(sockets, [], sockets, 10) if len(sockets_err) > 0: self._close([proxy_socket, sock]) break for s in sockets_read: data = s.recv(1024) if len(data) is 0: self._close([proxy_socket, sock]) if s is proxy_socket: logging.info('Closing proxy connection because the proxied socket closed.') sockets = [] break elif s is sock: logging.info('Closing proxy connection because the remote socket closed') sockets = [] break else: assert False if s is proxy_socket: self.handle_out_data(data, sock, session) elif s is sock: self.handle_in_data(data, proxy_socket, session) else: assert False proxy_socket.close() sock.close()
def log(self, version, msg_type, addr, req_varBinds, res_varBinds=None): session = conpot_core.get_session('snmp', addr[0], addr[1]) req_oid = req_varBinds[0][0] req_val = req_varBinds[0][1] log_dict = {'remote': addr, 'timestamp': datetime.utcnow(), 'data_type': 'snmp', 'data': {0: {'request': 'SNMPv{0} {1}: {2} {3}'.format(version, msg_type, req_oid, req_val)}}} logger.info('SNMPv%s %s request from %s: %s %s', version, msg_type, addr, req_oid, req_val) if res_varBinds: res_oid = ".".join(map(str, res_varBinds[0][0])) res_val = res_varBinds[0][1] logger.info('SNMPv%s response to %s: %s %s', version, addr, res_oid, res_val) log_dict['data'][0]['response'] = 'SNMPv{0} response: {1} {2}'.format(version, res_oid, res_val)
def log(self, version, request_type, addr, request, response=None): session = conpot_core.get_session('http', addr[0], addr[1]) log_dict = {'remote': addr, 'timestamp': datetime.utcnow(), 'data_type': 'http', 'data': {0: {'request': '{0} {1}: {2}'.format(version, request_type, request)}}} logger.info('{0} {1} request from {2}: {3}. {4}'.format(version, request_type, addr, request, session.id)) if response: logger.info('{0} response to {1}: {2}. {3}'.format(version, addr, response, session.id)) log_dict['data'][0]['response'] = '{0} response: {1}'.format(version, response) session.add_event({'request': str(request), 'response': str(response)}) else: session.add_event({'request': str(request)})
def log(self, version, msg_type, addr, req_varBinds, res_varBinds=None, sock=None): session = conpot_core.get_session('snmp', addr[0], addr[1], get_interface_ip(addr[0]), sock.getsockname()[1]) req_oid = req_varBinds[0][0] req_val = req_varBinds[0][1] event_type = 'SNMPv{0} {1}'.format(version, msg_type) request = {'oid': str(req_oid), 'val': str(req_val)} response = None logger.info('%s request from %s: %s %s', event_type, addr, req_oid, req_val) if res_varBinds: res_oid = ".".join(map(str, res_varBinds[0][0])) res_val = res_varBinds[0][1] logger.info('%s response to %s: %s %s', event_type, addr, res_oid, res_val) response = {'oid': str(res_oid), 'val': str(res_val)} session.add_event({'type': event_type, 'request': request, 'response': response})
def handle(self, sock, address): session = conpot_core.get_session('kamstrup_protocol', address[0], address[1]) logger.info('New Kamstrup connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) self.server_active = True parser = request_parser.KamstrupRequestParser() try: while self.server_active: raw_request = sock.recv(1024) if not raw_request: logger.info('Kamstrup client disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) break for x in raw_request: parser.add_byte(x) while True: request = parser.get_request() if not request: session.add_event({'type': 'CONNECTION_LOST'}) break else: logdata = {'request': binascii.hexlify(bytearray(request.message_bytes))} response = self.command_responder.respond(request) # real Kamstrup meters has delay in this interval gevent.sleep(random.uniform(0.24, 0.34)) if response: serialized_response = response.serialize() logdata['response'] = binascii.hexlify(serialized_response) logger.info('Kamstrup traffic from %s: %s (%s)', address[0], logdata, session.id) sock.send(serialized_response) session.add_event(logdata) else: session.add_event(logdata) break except socket.timeout: logger.debug('Socket timeout, remote: %s. (%s)', address[0], session.id) session.add_event({'type': 'CONNECTION_LOST'}) sock.close()
def log(self, version, msg_type, addr, req_varBinds, res_varBinds=None, sock=None): addr = ProxyAddresses.try_replace_addr(addr) session = conpot_core.get_session('snmp', addr[0], addr[1], get_interface_ip(addr[0]), sock.getsockname()[1]) req_oid = req_varBinds[0][0] req_val = req_varBinds[0][1] event_type = 'SNMPv{0} {1}'.format(version, msg_type) request = {'oid': str(req_oid), 'val': str(req_val)} response = None logger.info('%s request from %s: %s %s', event_type, addr, req_oid, req_val) if res_varBinds: res_oid = ".".join(map(str, res_varBinds[0][0])) res_val = res_varBinds[0][1] logger.info('%s response to %s: %s %s', event_type, addr, res_oid, res_val) response = {'oid': str(res_oid), 'val': str(res_val)} session.add_event({'type': event_type, 'request': request, 'response': response})
def log(self, version, request_type, addr, request, response=None): session = conpot_core.get_session('http', addr[0], addr[1], self.connection._sock.getsockname()[0], self.connection._sock.getsockname()[1]) log_dict = {'remote': addr, 'timestamp': datetime.utcnow(), 'data_type': 'http', 'dst_port': self.server.server_port, 'data': {0: {'request': '{0} {1}: {2}'.format(version, request_type, request)}}} logger.info('%s %s request from %s: %s. %s', version, request_type, addr, request, session.id) if response: logger.info('%s response to %s: %s. %s', version, addr, response, session.id) log_dict['data'][0]['response'] = '{0} response: {1}'.format(version, response) session.add_event({'request': str(request), 'response': str(response)}) else: session.add_event({'request': str(request)})
def handle(self, conn, address, enip_process=None, delay=None, **kwds): """ Handle an incoming connection """ host, port = address if address else ("UDP", "UDP") name = "ENIP_%s" % port session = conpot_core.get_session( "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"}) tcp = conn.family == socket.AF_INET and conn.type == socket.SOCK_STREAM udp = conn.family == socket.AF_INET and conn.type == socket.SOCK_DGRAM if tcp: try: conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) except Exception as e: logger.error( "%s unable to set TCP_NODELAY for client %r: %s", name, address, e ) try: conn.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) except Exception as e: logger.error( "%s unable to set SO_KEEPALIVE for client %r: %s", name, address, e ) self.handle_tcp( conn, address, session, name=name, enip_process=enip_process, delay=delay, **kwds ) elif udp: self.handle_udp( conn, name=name, enip_process=enip_process, session=session, **kwds ) else: raise NotImplementedError("Unknown socket protocol for EtherNet/IP CIP")
def handle(self, data, address): session = conpot_core.get_session('bacnet', address[0], address[1], get_interface_ip(address[0]), self.server.server_port) logger.info('New Bacnet connection from %s:%d. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) # I'm not sure if gevent DatagramServer handles issues where the # received data is over the MTU -> fragmentation if data: pdu = PDU() pdu.pduData = bytearray(data) apdu = APDU() try: apdu.decode(pdu) except DecodingError: logger.warning("DecodingError - PDU: {}".format(pdu)) return self.bacnet_app.indication(apdu, address, self.thisDevice) # send an appropriate response from BACnet app to the attacker self.bacnet_app.response(self.bacnet_app._response, address) logger.info('Bacnet client disconnected %s:%d. (%s)', address[0], address[1], session.id)
def handle(self, data, address): session = conpot_core.get_session('bacnet', address[0], address[1]) logger.info('New Bacnet connection from %s:%d. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) # I'm not sure if gevent DatagramServer handles issues where the # received data is over the MTU -> fragmentation if data: pdu = PDU() pdu.pduData = data apdu = APDU() try: apdu.decode(pdu) except DecodingError as e: logger.error("DecodingError: %s", e) logger.error("PDU: " + format(pdu)) return self.bacnet_app.indication(apdu, address, self.thisDevice) self.bacnet_app.response(self.bacnet_app._response, address) logger.info('Bacnet client disconnected %s:%d. (%s)', address[0], address[1], session.id)
def log(self, version, request_type, addr, request, response=None): session = conpot_core.get_session( "http", addr[0], addr[1], self.connection._sock.getsockname()[0], self.connection._sock.getsockname()[1], ) log_dict = { "remote": addr, "timestamp": datetime.utcnow(), "data_type": "http", "dst_port": self.server.server_port, "data": { 0: { "request": "{0} {1}: {2}".format(version, request_type, request) } }, } logger.info( "%s %s request from %s: %s. %s", version, request_type, addr, request, session.id, ) if response: logger.info("%s response to %s: %s. %s", version, addr, response, session.id) log_dict["data"][0]["response"] = "{0} response: {1}".format( version, response) session.add_event({ "request": str(request), "response": str(response) }) else: session.add_event({"request": str(request)})
def handle(self, sock, address): session = conpot_core.get_session('kamstrup_management_protocol', address[0], address[1], self.host, self.port) logger.info('New Kamstrup connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) try: sock.send( self.banner.format( conpot_core.get_databus().get_value("mac_address"))) while True: request = sock.recv(1024) if not request: logger.info('Kamstrup client disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) break logdata = {'request': request} response = self.command_responder.respond(request) logdata['response'] = response logger.info('Kamstrup management traffic from %s: %s (%s)', address[0], logdata, session.id) session.add_event(logdata) gevent.sleep(0.25) # TODO measure delay and/or RTT if response is None: session.add_event({'type': 'CONNECTION_LOST'}) break sock.send(response) except socket.timeout: logger.debug('Socket timeout, remote: %s. (%s)', address[0], session.id) session.add_event({'type': 'CONNECTION_LOST'}) sock.close()
def handle(self, buffer, client_addr): session = conpot_core.get_session('tftp', client_addr[0], client_addr[1], get_interface_ip(client_addr[0]), self.server._socket.getsockname()[1]) logger.info('New TFTP client has connected. Connection from {}:{}. '.format(client_addr[0], client_addr[1])) session.add_event({'type': '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) 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'}) 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'}) context.retry_count += 1 # TODO: We should accept retries from the user. if context.retry_count >= self.TIMEOUT_RETRIES: logger.info("TFTP: Hit max {} retries on {}, giving up".format(self.TIMEOUT_RETRIES, context)) else: logger.info("TFTP: resending on session %s" % context) context.state.resendLast() except TftpException as err: logger.info("TFTP: Fatal exception thrown from session {}: {}".format(context, str(err))) session.add_event({'type': 'CONNECTION_LOST'}) logger.info('TFTP: terminating connection: {}'.format(context)) session.set_ended() context.end() # Gathering up metrics before terminating the connection. metrics = context.metrics if metrics.duration == 0: logger.info("Duration too short, rate undetermined") else: logger.info("Transferred %d bytes in %.2f seconds" % (metrics.bytes, metrics.duration)) logger.info("Average rate: %.2f kbps" % metrics.kbps) logger.info("%.2f bytes in resent data" % metrics.resent_bytes) logger.info("%d duplicate packets" % metrics.dupcount) del context
def handle(self, sock, address): session = conpot_core.get_session('kamstrup_management_protocol', address[0], address[1], sock.getsockname()[0], sock.getsockname()[1]) logger.info('New Kamstrup connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) try: sock.send( str_to_bytes(self.banner.format(conpot_core.get_databus().get_value("mac_address"))) ) while True: data = sock.recv(1024) if not data: logger.info('Kamstrup client disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) break request = data.decode() logdata = {'request': request} response = self.command_responder.respond(request) logdata['response'] = response logger.info('Kamstrup management traffic from %s: %s (%s)', address[0], logdata, session.id) session.add_event(logdata) gevent.sleep(0.25) # TODO measure delay and/or RTT if response is None: session.add_event({'type': 'CONNECTION_LOST'}) break # encode data before sending reply = str_to_bytes(response) sock.send(reply) except socket.timeout: logger.debug('Socket timeout, remote: %s. (%s)', address[0], session.id) session.add_event({'type': 'CONNECTION_LOST'}) sock.close()
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session('modbus', address[0], address[1]) self.start_time = time.time() logger.info('New Modbus connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) try: while True: request = sock.recv(7) if not request: logger.info('Modbus client disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) break if request.strip().lower() == 'quit.': logger.info('Modbus client quit. (%s)', session.id) session.add_event({'type': 'CONNECTION_QUIT'}) break tr_id, pr_id, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): new_byte = sock.recv(1) request += new_byte query = modbus_tcp.TcpQuery() # logdata is a dictionary containing request, slave_id, # function_code and response response, logdata = self._databank.handle_request( query, request, self.mode) logdata['request'] = request.encode('hex') session.add_event(logdata) logger.info('Modbus traffic from %s: %s (%s)', address[0], logdata, session.id) if response: sock.sendall(response) logger.info('Modbus response sent to %s', address[0]) else: # MB serial connection addressing UID=0 if (self.mode == 'serial' and logdata['slave_id'] == 0): # delay is in milliseconds time.sleep(self.delay / 1000) logger.debug( 'Modbus server\'s turnaround delay expired.') logger.info( 'Modbus connection terminated with client %s.', address[0]) session.add_event({'type': 'CONNECTION_TERMINATED'}) sock.shutdown(socket.SHUT_RDWR) sock.close() break # Invalid addressing else: logger.info( 'Modbus client ignored due to invalid addressing.' ' (%s)', session.id) session.add_event({'type': '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'})
def handle(self, sock, addr): session = conpot_core.get_session('guardian_ast', addr[0], addr[1], sock.getsockname()[0], sock.getsockname()[1]) logger.info('New GuardianAST connection from %s:%d. (%s)', addr[0], addr[1], session.id) session.add_event({'type': '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) # Default Product names, change based off country needs product1 = self.databus.get_value('product1').ljust(22) product1 = self.databus.get_value('product1').ljust(22) product2 = self.databus.get_value('product2').ljust(22) product3 = self.databus.get_value('product3').ljust(22) product4 = self.databus.get_value('product4').ljust(22) # Create random Numbers for the volumes # # this will crate an initial Volume and then the second value based # off the orig value. vol1 = self.databus.get_value('vol1') vol1tc = random.randint(vol1, vol1+200) vol2 = self.databus.get_value('vol2') vol2tc = random.randint(vol2, vol2+200) vol3 = self.databus.get_value('vol3') vol3tc = random.randint(vol3, vol3+200) vol4 = self.databus.get_value('vol4') vol4tc = random.randint(vol4, vol4+200) # unfilled space ULLAGE ullage1 = str(self.databus.get_value('ullage1')) ullage2 = str(self.databus.get_value('ullage2')) ullage3 = str(self.databus.get_value('ullage3')) ullage4 = str(self.databus.get_value('ullage3')) # Height of tank height1 = str(self.databus.get_value('height1')).ljust(5, '0') height2 = str(self.databus.get_value('height2')).ljust(5, '0') height3 = str(self.databus.get_value('height3')).ljust(5, '0') height4 = str(self.databus.get_value('height4')).ljust(5, '0') # Water in tank, this is a variable that needs to be low h2o1 = str(self.databus.get_value('h2o1')).ljust(4, '0') h2o2 = str(self.databus.get_value('h2o2')).ljust(4, '0') h2o3 = str(self.databus.get_value('h2o3')).ljust(4, '0') h2o4 = str(self.databus.get_value('h2o4')).ljust(4, '0') # Temperature of the tank, this will need to be between 50 - 60 temp1 = str(self.databus.get_value('temp1')).ljust(5, '0') temp2 = str(self.databus.get_value('temp2')).ljust(5, '0') temp3 = str(self.databus.get_value('temp3')).ljust(5, '0') temp4 = str(self.databus.get_value('temp4')).ljust(5, '0') station = self.databus.get_value('station_name') # This function is to set-up up the message to be sent upon a successful I20100 command being sent # The final message is sent with a current date/time stamp inside of the main loop. def I20100(): ret = '\nI20100\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nIN-TANK INVENTORY\n\n' ret += 'TANK PRODUCT VOLUME TC VOLUME ULLAGE HEIGHT WATER TEMP' ret += '\n 1 ' + product1 + str(vol1) + ' ' + str(vol1tc) + ' ' + ullage1 + ' ' + height1 + ' ' + h2o1 + ' ' + temp1 ret += '\n 2 ' + product2 + str(vol2) + ' ' + str(vol2tc) + ' ' + ullage2 + ' ' + height2 + ' ' + h2o2 + ' ' + temp2 ret += '\n 3 ' + product3 + str(vol3) + ' ' + str(vol3tc) + ' ' + ullage3 + ' ' + height3 + ' ' + h2o3 + ' ' + temp3 ret += '\n 4 ' + product4 + str(vol4) + ' ' + str(vol4tc) + ' ' + ullage4 + ' ' + height4 + ' ' + h2o4 + ' ' + temp4 ret += '\n' return ret ########################################################################### # # Only one Tank is listed currently in the I20200 command # ########################################################################### def I20200(): ret = '\nI20200\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nDELIVERY REPORT\n\n' ret += 'T 1:' + product1 + '\nINCREASE DATE / TIME GALLONS TC GALLONS WATER TEMP DEG F HEIGHT\n\n' ret += ' END: ' + str(fill_stop.strftime('%m/%d/%Y %H:%M')) + ' ' + str(vol1 + 300) + ' ' + str(vol1tc + 300) + ' ' + h2o1 + ' ' + temp1 + ' ' + height1 + '\n' ret += ' START: ' + str(fill_start.strftime('%m/%d/%Y %H:%M')) + ' ' + str(vol1 - 300) + ' ' + str(vol1tc - 300) + ' ' + h2o1 + ' ' + temp1 + ' ' + str(float(height1) - 23) + '\n' ret += ' AMOUNT: ' + str(vol1) + ' ' + str(vol1tc) + '\n\n' return ret ########################################################################### # # I20300 In-Tank Leak Detect Report # ########################################################################### def I20300(): ret = '\nI20300\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n' ret += 'TANK 1 ' + product1 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 2 ' + product2 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 3 ' + product3 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 4 ' + product4 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' return ret ########################################################################### # Shift report command I20400 only one item in report at this time, # but can always add more if needed ########################################################################### def I20400(): ret = '\nI20400\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nSHIFT REPORT\n\n' ret += 'SHIFT 1 TIME: 12:00 AM\n\nTANK PRODUCT\n\n' ret += ' 1 ' + product1 + ' VOLUME TC VOLUME ULLAGE HEIGHT WATER TEMP\n' ret += 'SHIFT 1 STARTING VALUES ' + str(vol1) + ' ' + str(vol1tc) + ' ' + ullage1 + ' ' + height1 + ' ' + h2o1 + ' ' + temp1 + '\n' ret += ' ENDING VALUES ' + str(vol1 + 940) + ' ' + str(vol1tc + 886) + ' ' + str(int(ullage1) + 345) + ' ' + str(float(height1) + 53) + ' ' + h2o1 + ' ' + temp1 + '\n' ret += ' DELIVERY VALUE 0\n' ret += ' TOTALS 940\n\n' return ret ########################################################################### # I20500 In-Tank Status Report ########################################################################### def I20500(): ret = '\nI20500\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n\n' + station + '\n\n\n' ret += 'TANK PRODUCT STATUS\n\n' ret += ' 1 ' + product1 + ' NORMAL\n\n' ret += ' 2 ' + product2 + ' HIGH WATER ALARM\n' ret += ' HIGH WATER WARNING\n\n' ret += ' 3 ' + product3 + ' NORMAL\n\n' ret += ' 4 ' + product4 + ' NORMAL\n\n' return ret while True: try: # Get the initial data request = sock.recv(4096) # The connection has been closed if not request: break while not (b'\n' in request or b'00' in request): request += sock.recv(4096) # if first value is not ^A then do nothing # thanks John(achillean) for the help if request[:1] != b'\x01': logger.info('Non ^A command attempt %s:%d. (%s)', addr[0], addr[1], session.id) break # if request is less than 6, than do nothing if len(request) < 6: logger.info('Invalid command attempt %s:%d. (%s)', addr[0], addr[1], session.id) break cmds = {"I20100": I20100, "I20200": I20200, "I20300": I20300, "I20400": I20400, "I20500": I20500} cmd = request[1:7].decode() # strip ^A and \n out response = None if cmd in cmds: logger.info('%s command attempt %s:%d. (%s)', cmd, addr[0], addr[1], session.id) response = cmds[cmd]() elif cmd.startswith("S6020"): # change the tank name if cmd.startswith("S60201"): # split string into two, the command, and the data TEMP = request.split(b'S60201') # if length is less than two, print error if len(TEMP) < 2: response = AST_ERROR # Else the command was entered correctly and continue else: # Strip off the carrage returns and new lines TEMP1 = TEMP[1].rstrip(b'\r\n').decode() # if Length is less than 22 if len(TEMP1) < 22: # pad the result to have 22 chars product1 = TEMP1.ljust(22) elif len(TEMP1) > 22: # else only print 22 chars if the result was longer product1 = TEMP1[:20] + " " else: # else it fits fine (22 chars) product1 = TEMP1 logger.info('S60201: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60202"): TEMP = request.split(b'S60202') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product2 = TEMP1.ljust(22) elif len(TEMP1) > 22: product2 = TEMP1[:20] + " " else: product2 = TEMP1 logger.info('S60202: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60203"): TEMP = request.split(b'S60203') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product3 = TEMP1.ljust(22) elif len(TEMP1) > 22: product3 = TEMP1[:20] + " " else: product3 = TEMP1 logger.info('S60203: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60204"): TEMP = request.split(b'S60204') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product4 = TEMP1.ljust(22) elif len(TEMP1) > 22: product4 = TEMP1[:20] + " " else: product4 = TEMP1 logger.info('S60204: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60200"): TEMP = request.split(b'S60200') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product1 = TEMP1.ljust(22) product2 = TEMP1.ljust(22) product3 = TEMP1.ljust(22) product4 = TEMP1.ljust(22) elif len(TEMP1) > 22: product1 = TEMP1[:20] + " " product2 = TEMP1[:20] + " " product3 = TEMP1[:20] + " " product4 = TEMP1[:20] + " " else: product1 = TEMP1 product2 = TEMP1 product3 = TEMP1 product4 = TEMP1 logger.info('S60200: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) else: response = AST_ERROR else: response = AST_ERROR # log what was entered logger.info('%s command attempt %s:%d. (%s)', request, addr[0], addr[1], session.id) if response: sock.send(str_to_bytes(response)) session.add_event({"type": "AST {0}".format(cmd), "request": request, "response": response}) except Exception as e: logger.exception(('Unknown Error: {}'.format(str(e)))) logger.info('GuardianAST client disconnected %s:%d. (%s)', addr[0], addr[1], session.id) session.add_event({'type': 'CONNECTION_LOST'})
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session('s7comm', address[0], address[1]) self.start_time = time.time() logger.info('New connection from {0}:{1}. ({2})'.format(address[0], address[1], session.id)) try: while True: data = sock.recv(4, socket.MSG_WAITALL) if len(data) == 0: break _, _, length = unpack('!BBH', data[:4]) data += sock.recv(length - 4) tpkt_packet = TPKT().parse(data) cotp_base_packet = COTP_BASE_packet().parse(tpkt_packet.payload) if cotp_base_packet.tpdu_type == 0xe0: # connection request cotp_cr_request = COTP_ConnectionRequest().dissect(cotp_base_packet.payload) logger.debug('Received COTP Connection Request: dst-ref:{0} src-ref:{1} dst-tsap:{2} src-tsap:{3} ' 'tpdu-size:{4}. ({5})'.format(cotp_cr_request.dst_ref, cotp_cr_request.src_ref, cotp_cr_request.dst_tsap, cotp_cr_request.src_tsap, cotp_cr_request.tpdu_size, session.id)) # confirm connection response cotp_cc_response = COTP_ConnectionConfirm(cotp_cr_request.src_ref, cotp_cr_request.dst_ref, 0, cotp_cr_request.src_tsap, cotp_cr_request.dst_tsap, 0x0a).assemble() # encapsulate and transmit cotp_resp_base_packet = COTP_BASE_packet(0xd0, 0, cotp_cc_response).pack() tpkt_resp_packet = TPKT(3, cotp_resp_base_packet).pack() sock.send(tpkt_resp_packet) session.add_event({'request': data.encode('hex'), 'response': tpkt_resp_packet.encode('hex')}) data = sock.recv(1024) # another round of parsing payloads tpkt_packet = TPKT().parse(data) cotp_base_packet = COTP_BASE_packet().parse(tpkt_packet.payload) if cotp_base_packet.tpdu_type == 0xf0: logger.debug('Received known COTP TPDU: {0}. ({1})'.format(cotp_base_packet.tpdu_type, session.id)) # will throw exception if the packet does not contain the S7 magic number (0x32) S7_packet = S7().parse(cotp_base_packet.trailer) logger.debug('Received S7 packet: magic:{0} pdu_type:{1} reserved:{2} req_id:{3} param_len:{4} ' 'data_len:{5} result_inf:{6}'.format( S7_packet.magic, S7_packet.pdu_type, S7_packet.reserved, S7_packet.request_id, S7_packet.param_length, S7_packet.data_length, S7_packet.result_info, session.id)) # request pdu if S7_packet.pdu_type == 1: # 0xf0 == Request for connect / pdu negotiate if S7_packet.param == 0xf0: # create S7 response packet s7_resp_negotiate_packet = S7(3, 0, S7_packet.request_id, 0, S7_packet.parameters).pack() # wrap s7 the packet in cotp cotp_resp_negotiate_packet = COTP_BASE_packet(0xf0, 0x80, s7_resp_negotiate_packet).pack() # wrap the cotp packet tpkt_resp_packet = TPKT(3, cotp_resp_negotiate_packet).pack() sock.send(tpkt_resp_packet) session.add_event({'request': data.encode('hex'), 'response': tpkt_resp_packet.encode('hex')}) # handshake done, give some more data. data = sock.recv(1024) while data: tpkt_packet = TPKT().parse(data) cotp_base_packet = COTP_BASE_packet().parse(tpkt_packet.payload) if cotp_base_packet.tpdu_type == 0xf0: S7_packet = S7().parse(cotp_base_packet.trailer) logger.debug('Received S7 packet: magic:{0} pdu_type:{1} reserved:{2} ' 'req_id:{3} param_len:{4} data_len:{5} result_inf:{6}'.format( S7_packet.magic, S7_packet.pdu_type, S7_packet.reserved, S7_packet.request_id, S7_packet.param_length, S7_packet.data_length, S7_packet.result_info, session.id)) response_param, response_data = S7_packet.handle() s7_resp_ssl_packet = S7(7, 0, S7_packet.request_id, 0, response_param, response_data).pack() cotp_resp_ssl_packet = COTP_BASE_packet(0xf0, 0x80, s7_resp_ssl_packet).pack() tpkt_resp_packet = TPKT(3, cotp_resp_ssl_packet).pack() sock.send(tpkt_resp_packet) session.add_event({'request': data.encode('hex'), 'response': tpkt_resp_packet.encode('hex')}) data = sock.recv(1024) else: logger.debug( 'Received unknown COTP TPDU after handshake: {0}'.format(cotp_base_packet.tpdu_type)) else: logger.debug('Received unknown COTP TPDU before handshake: {0}'.format(cotp_base_packet.tpdu_type)) except socket.timeout: logger.debug('Socket timeout, remote: {0}. ({1})'.format(address[0], session.id))
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session( "modbus", address[0], address[1], sock.getsockname()[0], sock.getsockname()[1], ) self.start_time = time.time() logger.info("New Modbus connection from %s:%s. (%s)", address[0], address[1], session.id) session.add_event({"type": "NEW_CONNECTION"}) try: while True: request = None try: request = sock.recv(7) except Exception as e: logger.error( "Exception occurred in ModbusServer.handle() " "at sock.recv(): %s", str(e), ) if not request: logger.info("Modbus client disconnected. (%s)", session.id) session.add_event({"type": "CONNECTION_LOST"}) break if request.strip().lower() == "quit.": logger.info("Modbus client quit. (%s)", session.id) session.add_event({"type": "CONNECTION_QUIT"}) break if len(request) < 7: logger.info( "Modbus client provided data {} but invalid.".format( session.id)) session.add_event({"type": "CONNECTION_TERMINATED"}) break _, _, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): try: new_byte = sock.recv(1) request += new_byte except Exception: break query = modbus_tcp.TcpQuery() # logdata is a dictionary containing request, slave_id, # function_code and response response, logdata = self._databank.handle_request( query, request, self.mode) logdata["request"] = codecs.encode(request, "hex") session.add_event(logdata) logger.info("Modbus traffic from %s: %s (%s)", address[0], logdata, session.id) if response: sock.sendall(response) logger.info("Modbus response sent to %s", address[0]) else: # TODO: # response could be None under several different cases # MB serial connection addressing UID=0 if (self.mode == "serial") and (logdata["slave_id"] == 0): # delay is in milliseconds time.sleep(self.delay / 1000) logger.debug( "Modbus server's turnaround delay expired.") logger.info( "Modbus connection terminated with client %s.", address[0]) session.add_event({"type": "CONNECTION_TERMINATED"}) sock.shutdown(socket.SHUT_RDWR) sock.close() break # Invalid addressing else: logger.info( "Modbus client ignored due to invalid addressing." " (%s)", session.id, ) session.add_event({"type": "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"})
def handle(self, sock, address): session = conpot_core.get_session( "kamstrup_protocol", address[0], address[1], sock.getsockname()[0], sock.getsockname()[1], ) logger.info( "New Kamstrup connection from %s:%s. (%s)", address[0], address[1], session.id, ) session.add_event({"type": "NEW_CONNECTION"}) self.server_active = True parser = request_parser.KamstrupRequestParser() try: while self.server_active: raw_request = sock.recv(1024) if not raw_request: logger.info("Kamstrup client disconnected. (%s)", session.id) session.add_event({"type": "CONNECTION_LOST"}) break for x in raw_request: parser.add_byte(chr_py3(x)) while True: request = parser.get_request() if not request: session.add_event({"type": "CONNECTION_LOST"}) break else: logdata = { "request": binascii.hexlify(bytearray(request.message_bytes)) } response = self.command_responder.respond(request) # real Kamstrup meters has delay in this interval gevent.sleep(random.uniform(0.24, 0.34)) if response: serialized_response = response.serialize() logdata["response"] = binascii.hexlify( serialized_response) logger.info( "Kamstrup traffic from %s: %s (%s)", address[0], logdata, session.id, ) sock.send(serialized_response) session.add_event(logdata) else: session.add_event(logdata) break except socket.timeout: logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id) session.add_event({"type": "CONNECTION_LOST"}) sock.close()
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session("s7comm", address[0], address[1]) self.start_time = time.time() logger.info("New connection from {0}:{1}. ({2})".format(address[0], address[1], session.id)) session.add_event({"type": "NEW_CONNECTION"}) try: while True: data = sock.recv(4, socket.MSG_WAITALL) if len(data) == 0: session.add_event({"type": "CONNECTION_LOST"}) break _, _, length = unpack("!BBH", data[:4]) data += sock.recv(length - 4, socket.MSG_WAITALL) tpkt_packet = TPKT().parse(data) cotp_base_packet = COTP_BASE_packet().parse(tpkt_packet.payload) if cotp_base_packet.tpdu_type == 0xE0: # connection request cotp_cr_request = COTP_ConnectionRequest().dissect(cotp_base_packet.payload) logger.debug( "Received COTP Connection Request: dst-ref:{0} src-ref:{1} dst-tsap:{2} src-tsap:{3} " "tpdu-size:{4}. ({5})".format( cotp_cr_request.dst_ref, cotp_cr_request.src_ref, cotp_cr_request.dst_tsap, cotp_cr_request.src_tsap, cotp_cr_request.tpdu_size, session.id, ) ) # confirm connection response cotp_cc_response = COTP_ConnectionConfirm( cotp_cr_request.src_ref, cotp_cr_request.dst_ref, 0, cotp_cr_request.src_tsap, cotp_cr_request.dst_tsap, 0x0A, ).assemble() # encapsulate and transmit cotp_resp_base_packet = COTP_BASE_packet(0xD0, 0, cotp_cc_response).pack() tpkt_resp_packet = TPKT(3, cotp_resp_base_packet).pack() sock.send(tpkt_resp_packet) session.add_event({"request": data.encode("hex"), "response": tpkt_resp_packet.encode("hex")}) data = sock.recv(1024) # another round of parsing payloads tpkt_packet = TPKT().parse(data) cotp_base_packet = COTP_BASE_packet().parse(tpkt_packet.payload) if cotp_base_packet.tpdu_type == 0xF0: logger.debug( "Received known COTP TPDU: {0}. ({1})".format(cotp_base_packet.tpdu_type, session.id) ) # will throw exception if the packet does not contain the S7 magic number (0x32) S7_packet = S7().parse(cotp_base_packet.trailer) logger.debug( "Received S7 packet: magic:%s pdu_type:%s reserved:%s req_id:%s param_len:%s " "data_len:%s result_inf:%s session_id:%s", S7_packet.magic, S7_packet.pdu_type, S7_packet.reserved, S7_packet.request_id, S7_packet.param_length, S7_packet.data_length, S7_packet.result_info, session.id, ) # request pdu if S7_packet.pdu_type == 1: # 0xf0 == Request for connect / pdu negotiate if S7_packet.param == 0xF0: # create S7 response packet s7_resp_negotiate_packet = S7( 3, 0, S7_packet.request_id, 0, S7_packet.parameters ).pack() # wrap s7 the packet in cotp cotp_resp_negotiate_packet = COTP_BASE_packet( 0xF0, 0x80, s7_resp_negotiate_packet ).pack() # wrap the cotp packet tpkt_resp_packet = TPKT(3, cotp_resp_negotiate_packet).pack() sock.send(tpkt_resp_packet) session.add_event( {"request": data.encode("hex"), "response": tpkt_resp_packet.encode("hex")} ) # handshake done, give some more data. data = sock.recv(1024) while data: tpkt_packet = TPKT().parse(data) cotp_base_packet = COTP_BASE_packet().parse(tpkt_packet.payload) if cotp_base_packet.tpdu_type == 0xF0: S7_packet = S7().parse(cotp_base_packet.trailer) logger.debug( "Received S7 packet: magic:%s pdu_type:%s reserved:%s " "req_id:%s param_len:%s data_len:%s result_inf:%s session_id:%s", S7_packet.magic, S7_packet.pdu_type, S7_packet.reserved, S7_packet.request_id, S7_packet.param_length, S7_packet.data_length, S7_packet.result_info, session.id, ) response_param, response_data = S7_packet.handle() s7_resp_ssl_packet = S7( 7, 0, S7_packet.request_id, 0, response_param, response_data ).pack() cotp_resp_ssl_packet = COTP_BASE_packet(0xF0, 0x80, s7_resp_ssl_packet).pack() tpkt_resp_packet = TPKT(3, cotp_resp_ssl_packet).pack() sock.send(tpkt_resp_packet) session.add_event( {"request": data.encode("hex"), "response": tpkt_resp_packet.encode("hex")} ) data = sock.recv(1024) else: logger.debug( "Received unknown COTP TPDU after handshake: {0}".format(cotp_base_packet.tpdu_type) ) session.add_event( { "error": "Received unknown COTP TPDU after handshake: {0}".format( cotp_base_packet.tpdu_type ) } ) else: logger.debug("Received unknown COTP TPDU before handshake: {0}".format(cotp_base_packet.tpdu_type)) session.add_event( {"error": "Received unknown COTP TPDU before handshake: {0}".format(cotp_base_packet.tpdu_type)} ) except socket.timeout: session.add_event({"type": "CONNECTION_LOST"}) logger.debug("Socket timeout, remote: {0}. ({1})".format(address[0], session.id))
def handle(self, sock, address): session = conpot_core.get_session( self.proxy_id, address[0], address[1], sock.getsockname()[0], sock.getsockname()[1], ) logger.info( "New connection from %s:%s on %s proxy. (%s)", address[0], address[1], self.proxy_id, session.id, ) proxy_socket = socket() if self.keyfile and self.certfile: proxy_socket = wrap_socket( proxy_socket, keyfile=self.keyfile, certfile=self.certfile ) try: proxy_socket.connect((self.proxy_host, self.proxy_port)) except _socket.error: logger.exception( "Error while connecting to proxied service at ({}, {})".format( self.proxy_host, self.proxy_port ) ) self._close([proxy_socket, sock]) return sockets = [proxy_socket, sock] while len(sockets) == 2: gevent.sleep(0) sockets_read, _, sockets_err = select.select(sockets, [], sockets, 10) if len(sockets_err) > 0: self._close([proxy_socket, sock]) break for s in sockets_read: socket_close_reason = "socket closed" try: data = s.recv(1024) except _socket.error as socket_err: data = [] socket_close_reason = str(socket_err) if len(data) is 0: self._close([proxy_socket, sock]) if s is proxy_socket: logging.warning( "Closing proxied socket while receiving (%s, %s): %s.", self.proxy_host, self.proxy_port, socket_close_reason, ) sockets = [] break elif s is sock: logging.warning( "Closing connection to remote while receiving from remote (%s, %s): %s", socket_close_reason, address[0], address[1], ) sockets = [] break else: assert False try: if s is proxy_socket: self.handle_out_data(data, sock, session) elif s is sock: self.handle_in_data(data, proxy_socket, session) else: assert False except _socket.error as socket_err: if s is proxy_socket: destination = "proxied socket" else: destination = "remote connection" logger.warning( "Error while sending data to %s: %s.", destination, str(socket_err), ) sockets = [] break session.set_ended() proxy_socket.close() sock.close()
def _new_session_event(remote_ip, data): session = conpot_core.get_session('snap7', remote_ip, None) session.add_event(data) return session.id
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session( "IEC104", address[0], address[1], sock.getsockname()[0], sock.getsockname()[1], ) logger.info( "New IEC 104 connection from %s:%s. (%s)", address[0], address[1], session.id, ) session.add_event({"type": "NEW_CONNECTION"}) iec104_handler = IEC104(self.device_data_controller, sock, address, session.id) try: while True: timeout_t3 = gevent.Timeout( conpot_core.get_databus().get_value("T_3"), Timeout_t3) timeout_t3.start() try: try: request = sock.recv(6) if not request: logger.info("IEC104 Station disconnected. (%s)", session.id) session.add_event({"type": "CONNECTION_LOST"}) iec104_handler.disconnect() break while request and len(request) < 2: new_byte = sock.recv(1) request += new_byte _, length = struct.unpack(">BB", request[:2]) while len(request) < (length + 2): new_byte = sock.recv(1) if not new_byte: break request += new_byte # check if IEC 104 packet or for the first occurrence of the indication 0x68 for IEC 104 for elem in list(request): if 0x68 == elem: index = request.index(elem) iec_request = request[index:] timeout_t3.cancel() response = None # check which frame type if not (iec_request[2] & 0x01): # i_frame response = iec104_handler.handle_i_frame( iec_request) elif iec_request[2] & 0x01 and not ( iec_request[2] & 0x02): # s_frame iec104_handler.handle_s_frame(iec_request) elif iec_request[2] & 0x03: # u_frame response = iec104_handler.handle_u_frame( iec_request) else: logger.warning( "%s ---> No valid IEC104 type (%s)", address, session.id, ) if response: for resp_packet in response: if resp_packet: sock.send(resp_packet) break except Timeout_t3: pkt = iec104_handler.send_104frame(TESTFR_act) if pkt: sock.send(pkt) finally: timeout_t3.cancel() 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"}) iec104_handler.disconnect() break except socket.timeout: logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id) session.add_event({"type": "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"}) else: # determine and handle different error pass else: print(("socket error ", err)) iec104_handler.disconnect()
def handle(self, sock, addr): session = conpot_core.get_session('guardian_ast', addr[0], addr[1], sock.getsockname()[0], sock.getsockname()[1]) logger.info('New GuardianAST connection from %s:%d. (%s)', addr[0], addr[1], session.id) session.add_event({'type': '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) # Default Product names, change based off country needs product1 = self.databus.get_value('product1').ljust(22) product1 = self.databus.get_value('product1').ljust(22) product2 = self.databus.get_value('product2').ljust(22) product3 = self.databus.get_value('product3').ljust(22) product4 = self.databus.get_value('product4').ljust(22) # Create random Numbers for the volumes # # this will crate an initial Volume and then the second value based # off the orig value. vol1 = self.databus.get_value('vol1') vol1tc = random.randint(vol1, vol1 + 200) vol2 = self.databus.get_value('vol2') vol2tc = random.randint(vol2, vol2 + 200) vol3 = self.databus.get_value('vol3') vol3tc = random.randint(vol3, vol3 + 200) vol4 = self.databus.get_value('vol4') vol4tc = random.randint(vol4, vol4 + 200) # unfilled space ULLAGE ullage1 = str(self.databus.get_value('ullage1')) ullage2 = str(self.databus.get_value('ullage2')) ullage3 = str(self.databus.get_value('ullage3')) ullage4 = str(self.databus.get_value('ullage3')) # Height of tank height1 = str(self.databus.get_value('height1')).ljust(5, '0') height2 = str(self.databus.get_value('height2')).ljust(5, '0') height3 = str(self.databus.get_value('height3')).ljust(5, '0') height4 = str(self.databus.get_value('height4')).ljust(5, '0') # Water in tank, this is a variable that needs to be low h2o1 = str(self.databus.get_value('h2o1')).ljust(4, '0') h2o2 = str(self.databus.get_value('h2o2')).ljust(4, '0') h2o3 = str(self.databus.get_value('h2o3')).ljust(4, '0') h2o4 = str(self.databus.get_value('h2o4')).ljust(4, '0') # Temperature of the tank, this will need to be between 50 - 60 temp1 = str(self.databus.get_value('temp1')).ljust(5, '0') temp2 = str(self.databus.get_value('temp2')).ljust(5, '0') temp3 = str(self.databus.get_value('temp3')).ljust(5, '0') temp4 = str(self.databus.get_value('temp4')).ljust(5, '0') station = self.databus.get_value('station_name') # This function is to set-up up the message to be sent upon a successful I20100 command being sent # The final message is sent with a current date/time stamp inside of the main loop. def I20100(): ret = '\nI20100\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nIN-TANK INVENTORY\n\n' ret += 'TANK PRODUCT VOLUME TC VOLUME ULLAGE HEIGHT WATER TEMP' ret += '\n 1 ' + product1 + str(vol1) + ' ' + str( vol1tc ) + ' ' + ullage1 + ' ' + height1 + ' ' + h2o1 + ' ' + temp1 ret += '\n 2 ' + product2 + str(vol2) + ' ' + str( vol2tc ) + ' ' + ullage2 + ' ' + height2 + ' ' + h2o2 + ' ' + temp2 ret += '\n 3 ' + product3 + str(vol3) + ' ' + str( vol3tc ) + ' ' + ullage3 + ' ' + height3 + ' ' + h2o3 + ' ' + temp3 ret += '\n 4 ' + product4 + str(vol4) + ' ' + str( vol4tc ) + ' ' + ullage4 + ' ' + height4 + ' ' + h2o4 + ' ' + temp4 ret += '\n' return ret ########################################################################### # # Only one Tank is listed currently in the I20200 command # ########################################################################### def I20200(): ret = '\nI20200\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nDELIVERY REPORT\n\n' ret += 'T 1:' + product1 + '\nINCREASE DATE / TIME GALLONS TC GALLONS WATER TEMP DEG F HEIGHT\n\n' ret += ' END: ' + str( fill_stop.strftime('%m/%d/%Y %H:%M') ) + ' ' + str(vol1 + 300) + ' ' + str( vol1tc + 300 ) + ' ' + h2o1 + ' ' + temp1 + ' ' + height1 + '\n' ret += ' START: ' + str( fill_start.strftime('%m/%d/%Y %H:%M') ) + ' ' + str(vol1 - 300) + ' ' + str( vol1tc - 300) + ' ' + h2o1 + ' ' + temp1 + ' ' + str( float(height1) - 23) + '\n' ret += ' AMOUNT: ' + str( vol1) + ' ' + str(vol1tc) + '\n\n' return ret ########################################################################### # # I20300 In-Tank Leak Detect Report # ########################################################################### def I20300(): ret = '\nI20300\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n' ret += 'TANK 1 ' + product1 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 2 ' + product2 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 3 ' + product3 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 4 ' + product4 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' return ret ########################################################################### # Shift report command I20400 only one item in report at this time, # but can always add more if needed ########################################################################### def I20400(): ret = '\nI20400\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nSHIFT REPORT\n\n' ret += 'SHIFT 1 TIME: 12:00 AM\n\nTANK PRODUCT\n\n' ret += ' 1 ' + product1 + ' VOLUME TC VOLUME ULLAGE HEIGHT WATER TEMP\n' ret += 'SHIFT 1 STARTING VALUES ' + str(vol1) + ' ' + str( vol1tc ) + ' ' + ullage1 + ' ' + height1 + ' ' + h2o1 + ' ' + temp1 + '\n' ret += ' ENDING VALUES ' + str( vol1 + 940) + ' ' + str(vol1tc + 886) + ' ' + str( int(ullage1) + 345) + ' ' + str(float( height1) + 53) + ' ' + h2o1 + ' ' + temp1 + '\n' ret += ' DELIVERY VALUE 0\n' ret += ' TOTALS 940\n\n' return ret ########################################################################### # I20500 In-Tank Status Report ########################################################################### def I20500(): ret = '\nI20500\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n\n' + station + '\n\n\n' ret += 'TANK PRODUCT STATUS\n\n' ret += ' 1 ' + product1 + ' NORMAL\n\n' ret += ' 2 ' + product2 + ' HIGH WATER ALARM\n' ret += ' HIGH WATER WARNING\n\n' ret += ' 3 ' + product3 + ' NORMAL\n\n' ret += ' 4 ' + product4 + ' NORMAL\n\n' return ret while True: try: # Get the initial data request = sock.recv(4096) # The connection has been closed if not request: break while not (b'\n' in request or b'00' in request): request += sock.recv(4096) # if first value is not ^A then do nothing # thanks John(achillean) for the help if request[:1] != b'\x01': logger.info('Non ^A command attempt %s:%d. (%s)', addr[0], addr[1], session.id) break # if request is less than 6, than do nothing if len(request) < 6: logger.info('Invalid command attempt %s:%d. (%s)', addr[0], addr[1], session.id) break cmds = { "I20100": I20100, "I20200": I20200, "I20300": I20300, "I20400": I20400, "I20500": I20500 } cmd = request[1:7].decode() # strip ^A and \n out response = None if cmd in cmds: logger.info('%s command attempt %s:%d. (%s)', cmd, addr[0], addr[1], session.id) response = cmds[cmd]() elif cmd.startswith("S6020"): # change the tank name if cmd.startswith("S60201"): # split string into two, the command, and the data TEMP = request.split(b'S60201') # if length is less than two, print error if len(TEMP) < 2: response = AST_ERROR # Else the command was entered correctly and continue else: # Strip off the carrage returns and new lines TEMP1 = TEMP[1].rstrip(b'\r\n').decode() # if Length is less than 22 if len(TEMP1) < 22: # pad the result to have 22 chars product1 = TEMP1.ljust(22) elif len(TEMP1) > 22: # else only print 22 chars if the result was longer product1 = TEMP1[:20] + " " else: # else it fits fine (22 chars) product1 = TEMP1 logger.info('S60201: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60202"): TEMP = request.split(b'S60202') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product2 = TEMP1.ljust(22) elif len(TEMP1) > 22: product2 = TEMP1[:20] + " " else: product2 = TEMP1 logger.info('S60202: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60203"): TEMP = request.split(b'S60203') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product3 = TEMP1.ljust(22) elif len(TEMP1) > 22: product3 = TEMP1[:20] + " " else: product3 = TEMP1 logger.info('S60203: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60204"): TEMP = request.split(b'S60204') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product4 = TEMP1.ljust(22) elif len(TEMP1) > 22: product4 = TEMP1[:20] + " " else: product4 = TEMP1 logger.info('S60204: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60200"): TEMP = request.split(b'S60200') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product1 = TEMP1.ljust(22) product2 = TEMP1.ljust(22) product3 = TEMP1.ljust(22) product4 = TEMP1.ljust(22) elif len(TEMP1) > 22: product1 = TEMP1[:20] + " " product2 = TEMP1[:20] + " " product3 = TEMP1[:20] + " " product4 = TEMP1[:20] + " " else: product1 = TEMP1 product2 = TEMP1 product3 = TEMP1 product4 = TEMP1 logger.info('S60200: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) else: response = AST_ERROR else: response = AST_ERROR # log what was entered logger.info('%s command attempt %s:%d. (%s)', request, addr[0], addr[1], session.id) if response: sock.send(str_to_bytes(response)) session.add_event({ "type": "AST {0}".format(cmd), "request": request, "response": response }) except Exception as e: logger.exception(('Unknown Error: {}'.format(str(e)))) logger.info('GuardianAST client disconnected %s:%d. (%s)', addr[0], addr[1], session.id) session.add_event({'type': 'CONNECTION_LOST'})
def handle(self, sock, address): session = conpot_core.get_session(self.proxy_id, address[0], address[1]) logger.info('New connection from {0}:{1} on {2} proxy. ({3})'.format(address[0], address[1], self.proxy_id, session.id)) proxy_socket = socket() if self.keyfile and self.certfile: proxy_socket = wrap_socket(proxy_socket, self.keyfile, self.certfile) try: proxy_socket.connect((self.proxy_host, self.proxy_port)) except _socket.error as ex: logger.error('Error while connecting to proxied service at ({0}, {1}): {2}' .format(self.proxy_host, self.proxy_port, ex)) self._close([proxy_socket, sock]) return sockets = [proxy_socket, sock] while len(sockets) == 2: gevent.sleep() sockets_read, _, sockets_err = select.select(sockets, [], sockets, 10) if len(sockets_err) > 0: self._close([proxy_socket, sock]) break for s in sockets_read: socket_close_reason = 'socket closed' try: data = s.recv(1024) except _socket.error as socket_err: data = [] socket_close_reason = str(socket_err) if len(data) is 0: self._close([proxy_socket, sock]) if s is proxy_socket: logging.warning('Closing proxied socket while receiving ({0}, {1}): {2}.' .format(self.proxy_host, self.proxy_port, socket_close_reason)) sockets = [] break elif s is sock: logging.warning('Closing connection to remote while receiving from remote ({0}, {1}): {2}' .format(socket_close_reason, address[0], address[1])) sockets = [] break else: assert False try: if s is proxy_socket: self.handle_out_data(data, sock, session) elif s is sock: self.handle_in_data(data, proxy_socket, session) else: assert False except _socket.error as socket_err: if s is proxy_socket: destination = 'proxied socket' else: destination = 'remote connection' logger.warning('Error while sending data to {0}: {1}.'.format(destination, str(socket_err))) sockets = [] break session.set_ended() proxy_socket.close() sock.close()
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session('s7comm', address[0], address[1], sock.getsockname()[0], sock.getsockname()[1]) self.start_time = time.time() logger.info('New S7 connection from {0}:{1}. ({2})'.format( address[0], address[1], session.id)) session.add_event({'type': 'NEW_CONNECTION'}) try: while True: data = sock.recv(4, socket.MSG_WAITALL) if len(data) == 0: session.add_event({'type': 'CONNECTION_LOST'}) break _, _, length = unpack('!BBH', data[:4]) # check for length if length <= 4: logger.info('S7 error: Invalid length') session.add_event({'error': 'S7 error: Invalid length'}) break data += sock.recv(length - 4, socket.MSG_WAITALL) try: tpkt_packet = TPKT().parse(cleanse_byte_string(data)) cotp_base_packet = COTP_BASE_packet().parse( tpkt_packet.payload) except Exception as e: break if cotp_base_packet.tpdu_type == 0xe0: # connection request cotp_cr_request = COTP_ConnectionRequest().dissect( cotp_base_packet.payload) logger.info( 'Received COTP Connection Request: dst-ref:{0} src-ref:{1} dst-tsap:{2} src-tsap:{3} ' 'tpdu-size:{4}. ({5})'.format( cotp_cr_request.dst_ref, cotp_cr_request.src_ref, cotp_cr_request.dst_tsap, cotp_cr_request.src_tsap, cotp_cr_request.tpdu_size, session.id)) # confirm connection response cotp_cc_response = COTP_ConnectionConfirm( cotp_cr_request.src_ref, cotp_cr_request.dst_ref, 0, cotp_cr_request.src_tsap, cotp_cr_request.dst_tsap, 0x0a).assemble() # encapsulate and transmit cotp_resp_base_packet = COTP_BASE_packet( 0xd0, 0, cotp_cc_response).pack() tpkt_resp_packet = TPKT(3, cotp_resp_base_packet).pack() sock.send(tpkt_resp_packet) session.add_event({ 'request': codecs.encode(data, 'hex'), 'response': codecs.encode(tpkt_resp_packet, 'hex') }) data = sock.recv(1024) # another round of parsing payloads tpkt_packet = TPKT().parse(data) cotp_base_packet = COTP_BASE_packet().parse( tpkt_packet.payload) if cotp_base_packet.tpdu_type == 0xf0: logger.info( 'Received known COTP TPDU: {0}. ({1})'.format( cotp_base_packet.tpdu_type, session.id)) # will throw exception if the packet does not contain the S7 magic number (0x32) S7_packet = S7().parse(cotp_base_packet.trailer) logger.info( 'Received S7 packet: magic:%s pdu_type:%s reserved:%s req_id:%s param_len:%s ' 'data_len:%s result_inf:%s session_id:%s', S7_packet.magic, S7_packet.pdu_type, S7_packet.reserved, S7_packet.request_id, S7_packet.param_length, S7_packet.data_length, S7_packet.result_info, session.id) # request pdu if S7_packet.pdu_type == 1: # 0xf0 == Request for connect / pdu negotiate if S7_packet.param == 0xf0: # create S7 response packet s7_resp_negotiate_packet = S7( 3, 0, S7_packet.request_id, 0, S7_packet.parameters).pack() # wrap s7 the packet in cotp cotp_resp_negotiate_packet = COTP_BASE_packet( 0xf0, 0x80, s7_resp_negotiate_packet).pack() # wrap the cotp packet tpkt_resp_packet = TPKT( 3, cotp_resp_negotiate_packet).pack() sock.send(tpkt_resp_packet) session.add_event({ 'request': codecs.encode(data, 'hex'), 'response': codecs.encode(tpkt_resp_packet, 'hex') }) # handshake done, give some more data. data = sock.recv(1024) while data: tpkt_packet = TPKT().parse(data) cotp_base_packet = COTP_BASE_packet( ).parse(tpkt_packet.payload) if cotp_base_packet.tpdu_type == 0xf0: S7_packet = S7().parse( cotp_base_packet.trailer) logger.info( 'Received S7 packet: magic:%s pdu_type:%s reserved:%s ' 'req_id:%s param_len:%s data_len:%s result_inf:%s session_id:%s', S7_packet.magic, S7_packet.pdu_type, S7_packet.reserved, S7_packet.request_id, S7_packet.param_length, S7_packet.data_length, S7_packet.result_info, session.id) response_param, response_data = S7_packet.handle( ) s7_resp_ssl_packet = S7( 7, 0, S7_packet.request_id, 0, response_param, response_data).pack() cotp_resp_ssl_packet = COTP_BASE_packet( 0xf0, 0x80, s7_resp_ssl_packet).pack() tpkt_resp_packet = TPKT( 3, cotp_resp_ssl_packet).pack() sock.send(tpkt_resp_packet) session.add_event({ 'request': codecs.encode(data, 'hex'), 'response': codecs.encode( tpkt_resp_packet, 'hex') }) data = sock.recv(1024) else: logger.info( 'Received unknown COTP TPDU after handshake: {0}'. format(cotp_base_packet.tpdu_type)) session.add_event({ 'error': 'Received unknown COTP TPDU after handshake: {0}'. format(cotp_base_packet.tpdu_type) }) else: logger.info( 'Received unknown COTP TPDU before handshake: {0}'. format(cotp_base_packet.tpdu_type)) session.add_event({ 'error': 'Received unknown COTP TPDU before handshake: {0}'. format(cotp_base_packet.tpdu_type) }) except socket.timeout: session.add_event({'type': 'CONNECTION_LOST'}) logger.debug('Socket timeout, remote: {0}. ({1})'.format( address[0], session.id)) except socket.error: session.add_event({'type': 'CONNECTION_LOST'}) logger.debug('Connection reset by peer, remote: {0}. ({1})'.format( address[0], session.id)) except Exception as e: logger.exception('Exception caught {0}, remote: {1}. ({2})'.format( e, address[0], session.id))
def handle(self, sock, address): session = conpot_core.get_session(self.proxy_id, address[0], address[1], sock.getsockname()[0], sock.getsockname()[1]) logger.info( 'New connection from %s:%s on %s proxy. (%s)', address[0], address[1], self.proxy_id, session.id) proxy_socket = socket() if self.keyfile and self.certfile: proxy_socket = wrap_socket(proxy_socket, self.keyfile, self.certfile) try: proxy_socket.connect((self.proxy_host, self.proxy_port)) except _socket.error: logger.exception('Error while connecting to proxied service at ({}, {})'.format(self.proxy_host, self.proxy_port)) self._close([proxy_socket, sock]) return sockets = [proxy_socket, sock] while len(sockets) == 2: if Proxy.stop_servers: self._close([proxy_socket, sock]) break gevent.sleep(0) ProxyAddresses.put(proxy_socket, [address[0], address[1]]) sockets_read, _, sockets_err = select.select(sockets, [], sockets, 10) if len(sockets_err) > 0: self._close([proxy_socket, sock]) break for s in sockets_read: socket_close_reason = 'socket closed' try: data = s.recv(1024) except _socket.error as socket_err: data = [] socket_close_reason = str(socket_err) if len(data) is 0: ProxyAddresses.clear_unused(proxy_socket) self._close([proxy_socket, sock]) if s is proxy_socket: logging.warning( 'Closing proxied socket while receiving (%s, %s): %s.', self.proxy_host, self.proxy_port, socket_close_reason) sockets = [] break elif s is sock: logging.warning( 'Closing connection to remote while receiving from remote (%s, %s): %s', socket_close_reason, address[0], address[1]) sockets = [] break else: assert False try: if s is proxy_socket: self.handle_out_data(data, sock, session) elif s is sock: self.handle_in_data(data, proxy_socket, session) else: assert False except _socket.error as socket_err: if s is proxy_socket: destination = 'proxied socket' else: destination = 'remote connection' logger.warning('Error while sending data to %s: %s.', destination, str(socket_err)) sockets = [] break session.set_ended() proxy_socket.close() sock.close()
def handle(self, sock, address): session = conpot_core.get_session(self.proxy_id, address[0], address[1]) logger.info('New connection from {0}:{1} on {2} proxy. ({3})'.format( address[0], address[1], self.proxy_id, session.id)) proxy_socket = socket() if self.keyfile and self.certfile: proxy_socket = wrap_socket(proxy_socket, self.keyfile, self.certfile) try: proxy_socket.connect((self.proxy_host, self.proxy_port)) except _socket.error as ex: logger.error( 'Error while connecting to proxied service at ({0}, {1}): {2}'. format(self.proxy_host, self.proxy_port, ex)) self._close([proxy_socket, sock]) return sockets = [proxy_socket, sock] while len(sockets) == 2: gevent.sleep() sockets_read, _, sockets_err = select.select( sockets, [], sockets, 10) if len(sockets_err) > 0: self._close([proxy_socket, sock]) break for s in sockets_read: socket_close_reason = 'socket closed' try: data = s.recv(1024) except _socket.error as socket_err: data = [] socket_close_reason = str(socket_err) if len(data) is 0: self._close([proxy_socket, sock]) if s is proxy_socket: logging.warning( 'Closing proxied socket while receiving ({0}, {1}): {2}.' .format(self.proxy_host, self.proxy_port, socket_close_reason)) sockets = [] break elif s is sock: logging.warning( 'Closing connection to remote while receiving from remote ({0}, {1}): {2}' .format(socket_close_reason, address[0], address[1])) sockets = [] break else: assert False try: if s is proxy_socket: self.handle_out_data(data, sock, session) elif s is sock: self.handle_in_data(data, proxy_socket, session) else: assert False except _socket.error as socket_err: if s is proxy_socket: destination = 'proxied socket' else: destination = 'remote connection' logger.warning( 'Error while sending data to {0}: {1}.'.format( destination, str(socket_err))) sockets = [] break session.set_ended() proxy_socket.close() sock.close()
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session('IEC104',address[0], address[1], sock.getsockname()[0], sock.getsockname()[1]) logger.info('New IEC 104 connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) iec104_handler = IEC104(self.device_data_controller, sock, address, session.id) try: while True: timeout_t3 = gevent.Timeout(conpot_core.get_databus().get_value('T_3'), Timeout_t3) timeout_t3.start() try: try: request = sock.recv(6) if not request: logger.info('IEC104 Station disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) iec104_handler.disconnect() break while request and len(request) < 2: new_byte = sock.recv(1) request += new_byte protocol_identifier, length = struct.unpack(">BB", request[:2]) while len(request) < (length + 2): new_byte = sock.recv(1) if not new_byte: break request += new_byte # check if IEC 104 packet or for the first occurrence of the indication 0x68 for IEC 104 for elem in list(request): if 0x68 == elem: index = request.index(elem) iec_request = request[index:] timeout_t3.cancel() response = None # check which frame type if not (iec_request[2] & 0x01): # i_frame response = iec104_handler.handle_i_frame(iec_request) elif iec_request[2] & 0x01 and not(iec_request[2] & 0x02): # s_frame response = iec104_handler.handle_s_frame(iec_request) elif iec_request[2] & 0x03: # u_frame response = iec104_handler.handle_u_frame(iec_request) else: logger.warning("%s ---> No valid IEC104 type (%s)", address, session.id) if response: for resp_packet in response: if resp_packet: sock.send(resp_packet) # response_string = (" ".join(hex(n) for n in resp_packet)) break except Timeout_t3: pkt = iec104_handler.send_104frame(TESTFR_act) if pkt: sock.send(pkt) finally: timeout_t3.cancel() 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'}) iec104_handler.disconnect() break except socket.timeout: logger.debug('Socket timeout, remote: %s. (%s)', address[0], session.id) session.add_event({'type': 'CONNECTION_LOST'}) except socket.error as err: if isinstance(err.args, tuple): if err[0] == errno.EPIPE: # remote peer disconnected logger.info('IEC104 Station disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) else: # determine and handle different error pass else: print(("socket error ", err)) iec104_handler.disconnect()
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session('modbus', address[0], address[1], sock.getsockname()[0], sock.getsockname()[1]) self.start_time = time.time() logger.info( 'New Modbus connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) try: while True: request = None try: request = sock.recv(7) except Exception as e: logger.error('Exception occurred in ModbusServer.handle() ' 'at sock.recv(): %s', str(e)) if not request: logger.info('Modbus client disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) break if request.strip().lower() == 'quit.': logger.info('Modbus client quit. (%s)', session.id) session.add_event({'type': 'CONNECTION_QUIT'}) break if len(request) < 7: logger.info('Modbus client provided data {} but invalid.'.format(session.id)) session.add_event({'type': 'CONNECTION_TERMINATED'}) break tr_id, pr_id, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): new_byte = sock.recv(1) request += new_byte query = modbus_tcp.TcpQuery() # logdata is a dictionary containing request, slave_id, # function_code and response response, logdata = self._databank.handle_request( query, request, self.mode ) logdata['request'] = codecs.encode(request, 'hex') session.add_event(logdata) logger.info( 'Modbus traffic from %s: %s (%s)', address[0], logdata, session.id) if response: sock.sendall(response) logger.info('Modbus response sent to %s', address[0]) else: # TODO: # response could be None under several different cases # MB serial connection addressing UID=0 if (self.mode == 'serial') and (logdata['slave_id'] == 0): # delay is in milliseconds time.sleep(self.delay / 1000) logger.debug( 'Modbus server\'s turnaround delay expired.') logger.info('Modbus connection terminated with client %s.', address[0]) session.add_event({'type': 'CONNECTION_TERMINATED'}) sock.shutdown(socket.SHUT_RDWR) sock.close() break # Invalid addressing else: logger.info('Modbus client ignored due to invalid addressing.' ' (%s)', session.id) session.add_event({'type': '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'})