diff --git a/.coveragerc b/.coveragerc index e98c0a7f..ad330a8f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -5,13 +5,10 @@ omit = conpot/core/loggers/hpfriends.py conpot/core/loggers/log_worker.py conpot/core/loggers/syslog.py - conpot/core/loggers/mysql_log.py conpot/core/loggers/sqlite_log.py conpot/core/loggers/stix_transform.py conpot/core/loggers/taxii_log.py conpot/utils/mac_addr.py - # New features.. Just to avoid coverage drop - before we actually write some tests - conpot/core/auth.py .tox/* @@ -19,7 +16,6 @@ exclude_lines = pragma: no cover raise AssertionError raise NotImplementedError - if 0: if __name__ == .__main__.: ignore_errors = True diff --git a/.landscape.yaml b/.landscape.yaml deleted file mode 100644 index 2709474e..00000000 --- a/.landscape.yaml +++ /dev/null @@ -1,2 +0,0 @@ -strictness: medium -autodetect: yes diff --git a/.travis.yml b/.travis.yml index 26218e58..6d6c3114 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,8 +13,6 @@ before_install: - echo $REPO - echo $TRAVIS_TAG - pwd -services: - - mysql addons: apt: packages: @@ -26,7 +24,6 @@ install: - pip install tox - pip install -r requirements.txt before_script: - - mysql -e 'CREATE DATABASE IF NOT EXISTS conpot_unittest;' - chmod +x "$REPO/bin/conpot" script: - tox diff --git a/Dockerfile b/Dockerfile index 16f57a14..58afcec2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,6 @@ FROM python:3.8 AS conpot-builder RUN apt-get update && apt-get install -y \ - libmariadb-dev \ gcc \ && rm -rf /var/lib/apt/lists/* @@ -19,16 +18,10 @@ RUN pip3 install --user --no-cache-dir /opt/conpot # Run container FROM python:3.8-slim -RUN apt-get update && apt-get install -y \ - wget \ - && rm -rf /var/lib/apt/lists/* - RUN adduser --disabled-password --gecos "" conpot WORKDIR /home/conpot COPY --from=conpot-builder --chown=conpot:conpot /home/conpot/.local/ /home/conpot/.local/ -RUN mkdir -p /etc/conpot /var/log/conpot /usr/share/wireshark \ - && wget https://github.com/wireshark/wireshark/raw/master/manuf -o /usr/share/wireshark/manuf # Create directories RUN mkdir -p /var/log/conpot/ \ diff --git a/bin/__init__.py b/bin/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/bin/hmi_crawler b/bin/hmi_crawler deleted file mode 100644 index 1ad5a833..00000000 --- a/bin/hmi_crawler +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright (C) 2013 Lukas Rist -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -import os -import sys -import hashlib -import argparse -import socket - -from urllib.parse import urlparse - -import requests -from bs4 import BeautifulSoup - - -class Crawler(object): - def _get_md5(self, data, path): - md5 = hashlib.md5(data).hexdigest() - if md5 in self.known_files.values(): - return None - self.known_files[path] = md5 - return md5 - - def _populate_known_files(self): - for dir_name, _, file_names in os.walk(self.base_dir): - for name in file_names: - if name[-2:] == "py": - continue - path = dir_name + os.path.sep + name - with open(path, "rb") as fh: - data = fh.read() - md5 = self._get_md5(data, path) - if md5: - self.known_files[path] = md5 - - def __init__(self, args): - self.start = args.target - self.base_dir = args.www - self.known_files = dict() - self.known_pages = list() - self._populate_known_files() - - def _store_file(self, path, target="www/static/"): - disc_path = target + path - if not os.path.exists(target): - os.makedirs(target) - retries = 0 - data = None - while retries < 5: - try: - data = requests.get(self.start + "/" + path).content - except socket.error: - retries += 1 - else: - break - if data and self._get_md5(data, disc_path): - with open(disc_path, "wb") as fh: - fh.write(data) - - def _fix_style_paths(self, soup): - for tag in soup.find_all(attrs={"type": "text/css"}): - old_path = tag["href"] - if old_path.startswith("/"): - old_path = old_path[1:] - self._store_file(old_path, self.base_dir + "/static/") - path = "static/" + old_path - tag["href"] = path - return soup - - def _fix_img_paths(self, soup): - for tag in soup.find_all("img"): - old_path = tag["src"] - if old_path.startswith("/"): - old_path = old_path[1:] - self._store_file(old_path, self.base_dir + "/static/img/") - path = "static/img/" + old_path - tag["src"] = path - return soup - - def _fetch_page(self, url): - result = requests.get(url) - path = urlparse(result.url).path - soup = BeautifulSoup(result.text.encode("ascii", "ignore")) - return soup, path - - def _store_page(self, soup, name="/index.html"): - soup = self._fix_style_paths(soup) - soup = self._fix_img_paths(soup) - with open(self.base_dir + name, "wb") as html_fh: - html_fh.write(soup.prettify().encode("utf-8")) - - def _process_page(self, url, page): - soup, _ = self._fetch_page(url) - self.known_pages.append(url) - self._dig(soup) - self._store_page(soup, page) - return soup - - def _dig(self, soup): - for tag in soup.find_all("a"): - if not tag["href"].startswith("/"): - tag["href"] = "/" + tag["href"] - if self.start + tag["href"] in self.known_pages: - continue - url = self.start + tag["href"] - soup = self._process_page(url, tag["href"]) - for tag in soup.find_all("iframe"): - if not tag["src"].startswith("/"): - tag["src"] = "/" + tag["src"] - if self.start + tag["src"] in self.known_pages: - continue - url = self.start + tag["src"] - self._process_page(url, tag["src"]) - - def work(self): - soup, path = self._fetch_page(self.start) - self._store_page(soup, path) - self._dig(soup) - - -if __name__ == "__main__": - print( - """ - - HTTP HMI Crawler - - Please be aware that a crawler might break your web application. - This tool is supposed to be only used against web application owned by you. - - Usage on your own risk! - - """ - ) - parser = argparse.ArgumentParser() - parser.add_argument( - "-w", "--www", help="HMI public www path", default="www", metavar="www" - ) - parser.add_argument( - "-t", - "--target", - help="Target domain or IP address", - default="localhost:80", - metavar="http://target:port", - ) - args = parser.parse_args() - try: - agreement = input( - "Are you sure you want to crawl {0}? (y/N): ".format(args.target) - ) - if not agreement == "y": - print("You have to reply with 'y' to continue... Bye") - sys.exit(0) - if args.www.endswith("/"): - args.www = args.www[:-1] - if not os.path.exists(args.www): - confirmation = input("Create directory {0}? (Y/n): ".format(args.www)) - if not confirmation == "N": - os.makedirs(args.www) - Crawler(args).work() - except KeyboardInterrupt: - print("Bye...") diff --git a/conpot/core/databus.py b/conpot/core/databus.py index 32f42a9a..1f99af96 100644 --- a/conpot/core/databus.py +++ b/conpot/core/databus.py @@ -16,7 +16,6 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import logging -import json import inspect # this is needed because we use it in the xml. @@ -105,13 +104,6 @@ def initialize(self, config_file): raise Exception("Unknown value type: {0}".format(value_type)) self.initialized.set() - def get_shapshot(self): - # takes a snapshot of the internal honeypot state and returns it as json. - snapsnot = {} - for key in list(self._data.keys()): - snapsnot[key] = self.get_value(key) - return json.dumps(snapsnot) - def reset(self): logger.debug("Resetting databus.") diff --git a/conpot/core/filesystem.py b/conpot/core/filesystem.py index 429a5152..68c9b04e 100644 --- a/conpot/core/filesystem.py +++ b/conpot/core/filesystem.py @@ -16,7 +16,6 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import time -import typing import stat import tempfile import logging @@ -28,7 +27,6 @@ from os import F_OK, R_OK, W_OK from typing import Optional, Union, Text, Any, List from fs import open_fs, mirror, errors, subfs, base -from fs.time import datetime_to_epoch from fs.mode import Mode from fs.wrapfs import WrapFS from fs.permissions import Permissions @@ -43,8 +41,6 @@ ) from conpot.core.fs_utils import FSOperationNotPermitted -_F = typing.TypeVar("_F", bound="FS", covariant=True) - logger = logging.getLogger(__name__) diff --git a/conpot/core/fs_utils.py b/conpot/core/fs_utils.py index f0e0de86..9081c60e 100644 --- a/conpot/core/fs_utils.py +++ b/conpot/core/fs_utils.py @@ -20,7 +20,6 @@ """ import fs from typing import Optional, Union -from fs import errors from fs.permissions import Permissions import typing from fs.subfs import SubFS diff --git a/conpot/core/loggers/json_log.py b/conpot/core/loggers/json_log.py index 0d9afc32..581616d7 100644 --- a/conpot/core/loggers/json_log.py +++ b/conpot/core/loggers/json_log.py @@ -50,6 +50,3 @@ def log(self, event): json.dump(data, self.fileHandle, default=json_default) self.fileHandle.write("\n") self.fileHandle.flush() - - def log_session(self, session): - pass diff --git a/conpot/core/loggers/log_worker.py b/conpot/core/loggers/log_worker.py index ceca8923..edc66964 100644 --- a/conpot/core/loggers/log_worker.py +++ b/conpot/core/loggers/log_worker.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (C) 2014 Lukas Rist # # This program is free software; you can redistribute it and/or @@ -18,8 +17,6 @@ import json import logging - -# import uuid import time from datetime import datetime @@ -28,8 +25,6 @@ from gevent.queue import Empty from conpot.core.loggers.sqlite_log import SQLiteLogger - -# from conpot.core.loggers.mysql_log import MySQLlogger from conpot.core.loggers.hpfriends import HPFriendsLogger from conpot.core.loggers.syslog import SysLogger from conpot.core.loggers.taxii_log import TaxiiLogger @@ -45,7 +40,6 @@ def __init__(self, config, dom, session_manager, public_ip): self.log_queue = session_manager.log_queue self.session_manager = session_manager self.sqlite_logger = None - # self.mysql_logger = None self.json_logger = None self.friends_feeder = None self.syslog_client = None @@ -55,17 +49,6 @@ def __init__(self, config, dom, session_manager, public_ip): if config.getboolean("sqlite", "enabled"): self.sqlite_logger = SQLiteLogger() - # if config.getboolean('mysql', 'enabled'): - # host = config.get('mysql', 'host') - # port = config.getint('mysql', 'port') - # db = config.get('mysql', 'db') - # username = config.get('mysql', 'username') - # passphrase = config.get('mysql', 'passphrase') - # logdevice = config.get('mysql', 'device') - # logsocket = config.get('mysql', 'socket') - # sensorid = config.get('common', 'sensorid') - # self.mysql_logger = MySQLlogger(host, port, db, username, passphrase, logdevice, logsocket, sensorid) - if config.getboolean("json", "enabled"): filename = config.get("json", "filename") sensorid = config.get("common", "sensorid") @@ -137,9 +120,6 @@ def start(self): if self.sqlite_logger: self.sqlite_logger.log(event) - # if self.mysql_logger: - # self.mysql_logger.log(event) - if self.syslog_client: self.syslog_client.log(event) diff --git a/conpot/core/loggers/mysql_log.py b/conpot/core/loggers/mysql_log.py deleted file mode 100644 index 1cc362b3..00000000 --- a/conpot/core/loggers/mysql_log.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright (C) 2014 Daniel creo Haslinger -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import gevent -import logging - -import MySQLdb - -from warnings import filterwarnings - -filterwarnings("ignore", category=MySQLdb.Warning) - -logger = logging.getLogger(__name__) - - -class MySQLlogger(object): - def __init__( - self, host, port, db, username, passphrase, logdevice, logsocket, sensorid - ): - self.host = host - self.port = port - self.db = db - self.username = username - self.passphrase = passphrase - self.logdevice = logdevice - self.logsocket = logsocket - self.sensorid = sensorid - - self._connect() - - def _connect(self): - try: - if str(self.logsocket).lower() == "tcp": - self.conn = MySQLdb.connect( - host=self.host, - port=self.port, - user=self.username, - passwd=self.passphrase, - db=self.db, - ) - self._create_db() - elif str(self.logsocket).lower() == "dev": - self.conn = MySQLdb.connect( - unix_socket=self.logdevice, - user=self.username, - passwd=self.passphrase, - db=self.db, - ) - self._create_db() - except (AttributeError, MySQLdb.OperationalError): - logger.error( - "Could not create a stable database connection for logging. Check database and credentials." - ) - - def _create_db(self): - cursor = self.conn.cursor() - cursor.execute( - """ SELECT count(*) FROM information_schema.tables WHERE table_name = %s and table_schema=%s""", - ("events", self.db), - ) - if (cursor.fetchone()[0]) == 0: - cursor.execute( - """CREATE TABLE IF NOT EXISTS `events` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `sensorid` text NOT NULL, - `session` text NOT NULL, - `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `remote` text NOT NULL, - `protocol` text NOT NULL, - `request` text NOT NULL, - `response` text NOT NULL, - PRIMARY KEY (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=latin1; - """ - ) - - def log(self, event, retry=1): - cursor = self.conn.cursor() - - try: - if len(list(event["data"].keys())) > 1: - cursor.execute( - """INSERT INTO - events (sensorid, session, remote, protocol, request, response) - VALUES - (%s, %s, %s, %s, %s, %s)""", - ( - str(self.sensorid), - str(event["id"]), - str(event["remote"]), - event["data_type"], - event["data"].get("request"), - event["data"].get("response"), - ), - ) - else: - cursor.execute( - """INSERT INTO - events (sensorid, session, remote, protocol,request, response) - VALUES - (%s, %s, %s, %s, %s,"NA")""", - ( - str(self.sensorid), - str(event["id"]), - str(event["remote"]), - event["data_type"], - event["data"].get("type"), - ), - ) - self.conn.commit() - except (AttributeError, MySQLdb.OperationalError): - self._connect() - - if retry == 0: - logger.error("Logging failed. Database connection not available.") - return False - else: - logger.debug( - "Logging failed: Database connection lost. Retrying (%s tries left)...", - retry, - ) - retry -= 1 - gevent.sleep(float(0.5)) - return self.log(event, retry) - - return cursor.lastrowid - - def log_session(self, session): - pass - - def select_data(self): - cursor = self.conn.cursor() - cursor.execute("SELECT * FROM events") - print((cursor.fetchall())) - - def select_session_data(self, sessionid): - cursor = self.conn.cursor() - cursor.execute("SELECT * FROM events WHERE session = %s", [str(sessionid)]) - return cursor.fetchall() - - def truncate_table(self, table): - cursor = self.conn.cursor() - try: - affected = cursor.execute("TRUNCATE TABLE %s", [str(table)]) - self.conn.commit() - except (AttributeError, MySQLdb.IntegrityError, MySQLdb.OperationalError): - return False - - return affected diff --git a/conpot/core/loggers/sqlite_log.py b/conpot/core/loggers/sqlite_log.py index 45be6e2a..f7a478dc 100644 --- a/conpot/core/loggers/sqlite_log.py +++ b/conpot/core/loggers/sqlite_log.py @@ -71,11 +71,3 @@ def log(self, event): ) self.conn.commit() return cursor.lastrowid - - def log_session(self, session): - pass - - def select_data(self): - cursor = self.conn.cursor() - cursor.execute("SELECT * FROM events") - print((cursor.fetchall())) diff --git a/conpot/core/loggers/stix_transform.py b/conpot/core/loggers/stix_transform.py index 1d04a58e..0f5f4791 100644 --- a/conpot/core/loggers/stix_transform.py +++ b/conpot/core/loggers/stix_transform.py @@ -29,11 +29,7 @@ from stix.incident.time import Time as StixTime from stix.indicator import Indicator from stix.ttp import TTP, VictimTargeting -from stix.extensions.identity.ciq_identity_3_0 import ( - CIQIdentity3_0Instance, - STIXCIQIdentity3_0, - OrganisationInfo, -) +from stix.extensions.identity.ciq_identity_3_0 import CIQIdentity3_0Instance from cybox.core import Observable from cybox.objects.socket_address_object import SocketAddress diff --git a/conpot/core/session_manager.py b/conpot/core/session_manager.py index 2607acc9..084e1e56 100644 --- a/conpot/core/session_manager.py +++ b/conpot/core/session_manager.py @@ -55,16 +55,6 @@ def get_session( self._sessions.append(attack_session) return attack_session - def get_session_count(self, protocol=None): - count = 0 - if protocol: - for session in self._sessions: - if session.protocol == protocol: - count += 1 - else: - count = len(self._sessions) - return count - def purge_sessions(self): # there is no native purge/clear mechanism for gevent queues, so... self.log_queue = Queue() diff --git a/conpot/core/virtual_fs.py b/conpot/core/virtual_fs.py index 696dbcb1..baccb628 100644 --- a/conpot/core/virtual_fs.py +++ b/conpot/core/virtual_fs.py @@ -20,7 +20,7 @@ import sys import fs import conpot -from fs import open_fs, errors, subfs +from fs import open_fs, subfs from conpot.core.filesystem import AbstractFS, SubAbstractFS logger = logging.getLogger(__name__) diff --git a/conpot/helpers.py b/conpot/helpers.py index 39ec3529..57428735 100644 --- a/conpot/helpers.py +++ b/conpot/helpers.py @@ -32,26 +32,11 @@ def chr_py3(x): return bytearray((x,)) -# covert a number to an ascii byte string -def number_to_bytes(x): - return x if isinstance(x, bytes) else bytes(str(int(x)), encoding="ascii") - - # convert a string to an ascii byte string def str_to_bytes(x): return x if isinstance(x, bytes) else str(x).encode("ascii") -# pack a short int -def pack_short_int(x): - return x if isinstance(x, bytes) else x.to_bytes(2, byteorder="big") - - -# unpack a short int -def unpack_short_int(x): - return int.from_bytes(x, byteorder="big") - - months_map = { 1: "Jan", 2: "Feb", diff --git a/conpot/protocols/IEC104/IEC104_server.py b/conpot/protocols/IEC104/IEC104_server.py index 95a9b60d..d66761ab 100644 --- a/conpot/protocols/IEC104/IEC104_server.py +++ b/conpot/protocols/IEC104/IEC104_server.py @@ -111,8 +111,6 @@ def handle(self, sock, address): 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: diff --git a/conpot/protocols/IEC104/frames.py b/conpot/protocols/IEC104/frames.py index 46b1c1c9..8a8123d8 100644 --- a/conpot/protocols/IEC104/frames.py +++ b/conpot/protocols/IEC104/frames.py @@ -1035,8 +1035,6 @@ def guess_payload_class(self, payload): return asdu_infobj_63 elif self.TypeID == 64: return asdu_infobj_64 - # elif self.TypeID == 70: - # return asdu_infobj_70 elif self.TypeID == 100: return asdu_infobj_100 elif self.TypeID == 101: diff --git a/conpot/protocols/bacnet/bacnet_app.py b/conpot/protocols/bacnet/bacnet_app.py index 829d5d56..84c3e29e 100644 --- a/conpot/protocols/bacnet/bacnet_app.py +++ b/conpot/protocols/bacnet/bacnet_app.py @@ -23,7 +23,6 @@ import sys from bacpypes.pdu import GlobalBroadcast import bacpypes.object -from bacpypes import errors from bacpypes.app import BIPSimpleApplication from bacpypes.constructeddata import Any from bacpypes.constructeddata import InvalidParameterDatatype diff --git a/conpot/protocols/ftp/ftp_base_handler.py b/conpot/protocols/ftp/ftp_base_handler.py index 76fb31fd..79b19ad6 100644 --- a/conpot/protocols/ftp/ftp_base_handler.py +++ b/conpot/protocols/ftp/ftp_base_handler.py @@ -17,8 +17,6 @@ import socketserver import gevent -from gevent import queue -from gevent import select import conpot.core as conpot_core from conpot.core.filesystem import FilesystemError import logging @@ -27,10 +25,7 @@ import fs from datetime import datetime import os -from fs import errors -from fs.path import frombase from conpot.helpers import sanitize_file_name -from gevent import event from conpot.protocols.ftp.ftp_utils import FTPPrivilegeException from gevent import socket diff --git a/conpot/protocols/ftp/ftp_handler.py b/conpot/protocols/ftp/ftp_handler.py index 4e3e23cb..fe1ed488 100644 --- a/conpot/protocols/ftp/ftp_handler.py +++ b/conpot/protocols/ftp/ftp_handler.py @@ -11,7 +11,6 @@ from datetime import datetime import gevent from gevent import socket -from fs import errors from conpot.core.filesystem import FilesystemError, FSOperationNotPermitted from conpot.protocols.ftp.ftp_utils import FTPPrivilegeException, get_data_from_iter diff --git a/conpot/protocols/kamstrup/management_protocol/kamstrup_management_server.py b/conpot/protocols/kamstrup/management_protocol/kamstrup_management_server.py index 601f999d..ba0bbd44 100644 --- a/conpot/protocols/kamstrup/management_protocol/kamstrup_management_server.py +++ b/conpot/protocols/kamstrup/management_protocol/kamstrup_management_server.py @@ -30,9 +30,7 @@ @conpot_protocol class KamstrupManagementServer(object): - def __init__(self, template, template_directory, args, timeout=0): - self.template = template - self.timeout = timeout + def __init__(self, template, template_directory, args): self.command_responder = CommandResponder() self.banner = "\r\nWelcome...\r\nConnected to [{0}]\r\n" logger.info("Kamstrup management protocol server initialized.") diff --git a/conpot/protocols/kamstrup/meter_protocol/kamstrup_server.py b/conpot/protocols/kamstrup/meter_protocol/kamstrup_server.py index 3cadfde1..5a36967e 100644 --- a/conpot/protocols/kamstrup/meter_protocol/kamstrup_server.py +++ b/conpot/protocols/kamstrup/meter_protocol/kamstrup_server.py @@ -32,8 +32,7 @@ @conpot_protocol class KamstrupServer(object): - def __init__(self, template, template_directory, args, timeout=0): - self.timeout = timeout + def __init__(self, template, template_directory, args): self.command_responder = CommandResponder(template) self.server_active = True self.server = None diff --git a/conpot/protocols/kamstrup/usage_simulator.py b/conpot/protocols/kamstrup/usage_simulator.py index b692f77f..03346826 100644 --- a/conpot/protocols/kamstrup/usage_simulator.py +++ b/conpot/protocols/kamstrup/usage_simulator.py @@ -18,7 +18,6 @@ import logging import gevent -from gevent import event import conpot.core as conpot_core diff --git a/conpot/protocols/modbus/modbus_server.py b/conpot/protocols/modbus/modbus_server.py index 3af50f71..5400b105 100644 --- a/conpot/protocols/modbus/modbus_server.py +++ b/conpot/protocols/modbus/modbus_server.py @@ -23,8 +23,8 @@ @conpot_protocol class ModbusServer(modbus.Server): - def __init__(self, template, template_directory, args, timeout=5): - self.timeout = timeout + def __init__(self, template, template_directory, args): + self.timeout = 5 self.delay = None self.mode = None self.host = None diff --git a/conpot/protocols/snmp/conpot_cmdrsp.py b/conpot/protocols/snmp/conpot_cmdrsp.py index 2a0837d5..654539fe 100644 --- a/conpot/protocols/snmp/conpot_cmdrsp.py +++ b/conpot/protocols/snmp/conpot_cmdrsp.py @@ -1,7 +1,6 @@ import sys import logging import random -from datetime import datetime from pysnmp.entity.rfc3413 import cmdrsp from pysnmp.proto import error diff --git a/conpot/protocols/tftp/tftp_handler.py b/conpot/protocols/tftp/tftp_handler.py index ca605b1a..4dbcb8bd 100644 --- a/conpot/protocols/tftp/tftp_handler.py +++ b/conpot/protocols/tftp/tftp_handler.py @@ -4,7 +4,6 @@ import tftpy import time from gevent import socket -from fs import errors from tftpy import TftpException, TftpErrors from tftpy.TftpStates import TftpStateExpectACK, TftpStateExpectDAT from tftpy.TftpPacketTypes import TftpPacketRRQ, TftpPacketWRQ diff --git a/conpot/protocols/tftp/tftp_server.py b/conpot/protocols/tftp/tftp_server.py index ef9c4379..90861035 100644 --- a/conpot/protocols/tftp/tftp_server.py +++ b/conpot/protocols/tftp/tftp_server.py @@ -25,7 +25,6 @@ from gevent.server import DatagramServer import conpot.core as conpot_core from conpot.core.protocol_wrapper import conpot_protocol -from gevent import event from tftpy import TftpException, TftpTimeout import logging from conpot.utils.ext_ip import get_interface_ip diff --git a/conpot/testing.cfg b/conpot/testing.cfg index 0f43999b..c1639226 100644 --- a/conpot/testing.cfg +++ b/conpot/testing.cfg @@ -19,16 +19,6 @@ filename = /var/log/conpot.json [sqlite] enabled = False -[mysql] -enabled = False -device = /tmp/mysql.sock -host = localhost -port = 3306 -db = conpot -username = conpot -passphrase = conpot -socket = tcp ; tcp (sends to host:port), dev (sends to mysql device/socket file) - [syslog] enabled = False device = /dev/log diff --git a/conpot/tests/empty_template.xml b/conpot/tests/empty_template.xml deleted file mode 100644 index c2b8ad0a..00000000 --- a/conpot/tests/empty_template.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/conpot/tests/helpers/s7comm_client.py b/conpot/tests/helpers/s7comm_client.py index 84ed8642..9753a877 100644 --- a/conpot/tests/helpers/s7comm_client.py +++ b/conpot/tests/helpers/s7comm_client.py @@ -18,7 +18,7 @@ from struct import pack, unpack from optparse import OptionGroup -from conpot.helpers import chr_py3, str_to_bytes +from conpot.helpers import str_to_bytes import struct import socket import string diff --git a/conpot/tests/helpers/snmp_client.py b/conpot/tests/helpers/snmp_client.py index 45c751a8..40dd52cc 100644 --- a/conpot/tests/helpers/snmp_client.py +++ b/conpot/tests/helpers/snmp_client.py @@ -4,7 +4,6 @@ from pysnmp.entity import engine, config from pysnmp.carrier.asynsock.dgram import udp from pysnmp.entity.rfc3413 import cmdgen -from pysnmp.proto import rfc1902 class SNMPClient(object): diff --git a/conpot/tests/test_bacnet_server.py b/conpot/tests/test_bacnet_server.py index e63c38bb..a45fa416 100644 --- a/conpot/tests/test_bacnet_server.py +++ b/conpot/tests/test_bacnet_server.py @@ -32,8 +32,6 @@ WhoHasRequest, ReadPropertyRequest, ReadPropertyACK, - AtomicReadFileRequest, - AuthenticateRequest, ) from bacpypes.constructeddata import Any from bacpypes.primitivedata import Real @@ -188,7 +186,3 @@ def test_no_response_requests(self): with Timeout(1, False): results = [s.recvfrom(buf_size) for i in range(len(test_requests))] self.assertIsNone(results) - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_base.py b/conpot/tests/test_base.py deleted file mode 100644 index 7abc8625..00000000 --- a/conpot/tests/test_base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) 2013 Lukas Rist -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -import unittest - - -class TestBase(unittest.TestCase): - def setUp(self): - pass - - def tearDown(self): - pass - - def test_base(self): - return True diff --git a/conpot/tests/test_enip_server.py b/conpot/tests/test_enip_server.py index 7a82f806..f74a50a0 100644 --- a/conpot/tests/test_enip_server.py +++ b/conpot/tests/test_enip_server.py @@ -215,7 +215,3 @@ def test_malformend_request_tcp(self): def test_malformend_request_udp(self): pass - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_ext_ip_util.py b/conpot/tests/test_ext_ip_util.py index c4972a9f..bb556ba4 100644 --- a/conpot/tests/test_ext_ip_util.py +++ b/conpot/tests/test_ext_ip_util.py @@ -55,7 +55,3 @@ def test_fetch_ext_ip(self): self.assertIsNotNone( conpot.utils.ext_ip.get_ext_ip(urls=["https://api.ipify.org"]) ) - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_ftp.py b/conpot/tests/test_ftp.py index 3c541e16..a0f87ca8 100644 --- a/conpot/tests/test_ftp.py +++ b/conpot/tests/test_ftp.py @@ -551,7 +551,3 @@ def test_max_retries(self): user="nobody", passwd="incorrect_pass", ) - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_guardian_ast.py b/conpot/tests/test_guardian_ast.py index 1eeb5574..24ce828d 100644 --- a/conpot/tests/test_guardian_ast.py +++ b/conpot/tests/test_guardian_ast.py @@ -169,7 +169,3 @@ def test_S60200(self): s.close() count = len(re.findall("(?=ULTIMATETEST)", data.decode())) self.assertEqual(count, 4) - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_http_server.py b/conpot/tests/test_http_server.py index cacdb0f1..196469d9 100644 --- a/conpot/tests/test_http_server.py +++ b/conpot/tests/test_http_server.py @@ -216,7 +216,3 @@ def test_not_implemented_method(self): data=payload, ) self.assertEqual(ret.status_code, 501) - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_iec104_server.py b/conpot/tests/test_iec104_server.py index 5831d0a6..c413b2b4 100644 --- a/conpot/tests/test_iec104_server.py +++ b/conpot/tests/test_iec104_server.py @@ -102,7 +102,7 @@ def test_write_relation_for_existing(self): self.databus.set_value("22_20", 0) # Must be in template and relation to 13_20 self.databus.set_value("13_20", 0) # Must be in template - # print str(hex(IEC104.addr_in_hex('13_20'))) + single_command = ( frames.i_frame() / frames.asdu_head(COT=6) @@ -148,7 +148,7 @@ def test_write_no_relation_for_existing(self): s.recv(6) self.databus.set_value("22_19", 0) # Must be in template and no relation - # print str(hex(IEC104.addr_in_hex('13_20'))) + single_command = ( frames.i_frame() / frames.asdu_head(COT=6) @@ -186,7 +186,7 @@ def test_write_wrong_type_for_existing(self): s.recv(6) self.databus.set_value("22_20", 0) # Must be in template - # print str(hex(IEC104.addr_in_hex('13_20'))) + single_command = ( frames.i_frame() / frames.asdu_head(COT=6) @@ -221,7 +221,3 @@ def test_failing_connection_connection_lost_event(self, mock_timeout): self.assertEqual("CONNECTION_LOST", con_lost_event["data"]["type"]) s.close() - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_ipmi_server.py b/conpot/tests/test_ipmi_server.py index f71aacf8..bbab1020 100644 --- a/conpot/tests/test_ipmi_server.py +++ b/conpot/tests/test_ipmi_server.py @@ -164,7 +164,3 @@ def test_misc(self): port=str(self.ipmi_server.server.server_port), ) self.assertEqual(result, b"Set session password\n") - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_kamstrup_management_protocol.py b/conpot/tests/test_kamstrup_management_protocol.py index f3d881f0..fcc2c21c 100644 --- a/conpot/tests/test_kamstrup_management_protocol.py +++ b/conpot/tests/test_kamstrup_management_protocol.py @@ -214,7 +214,3 @@ def test_request_connect_command(self): self.kamstrup_management_server, ) ) - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_kamstrup_meter_protocol.py b/conpot/tests/test_kamstrup_meter_protocol.py index d1543f62..0d4145ab 100644 --- a/conpot/tests/test_kamstrup_meter_protocol.py +++ b/conpot/tests/test_kamstrup_meter_protocol.py @@ -80,7 +80,3 @@ def test_request_get_register(self): # FIXME: verify bytes received from server - ask jkv? pkt = [hex(data[i]) for i in range(len(data))] self.assertTrue(("0x40" in pkt) and ("0x3f" in pkt) and ("0xd" in pkt)) - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_logger_mysql.py b/conpot/tests/test_logger_mysql.py deleted file mode 100644 index a30f3dba..00000000 --- a/conpot/tests/test_logger_mysql.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (C) 2014 Daniel creo Haslinger -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import unittest - -from conpot.core.loggers.mysql_log import MySQLlogger - - -@unittest.skip("switch for a more generic db solution in py3") -class Test_MySQLlogger(unittest.TestCase): - @unittest.skip - def test_mysqllogger(self): - """ - Objective: Test if events can be stored to and retrieved from mysql properly. - """ - - # instanciate our mysql logging infrastructure - host = "127.0.0.1" - port = 3306 - username = "travis" - passphrase = "" - db = "conpot_unittest" - logdevice = "" - logsocket = "tcp" - sensorid = "default" - - mysqllogger = MySQLlogger( - host, port, db, username, passphrase, logdevice, logsocket, sensorid - ) - - # create a test event - test_event = dict() - test_event["id"] = 1337 - test_event["remote"] = "127.0.0.2" - test_event["data_type"] = "unittest" - test_event["data"] = {"request": "foo", "response": "bar"} - - # lets do it, but do not retry in case of failure - success = mysqllogger.log(test_event, 0) - self.assertTrue(success, "Could not log to mysql database") - - # now that we logged something, lets try to retrieve the event again.. - retrieved_event = mysqllogger.select_session_data(test_event["id"]) - self.assertEqual( - len(retrieved_event), - 1, - "Retrieved wrong number of events (or no event at all)", - ) diff --git a/conpot/tests/test_mac_addr.py b/conpot/tests/test_mac_addr.py index dcf79038..a53f9135 100644 --- a/conpot/tests/test_mac_addr.py +++ b/conpot/tests/test_mac_addr.py @@ -63,7 +63,3 @@ def test_mac(self): self.assertTrue(flag is True) else: self.skipTest("Can't change MAC address") - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_modbus_server.py b/conpot/tests/test_modbus_server.py index 9e7b98dc..400f33d0 100644 --- a/conpot/tests/test_modbus_server.py +++ b/conpot/tests/test_modbus_server.py @@ -189,7 +189,3 @@ def test_response_function_43_device_info(self): data = s.recv(1024) s.close() self.assertTrue(b"SIMATIC" in data and b"Siemens" in data) - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_proxy.py b/conpot/tests/test_proxy.py index 7b817e1f..36cb1127 100644 --- a/conpot/tests/test_proxy.py +++ b/conpot/tests/test_proxy.py @@ -26,8 +26,6 @@ from conpot.emulators.proxy import Proxy from conpot.protocols.misc.ascii_decoder import AsciiDecoder -# gevent.monkey.patch_all() - package_directory = os.path.dirname(os.path.abspath(conpot.__file__)) @@ -145,7 +143,3 @@ def test_ssl_proxy_with_decoder(self): def echo_server(self, sock, address): r = sock.recv(len(self.test_input)) sock.send(r) - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_s7_server.py b/conpot/tests/test_s7_server.py index a3f3f93f..a697db43 100644 --- a/conpot/tests/test_s7_server.py +++ b/conpot/tests/test_s7_server.py @@ -85,7 +85,3 @@ def test_s7(self): except AssertionError: print((sec, item, val)) raise - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_snmp_server.py b/conpot/tests/test_snmp_server.py index 62c1adf8..dd905f34 100644 --- a/conpot/tests/test_snmp_server.py +++ b/conpot/tests/test_snmp_server.py @@ -87,7 +87,3 @@ def mock_callback( else: for oid, val in varBindTable: self.result = val.prettyPrint() - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_tftp.py b/conpot/tests/test_tftp.py index 52e25c48..fa9f9f38 100644 --- a/conpot/tests/test_tftp.py +++ b/conpot/tests/test_tftp.py @@ -65,7 +65,3 @@ def test_tftp_download(self): finally: _, _data_fs = conpot_core.get_vfs("tftp") _data_fs.remove("download") - - -if __name__ == "__main__": - unittest.main() diff --git a/conpot/tests/test_vfs.py b/conpot/tests/test_vfs.py index 3a95066c..6729c700 100644 --- a/conpot/tests/test_vfs.py +++ b/conpot/tests/test_vfs.py @@ -25,7 +25,6 @@ from freezegun import freeze_time import fs from datetime import datetime -from fs import permissions from fs.time import epoch_to_datetime @@ -509,7 +508,3 @@ def test_format_list(self): [_result] = [i for i in self._f_list] self.assertIn(self.test_vfs.default_user, _result) self.assertIn("Jul 15 17:51", _result) - - -if __name__ == "__main__": - unittest.main() diff --git a/docs/source/api/reference/conpot.core.loggers.rst b/docs/source/api/reference/conpot.core.loggers.rst index 57f91839..5fd08484 100644 --- a/docs/source/api/reference/conpot.core.loggers.rst +++ b/docs/source/api/reference/conpot.core.loggers.rst @@ -36,14 +36,6 @@ conpot.core.loggers.log\_worker module :undoc-members: :show-inheritance: -conpot.core.loggers.mysql\_log module -------------------------------------- - -.. automodule:: conpot.core.loggers.mysql_log - :members: - :undoc-members: - :show-inheritance: - conpot.core.loggers.sqlite\_log module -------------------------------------- diff --git a/docs/source/api/reference/conpot.tests.rst b/docs/source/api/reference/conpot.tests.rst index cba8ccea..3717aee0 100644 --- a/docs/source/api/reference/conpot.tests.rst +++ b/docs/source/api/reference/conpot.tests.rst @@ -19,14 +19,6 @@ conpot.tests.test\_bacnet\_server module :undoc-members: :show-inheritance: -conpot.tests.test\_base module ------------------------------- - -.. automodule:: conpot.tests.test_base - :members: - :undoc-members: - :show-inheritance: - conpot.tests.test\_docs module ------------------------------ @@ -131,14 +123,6 @@ conpot.tests.test\_logger\_json module :undoc-members: :show-inheritance: -conpot.tests.test\_logger\_mysql module ---------------------------------------- - -.. automodule:: conpot.tests.test_logger_mysql - :members: - :undoc-members: - :show-inheritance: - conpot.tests.test\_mac\_addr module ----------------------------------- diff --git a/docs/source/installation/centos.rst b/docs/source/installation/centos.rst deleted file mode 100644 index 8d18f57c..00000000 --- a/docs/source/installation/centos.rst +++ /dev/null @@ -1,73 +0,0 @@ -CentOS installation 7.3 -====================================== - -Validated to work on CentOS version 7.3-1611 & Conpot 0.5.1 (may also work on other CentOS versions) - -1. Login via ssh with an account with sufficient system privileges (e.g root) ------------------------------------------------------------------------------ -2. Upgrade the system ---------------------- -:: - -$ sudo yum -y update - - -3. Install needed packages and libs ------------------------------------ -:: - -$ sudo yum -y install libxslt-devel libxml2-devel python-pip python -$ sudo yum -y install mariadb-server mysql-connector-python.noarch mariadb-devel -$ sudo yum -y install git python-lxml.x86_64 python-devel -$ sudo yum -y groupinstall "Development tools" -$ wget https://bootstrap.pypa.io/get-pip.py && sudo python ./get-pip.py - -Upgrade `lxml` -:: - -$ sudo pip install -U lxml - -4. Start mysql server ------------------------- -:: - -$ sudo chkconfig mariadb on -$ sudo service mariadb start - -Sugestions to mysql secure installation are to change the root password and accect to removing anonymous users, test database and disallow root login. -:: - -$ sudo mysql_secure_installation - -5. CONPOT installation ----------------------- -:: - -$ git clone https://github.com/mushorg/conpot -$ cd conpot/ -$ sudo python setup.py install - -6. Open ports in firewalld : 80 , 102, 161 and 502 ---------------------------------------------------- -:: - -$ firewall-cmd --permanent --add-port=80/tcp -$ firewall-cmd --permanent --add-port=102/tcp -$ firewall-cmd --permanent --add-port=161/tcp -$ firewall-cmd --permanent --add-port=502/tcp -$ firewall-cmd --reload - - -7. Start the Conpot honeypot ------------------------------ - -:: - -$ conpot --template default - -8. Check if it's running and you can access it from remote (in browser) ------------------------------------------------------------------------ - -:: - -$ lynx http://YOUR_IP/ diff --git a/docs/source/installation/install.rst b/docs/source/installation/install.rst index 2b0851a8..98b7b318 100644 --- a/docs/source/installation/install.rst +++ b/docs/source/installation/install.rst @@ -8,7 +8,7 @@ Note that this is also the recommended way of installing conpot on a machine. In Install dependencies: :: - $ sudo apt-get install git libxslt1-dev python3.6-dev libevent-dev default-libmysqlclient-dev + $ sudo apt-get install libxslt1-dev python3.6-dev libevent-dev Create the virtualenv :: @@ -20,14 +20,7 @@ Activate the environment $ source conpot/bin/activate -Upgrade any basic tools in the environment and deps -:: - - $ pip install --upgrade pip - $ pip install --upgrade setuptools - $ pip install cffi - -Install the table version of Conpot from PyPI: +Install the stable version from PyPI: :: $ pip install conpot diff --git a/docs/source/usage/hmi.rst b/docs/source/usage/hmi.rst deleted file mode 100644 index 778038d8..00000000 --- a/docs/source/usage/hmi.rst +++ /dev/null @@ -1,56 +0,0 @@ -============================== -Human Machine Interfaces (HMI) -============================== - -The default HMI ---------------- - -Yes, Conpot comes with a default 'HMI'. If you run Conpot with the default configuration and no additional -parameters, it will serve the default page on port 80. The default page is just a plain text file which gets -the 'sysDescr' from the SNMP server. For example: -:: - - System is running: Siemens, SIMATIC, S7-200 - - -Creating your own HMI ---------------------- - -Manual HMI creation -~~~~~~~~~~~~~~~~~~~ - -The probably most painless way to customize your HMI is by modifying the default page. If you don't want to -mess with the default files ('recommended') you can create a directory holding a custom index.html. In order -to use the custom HTML page you have to start Conpot with the following parameters: -:: - - # conpot -w -r - -For example: -:: - - # conpot -w www/ -r index.html - -We use jinja2 as template engine. Please refer to the default HMI template (``conpot/www/index.html``) for a -minimal example. - - -Crawling an existing HMI -~~~~~~~~~~~~~~~~~~~~~~~~ - -We recommend to use the crawler only against your own applications! Usage on your own risk. -Improper usage will most likely damage the system. - -In case you have access to a HTML HMI you can use to crawler to create a copy that's compatible with Conpot's -HTTP server. -:: - - # hmi_crawler --target http:// --www - # hmi_crawler -t http://localhost:80 -w dump/ - -This will dump a copy of the target web content into ``www``. If you run Conpot now: -:: - - # conpot -w dump/ -r index.html - -It will server the dumped web page as HMI. \ No newline at end of file diff --git a/docs/source/usage/index.rst b/docs/source/usage/index.rst index f99f96ed..a4aa6920 100644 --- a/docs/source/usage/index.rst +++ b/docs/source/usage/index.rst @@ -9,4 +9,3 @@ Conpot usage usage customization - hmi diff --git a/requirements.txt b/requirements.txt index 8b975a51..a686e345 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,14 @@ gevent>=1.0 -cffi pysnmp pysnmp-mibs pysmi lxml -bottle -jinja2 -beautifulsoup4 requests sphinx libtaxii>=1.1.0 -mysqlclient==2.0.1 -xlrd crc16 natsort scapy==2.4.3rc1 -enum34 hpfeeds3 modbus-tk stix-validator @@ -26,13 +19,11 @@ pyghmi==1.4.1 mixbox modbus-tk cpppo -pytest fs==2.3.0 python-slugify tftpy # some freezegun versions broken freezegun!=0.3.13 pytest -tox pycrypto sphinx_rtd_theme diff --git a/setup.py b/setup.py index 2c2f7d30..ca8dabda 100644 --- a/setup.py +++ b/setup.py @@ -32,10 +32,5 @@ long_description_content_type="text/markdown", description="""Conpot is an ICS honeypot with the goal to collect intelligence about the motives and methods of adversaries targeting industrial control systems""", - test_suite="nose.collector", - tests_require="nose", - dependency_links=[ - "https://github.com/rep/hpfeeds/archive/master.zip#egg=hpfeeds", - ], install_requires=open("requirements.txt").read().splitlines(), ) diff --git a/tox.ini b/tox.ini index a46bce05..44f2adc6 100644 --- a/tox.ini +++ b/tox.ini @@ -9,13 +9,10 @@ basepython = description = run conpot tests in {envpython} envdir = {toxinidir}/.tox/py37 deps = - coverage pytest_timeout - pytest-xdist pytest-cov commands = pip --default-timeout=1000 install -r requirements.txt - # {posargs:coverage run --timid --source={envpython} -m pytest -v --timeout=30} {envpython} -m pytest -rsx -v --timeout=60 --cov=conpot {posargs} [testenv:run]