diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 04775a5..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: publish -on: [push] -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: install requirements - run: sudo apt-get install curl - - name: build and run - run: | - curl https://raw.githubusercontent.com/qeeqbox/global-scripts/main/auto-checking.sh > auto-checking.sh - chmod +x auto-checking.sh - ./auto-checking.sh honeypots - - name: commit changes - if: success() - run: | - git config user.name "qb-auto" - git config user.email "qbactions@gmail.com" - git add -- . :!auto-checking.sh - git commit -m "🤖" - git push - - name: delete auto-checking.sh - if: always() - run: | - [ -e auto-checking.sh ] && rm -- auto-checking.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b84285a..bb178d5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,6 +27,6 @@ jobs: - name: Install dependencies run: python -m pip install --upgrade pip setuptools wheel - name: Installation - run: python -m pip install ".[test]" + run: python -m pip install ".[dev]" - name: Unit Tests run: pytest -v ./tests diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a0578fe --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,25 @@ +default_stages: [commit, push] +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: check-added-large-files + args: [--maxkb=10000] + + - id: check-toml + + - id: forbid-new-submodules + + - id: no-commit-to-branch + +- repo: https://github.com/jumanjihouse/pre-commit-hooks + rev: 2.1.5 + hooks: + - id: shellcheck + +- repo: https://github.com/charliermarsh/ruff-pre-commit + rev: 'v0.1.11' + hooks: + - id: ruff + args: [ --fix, --exit-non-zero-on-fix ] + - id: ruff-format diff --git a/README.md b/README.md index 3da7fe6..f0ce5b6 100644 --- a/README.md +++ b/README.md @@ -253,19 +253,21 @@ qsshserver.kill_server() - Port: 21/tcp - Lib: Twisted.ftp - Logs: ip, port, username and password (default) - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QHTTPProxyServer - Server: HTTP Proxy - Port: 8080/tcp - Lib: Twisted (low level emulation) - Logs: ip, port and data - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) + - Returns a dummy template by default + - A custom template can be provided by setting `"template"` for this server in `config.json` (should be an absolute path) - QHTTPServer - Server: HTTP - Port: 80/tcp - Lib: Twisted.http - Logs: ip, port, username and password - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QHTTPSServer - Server: HTTPS - Port: 443/tcp @@ -276,7 +278,7 @@ qsshserver.kill_server() - Port: 143/tcp - Lib: Twisted.imap - Logs: ip, port, username and password (default) - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QMysqlServer - Emulator: Mysql - Port: 3306/tcp @@ -287,7 +289,7 @@ qsshserver.kill_server() - Port: 110/tcp - Lib: Twisted.pop3 - Logs: ip, port, username and password (default) - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QPostgresServer - Emulator: Postgres - Port: 5432/tcp @@ -308,7 +310,7 @@ qsshserver.kill_server() - Port: 25/tcp - Lib: smtpd - Logs: ip, port, username and password (default) - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QSOCKS5Server - Server: SOCK5 - Port: 1080/tcp @@ -319,7 +321,7 @@ qsshserver.kill_server() - Port: 22/tcp - Lib: paramiko - Logs: ip, port, username and password - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QTelnetServer - Server: Telnet - Port: 23/tcp @@ -359,7 +361,7 @@ qsshserver.kill_server() - Emulator: Oracle - Port: 1521/tcp - Lib: Twisted (low level emulation) - - Logs: ip, port and connet data + - Logs: ip, port and connect data - QSNMPServer - Emulator: SNMP - Port: 161/udp @@ -370,31 +372,31 @@ qsshserver.kill_server() - Port: 5060/udp - Lib: Twisted.sip - Logs: ip, port and data - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QIRCServer - Emulator: IRC - Port: 6667/tcp - Lib: Twisted.irc - Logs: ip, port, username and password - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QPJLServer - Emulator: PJL - Port: 9100/tcp - Lib: Twisted - Logs: ip, port - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QIPPServer - Emulator: IPP - Port: 631/tcp - Lib: Twisted - - Logs: ip, por - - Options: Capture all threat actor commands and data (avalible) + - Logs: ip, port + - Options: Capture all threat actor commands and data (available) - QRDPServer - Emulator: RDP - Port: 3389/tcp - Lib: Sockets - Logs: ip, port, username and password - - Options: Capture all threat actor commands and data (avalible) + - Options: Capture all threat actor commands and data (available) - QDHCPServer - Emulator: DHCP - Port: 67/udp diff --git a/auto_pip.sh b/auto_pip.sh index c983efe..36c2170 100644 --- a/auto_pip.sh +++ b/auto_pip.sh @@ -1,9 +1,7 @@ #!/bin/bash -echo "[x] Checking setup.py" -python3 setup.py check -r -s echo "[x] Creating pypi Package" -python3 setup.py sdist bdist_wheel 2>stderr.log 1>stdout.log +python3 -m build 2>stderr.log 1>stdout.log if grep -q "error:" stderr.log then diff --git a/clean-up.logs b/clean-up.logs deleted file mode 100644 index 3039318..0000000 --- a/clean-up.logs +++ /dev/null @@ -1,9 +0,0 @@ -Fri Jan 19 16:52:27 UTC 2024 -[X] honeypots -[X] installing python3, python3-pip, autopep8 & jq -[X] installing the pip package -[X] checking the pip package -[X] delete -[X] good package -[X] running autopep8 -[X] done diff --git a/honeypots/__init__.py b/honeypots/__init__.py index c9bcb8d..d6bffe3 100644 --- a/honeypots/__init__.py +++ b/honeypots/__init__.py @@ -1,9 +1,22 @@ -#!/usr/bin/env python -from .__main__ import main_logic from .dhcp_server import QDHCPServer from .dns_server import QDNSServer from .elastic_server import QElasticServer from .ftp_server import QFTPServer +from .helper import ( + is_privileged, + clean_all, + close_port_wrapper, + disable_logger, + get_free_port, + get_running_servers, + kill_server_wrapper, + kill_servers, + postgres_class, + server_arguments, + set_local_vars, + setup_logger, + set_up_error_logging, +) from .http_proxy_server import QHTTPProxyServer from .http_server import QHTTPServer from .https_server import QHTTPSServer @@ -30,4 +43,49 @@ from .ssh_server import QSSHServer from .telnet_server import QTelnetServer from .vnc_server import QVNCServer -from .helper import server_arguments, clean_all, kill_servers, get_free_port, close_port_wrapper, kill_server_wrapper, setup_logger, disable_logger, postgres_class, get_running_servers, set_local_vars, check_privileges + +__all__ = [ + "QBSniffer", + "QDHCPServer", + "QDNSServer", + "QElasticServer", + "QFTPServer", + "QHTTPProxyServer", + "QHTTPSServer", + "QHTTPServer", + "QIMAPServer", + "QIPPServer", + "QIRCServer", + "QLDAPServer", + "QMSSQLServer", + "QMemcacheServer", + "QMysqlServer", + "QNTPServer", + "QOracleServer", + "QPJLServer", + "QPOP3Server", + "QPostgresServer", + "QRDPServer", + "QRedisServer", + "QSIPServer", + "QSMBServer", + "QSMTPServer", + "QSNMPServer", + "QSOCKS5Server", + "QSSHServer", + "QTelnetServer", + "QVNCServer", + "is_privileged", + "clean_all", + "close_port_wrapper", + "disable_logger", + "get_free_port", + "get_running_servers", + "kill_server_wrapper", + "kill_servers", + "postgres_class", + "server_arguments", + "set_local_vars", + "setup_logger", + "set_up_error_logging", +] diff --git a/honeypots/__main__.py b/honeypots/__main__.py old mode 100644 new mode 100755 index 096dfaa..76d6ed3 --- a/honeypots/__main__.py +++ b/honeypots/__main__.py @@ -1,15 +1,95 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') -filterwarnings('ignore', category=RuntimeWarning, module='runpy') +from __future__ import annotations -all_servers = ['QDNSServer', 'QFTPServer', 'QHTTPProxyServer', 'QHTTPServer', 'QHTTPSServer', 'QIMAPServer', 'QMysqlServer', 'QPOP3Server', 'QPostgresServer', 'QRedisServer', 'QSMBServer', 'QSMTPServer', 'QSOCKS5Server', 'QSSHServer', 'QTelnetServer', 'QVNCServer', 'QElasticServer', 'QMSSQLServer', 'QLDAPServer', 'QNTPServer', 'QMemcacheServer', 'QOracleServer', 'QSNMPServer', 'QSIPServer', 'QIRCServer', 'QRDPServer', 'QDHCPServer', 'QPJLServer', 'QIPPServer'] -temp_honeypots = [] - -from signal import signal, alarm, SIGALRM, SIG_IGN, SIGTERM, SIGINT, SIGTSTP -from time import sleep +import logging +import sys +from argparse import ArgumentParser, SUPPRESS, Namespace +from atexit import register from functools import wraps +from json import JSONDecodeError, loads +from os import geteuid +from pathlib import Path +from signal import alarm, SIGALRM, SIGINT, signal, SIGTERM, SIGTSTP +from subprocess import Popen +from sys import stdout +from time import sleep +from typing import Any +from uuid import uuid4 + +from netifaces import ifaddresses, AF_INET, AF_LINK, interfaces +from psutil import net_io_counters, Process + +from honeypots import ( + QBSniffer, + QDHCPServer, + QDNSServer, + QElasticServer, + QFTPServer, + QHTTPProxyServer, + QHTTPSServer, + QHTTPServer, + QIMAPServer, + QIPPServer, + QIRCServer, + QLDAPServer, + QMSSQLServer, + QMemcacheServer, + QMysqlServer, + QNTPServer, + QOracleServer, + QPJLServer, + QPOP3Server, + QPostgresServer, + QRDPServer, + QRedisServer, + QSIPServer, + QSMBServer, + QSMTPServer, + QSNMPServer, + QSOCKS5Server, + QSSHServer, + QTelnetServer, + QVNCServer, + is_privileged, + clean_all, + setup_logger, + set_up_error_logging, +) + +all_servers = { + "dhcp": QDHCPServer, + "dns": QDNSServer, + "elastic": QElasticServer, + "ftp": QFTPServer, + "httpproxy": QHTTPProxyServer, + "https": QHTTPSServer, + "http": QHTTPServer, + "imap": QIMAPServer, + "ipp": QIPPServer, + "irc": QIRCServer, + "ldap": QLDAPServer, + "mssql": QMSSQLServer, + "memcache": QMemcacheServer, + "mysql": QMysqlServer, + "ntp": QNTPServer, + "oracle": QOracleServer, + "pjl": QPJLServer, + "pop3": QPOP3Server, + "postgres": QPostgresServer, + "rdp": QRDPServer, + "redis": QRedisServer, + "sip": QSIPServer, + "smb": QSMBServer, + "smtp": QSMTPServer, + "snmp": QSNMPServer, + "socks5": QSOCKS5Server, + "ssh": QSSHServer, + "telnet": QTelnetServer, + "vnc": QVNCServer, +} + +logger = set_up_error_logging() class SignalFence: @@ -20,7 +100,7 @@ def __init__(self, signals_to_listen_on, interval=1): for signal_to_listen_on in signals_to_listen_on: signal(signal_to_listen_on, self.handle_signal) - def handle_signal(self, signum, frame): + def handle_signal(self, signum, frame): # noqa: ARG002 self.fence_up = False def wait_on_fence(self): @@ -33,309 +113,400 @@ def __init__(self, strategy): self.strategy = strategy def await_termination(self): - if self.strategy == 'input': - input('') - elif self.strategy == 'signal': + if self.strategy == "input": + input("") + elif self.strategy == "signal": SignalFence([SIGTERM, SIGINT, SIGTSTP]).wait_on_fence() else: - raise Exception('Unknown termination strategy: ' + strategy) + raise Exception(f"Unknown termination strategy: {self.strategy}") def timeout(seconds=10): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): - def handle_timeout(signum, frame): - raise Exception() + def handle_timeout(signum, frame): # noqa: ARG001 + raise TimeoutError() + signal(SIGALRM, handle_timeout) alarm(seconds) - result = None try: result = func(*args, **kwargs) finally: alarm(0) return result - return wrapper - return decorator + return wrapper -def list_all_honeypots(): - for honeypot in all_servers: - print(honeypot[1:].replace('Server', '').lower()) + return decorator @timeout(5) -def server_timeout(object, name): +def server_timeout(obj, name): try: - print('[x] Start testing {}'.format(name)) - object.test_server() - except BaseException: - print('[x] Timeout {}'.format(name)) - print('[x] Done testing {}'.format(name)) - - -def main_logic(): + logger.info(f"Start testing {name}") + obj.test_server() + except TimeoutError: + logging.error(f"Timeout during test {name}") + logger.info(f"Done testing {name}") + + +class HoneypotsManager: + def __init__(self, options: Namespace, server_args: dict[str, str | int]): + self.options = options + self.server_args = server_args + self.config_data = self._load_config() if self.options.config else None + self.auto = options.auto if geteuid() != 0 else False + self.honeypots: list[tuple[Any, str, bool]] = [] + + def main(self): + logger.info("For updates, check https://github.com/qeeqbox/honeypots") + if not is_privileged(): + logger.warning( + "Using system or well-known ports requires higher privileges (E.g. sudo -E)" + ) + + if self.options.list: + for service in all_servers: + print(service) # noqa: T201 + elif self.options.kill: + clean_all() + elif self.options.chameleon and self.config_data is not None: + self._start_chameleon_mode() + elif self.options.setup: + self._set_up_honeypots() + + def _load_config(self): + config_path = Path(self.options.config) + if not config_path.is_file(): + logger.error(f'Config file "{config_path}" not found') + sys.exit(1) + try: + config_data = loads(config_path.read_text()) + logger.info(f"Successfully loaded config file {config_path}") + return config_data + except FileNotFoundError: + logger.error(f"Unable to load config file: File {config_path} not found") + sys.exit(1) + except JSONDecodeError as error: + logger.error(f"Unable to parse config file as JSON: {error}") + sys.exit(1) + + def _set_up_honeypots(self): # noqa: C901 + register(_exit_handler) + if self.options.termination_strategy == "input": + logger.info("Use [Enter] to exit or python3 -m honeypots --kill") + if self.options.config != "": + logger.warning("config.json file overrides --ip, --port, --username and --password") + if self.options.setup == "all": + self._start_all_servers() + else: + self._start_configured_servers() - from honeypots import QDNSServer, QFTPServer, QHTTPProxyServer, QHTTPServer, QHTTPSServer, QIMAPServer, QMysqlServer, QPOP3Server, QPostgresServer, QRedisServer, QSMBServer, QSMTPServer, QSOCKS5Server, QSSHServer, QTelnetServer, QVNCServer, QMSSQLServer, QElasticServer, QLDAPServer, QNTPServer, QMemcacheServer, QOracleServer, QSNMPServer, QSIPServer, QIRCServer, QRDPServer, QDHCPServer, QPJLServer, QIPPServer, server_arguments, clean_all, postgres_class, setup_logger, QBSniffer, get_running_servers, check_privileges - from atexit import register - from argparse import ArgumentParser, SUPPRESS - from sys import stdout - from subprocess import Popen - from netifaces import ifaddresses, AF_INET, AF_LINK, interfaces - from psutil import Process, net_io_counters - from uuid import uuid4 - from json import JSONEncoder, dumps, load - from os import geteuid - - def exit_handler(): - print('[x] Cleaning') - clean_all() - sleep(1) - - class _ArgumentParser(ArgumentParser): - def error(self, message): - self.exit(2, 'Error: %s\n' % (message)) - - ARG_PARSER = _ArgumentParser(description='Qeeqbox/honeypots customizable honeypots for monitoring network traffic, bots activities, and username\\password credentials', usage=SUPPRESS) - ARG_PARSER._action_groups.pop() - ARG_PARSER_SETUP = ARG_PARSER.add_argument_group('Arguments') - ARG_PARSER_SETUP.add_argument('--setup', help='target honeypot E.g. ssh or you can have multiple E.g ssh,http,https', metavar='', default='') - ARG_PARSER_SETUP.add_argument('--list', action='store_true', help='list all available honeypots') - ARG_PARSER_SETUP.add_argument('--kill', action='store_true', help='kill all honeypots') - ARG_PARSER_SETUP.add_argument('--verbose', action='store_true', help='Print error msgs') - ARG_PARSER_OPTIONAL = ARG_PARSER.add_argument_group('Honeypots options') - ARG_PARSER_OPTIONAL.add_argument('--ip', help='Override the IP', metavar='', default='') - ARG_PARSER_OPTIONAL.add_argument('--port', help='Override the Port (Do not use on multiple!)', metavar='', default='') - ARG_PARSER_OPTIONAL.add_argument('--username', help='Override the username', metavar='', default='') - ARG_PARSER_OPTIONAL.add_argument('--password', help='Override the password', metavar='', default='') - ARG_PARSER_OPTIONAL.add_argument('--config', help='Use a config file for honeypots settings', metavar='', default='') - ARG_PARSER_OPTIONAL.add_argument('--options', type=str, help='Extra options', metavar='', default='') - ARG_PARSER_OPTIONAL_2 = ARG_PARSER.add_argument_group('General options') - ARG_PARSER_OPTIONAL_2.add_argument('--termination-strategy', help='Determines the strategy to terminate by', default='input', choices=['input', 'signal']) - ARG_PARSER_OPTIONAL_2.add_argument('--test', default='', metavar='', help='Test a honeypot') - ARG_PARSER_OPTIONAL_2.add_argument('--auto', help='Setup the honeypot with random port', action='store_true') - ARG_PARSER_CHAMELEON = ARG_PARSER.add_argument_group('Chameleon') - ARG_PARSER_CHAMELEON.add_argument('--chameleon', action='store_true', help='reserved for chameleon project') - ARG_PARSER_CHAMELEON.add_argument('--sniffer', action='store_true', help='sniffer - reserved for chameleon project') - ARG_PARSER_CHAMELEON.add_argument('--iptables', action='store_true', help='iptables - reserved for chameleon project') - ARGV = ARG_PARSER.parse_args() - PARSED_ARG_PARSER_OPTIONAL = {action.dest: getattr(ARGV, action.dest, '') for action in ARG_PARSER_OPTIONAL._group_actions} - config_data = None - print("[!] For updates, check https://github.com/qeeqbox/honeypots") - if check_privileges() == False: - print("[!] Using system or well-known ports requires higher privileges (E.g. sudo -E)") - if ARGV.config != '': - with open(ARGV.config) as f: - try: - config_data = load(f) - except Exception as e: - print('[!] Unable to load or parse config.json file', e) - exit() - if 'db_sqlite' in config_data['logs'] or 'db_postgres' in config_data['logs']: - uuid = 'honeypotslogger' + '_' + 'main' + '_' + str(uuid4())[:8] - if 'db_options' in config_data: - if 'drop' in config_data['db_options']: - print('[x] Setup Logger {} with a db, drop is on'.format(uuid)) - logs = setup_logger('main', uuid, ARGV.config, True) - else: - print('[x] Setup Logger {} with a db, drop is off'.format(uuid)) - logs = setup_logger('main', uuid, ARGV.config, False) + running_honeypots = {"good": [], "bad": []} + if len(self.honeypots) > 0: + for _, server_name, status in self.honeypots: + if status is False or status is None: + running_honeypots["bad"].append(server_name) else: - logs = setup_logger('main', uuid, ARGV.config, True) - if ARGV.list: - list_all_honeypots() - elif ARGV.kill: - clean_all() - elif ARGV.chameleon and config_data is not None: - print('[x] Chameleon mode') - if config_data['sniffer_filter'] and config_data['sniffer_interface']: - if not ARGV.test: - if ARGV.sniffer: - current_interfaces = 'unknown' - try: - current_interfaces = ' '.join(interfaces()) - if config_data['sniffer_interface'] in current_interfaces: - print('[x] Your IP: {}'.format(ifaddresses(config_data['sniffer_interface'])[AF_INET][0]['addr'])) - print('[x] Your MAC: {}'.format(ifaddresses(config_data['sniffer_interface'])[AF_LINK][0]['addr'])) - else: - exit() - except Exception as e: - print('[!] Unable to detect IP or MAC for [{}] interface, current interfaces are [{}]'.format(config_data['sniffer_interface'], current_interfaces), e) - exit() - if ARGV.iptables: - try: - print('[x] Fixing iptables') - Popen('iptables -A OUTPUT -p tcp -m tcp --tcp-flags RST RST -j DROP', shell=True) - except Exception as e: - print(e) - print('[x] Wait for 10 seconds..') - stdout.flush() - sleep(2) - - if ARGV.config != '': - print('[x] Config.json file overrides --ip, --port, --username and --password') - - if isinstance(config_data['honeypots'], dict): - print('[x] Parsing honeypot [hard]') - for honeypot in config_data['honeypots']: - for _honeypot in all_servers: - if 'q{}server'.format(honeypot).lower() == _honeypot.lower(): - if ARGV.port != '': - ARGV.port = int(ARGV.port) - PARSED_ARG_PARSER_OPTIONAL['port'] = ARGV.port - x = locals()[_honeypot](**PARSED_ARG_PARSER_OPTIONAL) - if not ARGV.test: - x.run_server(process=True) - else: - server_timeout(x, _honeypot) - x.kill_server() - temp_honeypots.append(x) - elif isinstance(config_data['honeypots'], str): - print('[x] Parsing honeypot [easy]') - if ':' in config_data['honeypots']: - print('[!] You cannot bind ports with [:] in this mode, use the honeypots dict instead') - exit() - for server in config_data['honeypots'].split(','): - for honeypot in all_servers: - if 'q{}server'.format(server).lower() == honeypot.lower(): - if ARGV.port != '': - ARGV.port = int(ARGV.port) - PARSED_ARG_PARSER_OPTIONAL['port'] = ARGV.port - x = locals()[honeypot](**PARSED_ARG_PARSER_OPTIONAL) - if not ARGV.test: - x.run_server(process=True) - else: - server_timeout(x, honeypot) - x.kill_server() - temp_honeypots.append(x) + running_honeypots["good"].append(server_name) + + if len(running_honeypots["good"]) > 0: + running_servers = ", ".join(running_honeypots["good"]) + logger.info(f"servers {running_servers} running...") + + if len(running_honeypots["bad"]) > 0: + not_running_servers = ", ".join(running_honeypots["bad"]) + logger.warning(f"servers {not_running_servers} not running...") + + if len(running_honeypots["bad"]) == 0: + logger.info("Everything looks good!") + + if len(running_honeypots["good"]) > 0 and not self.options.test: + Termination(self.options.termination_strategy).await_termination() + + self._stop_servers() + + def _start_all_servers(self): + try: + for service in all_servers: + self._start_server(service) + except Exception as error: + logger.exception(f"Starting honeypots failed: {error}") + + def _start_configured_servers(self): + for service in self.options.setup.split(","): + logger.info("Parsing honeypot [normal]") + if ":" in service: + service, port = service.split(":") # noqa: PLW2901 + auto = False + self.options.port = int(port) + self.server_args["port"] = self.options.port + elif self.options.port: + auto = False + elif self.options.test: + logger.error( + f"server {service} was configured with random port, unable to test..." + ) + continue else: - print('[!] Unable to parse honeypot from config.json file') - exit() - - if ARGV.sniffer: - print('[x] Start sniffer') - x = locals()['QBSniffer'](filter=config_data['sniffer_filter'], interface=config_data['sniffer_interface'], config=ARGV.config) - x.run_sniffer(process=True) - temp_honeypots.append(x) - - if not ARGV.test: - print('[x] Everything looks good!') - while True: - try: - _servers = {} - logs.info(['system', {'type': 'network', 'bytes_sent': net_io_counters().bytes_sent, 'bytes_recv': net_io_counters().bytes_recv, 'packets_sent': net_io_counters().packets_sent, 'packets_recv': net_io_counters().packets_recv}]) - for server in temp_honeypots: - _servers[server.__class__.__name__] = {'memory': Process(server.process.pid).memory_percent(), 'cpu': Process(server.process.pid).cpu_percent()} - logs.info(['system', _servers]) - except Exception as e: - print(e) - sleep(20) - else: - if len(temp_honeypots) > 0: - for server in temp_honeypots: - try: - print('[x] Killing {} tester'.format(server.__class__.__name__)) - server.kill_server() - except Exception as e: - print(e) - print('[x] Please wait few seconds') - sleep(5) - elif ARGV.setup != '': - - register(exit_handler) - auto = ARGV.auto - if ARGV.termination_strategy == 'input': - print('[x] Use [Enter] to exit or python3 -m honeypots --kill') - - if ARGV.config != '': - print('[x] config.json file overrides --ip, --port, --username and --password') - - if geteuid() == 0: - auto = False - - if ARGV.setup == 'all': + auto = True + self._start_server(service, auto) + + def _start_server(self, service: str, auto: bool | None = None): + if auto is None: + auto = self.auto + server_class = all_servers.get(service.lower()) + if not server_class: + logger.warning(f"Skipping unknown service {service}") + return + server = server_class(**self.server_args) + if not self.options.test: + status = server.run_server(process=True, auto=auto) + else: + server_timeout(server, service) + server.kill_server() + status = False + self.honeypots.append((server, service, status)) + + def _stop_servers(self): + logger.info("[x] Stopping servers...") + for server, name, _ in self.honeypots: try: - for honeypot in all_servers: - status = False - x = locals()[honeypot](**PARSED_ARG_PARSER_OPTIONAL) - status = x.run_server(process=True, auto=auto) - temp_honeypots.append([x, honeypot, status]) - except Exception as e: - print(e) + logger.info(f"[x] Killing {server.__class__.__name__} server") + server.kill_server() + except Exception as error: + logger.exception(f"Error when killing server {name}: {error}") + logger.info("[x] Please wait few seconds") + sleep(5) + + def _start_chameleon_mode(self): # noqa: C901,PLR0912 + logger.info("[x] Chameleon mode") + if "db_sqlite" in self.config_data["logs"] or "db_postgres" in self.config_data["logs"]: + logs = self._setup_logging() else: - servers = ARGV.setup.split(',') - for server in servers: - print('[x] Parsing honeypot [normal]') - if ':' in server: - for honeypot in all_servers: - if 'q{}server'.format(server.split(':')[0]).lower() == honeypot.lower(): - ARGV.port = int(server.split(':')[1]) - PARSED_ARG_PARSER_OPTIONAL['port'] = ARGV.port - x = locals()[honeypot](**PARSED_ARG_PARSER_OPTIONAL) - status = False - if not ARGV.test: - status = x.run_server(process=True) - else: - server_timeout(x, honeypot) - x.kill_server() - temp_honeypots.append([x, honeypot, status]) - elif ARGV.port != '': - for honeypot in all_servers: - if 'q{}server'.format(server).lower() == honeypot.lower(): - x = locals()[honeypot](**PARSED_ARG_PARSER_OPTIONAL) - status = False - if not ARGV.test: - status = x.run_server(process=True) - else: - server_timeout(x, honeypot) - x.kill_server() - temp_honeypots.append([x, honeypot, status]) - else: - for honeypot in all_servers: - if 'q{}server'.format(server).lower() == honeypot.lower(): - x = locals()[honeypot](**PARSED_ARG_PARSER_OPTIONAL) - status = False - if not ARGV.test: - status = x.run_server(process=True, auto=auto) - else: - print('[x] {} was configured with random port, unable to test..'.format(honeypot)) - temp_honeypots.append([x, honeypot, status]) - - running_honeypots = {'good': [], 'bad': []} - - if len(temp_honeypots) > 0: - good = True - for server in temp_honeypots: - if server[2] == False or server[2] is None: - running_honeypots['bad'].append(server[1]) - else: - running_honeypots['good'].append(server[1]) + logger.error("logging must be configured with db_sqlite or db_postgres") + sys.exit(1) + + sniffer_filter = self.config_data.get("sniffer_filter") + sniffer_interface = self.config_data.get("sniffer_interface") + if not (sniffer_filter and sniffer_interface): + return + + if not self.options.test and self.options.sniffer: + _check_interfaces(sniffer_interface) + if self.options.iptables: + _fix_ip_tables() + logger.info("[x] Wait for 10 seconds...") + stdout.flush() + sleep(2) + + if self.options.config != "": + logger.warning( + "[x] Config.json file overrides --ip, --port, --username and --password" + ) + + if self.options.port: + self.options.port = int(self.options.port) + self.server_args["port"] = self.options.port + + honeypots = self.config_data["honeypots"] + if isinstance(honeypots, dict): + logger.info("[x] Parsing honeypot [hard]") + for honeypot in honeypots: + self._start_server(honeypot) + elif isinstance(honeypots, str): + logger.info("[x] Parsing honeypot [easy]") + if ":" in honeypots: + logger.error( + "[!] You cannot bind ports with [:] in this mode, " + "use the honeypots dict instead" + ) + sys.exit(1) + for server in honeypots.split(","): + self._start_server(server) + else: + logger.error(f"[!] Unable to parse honeypots from config: {honeypots}") + sys.exit(1) - if len(running_honeypots['good']) > 0: - print('[x] {} running..'.format(', '.join(running_honeypots['good']))) + if self.options.sniffer: + self._start_sniffer(sniffer_filter, sniffer_interface) - if len(running_honeypots['bad']) > 0: - print('[x] {} not running..'.format(', '.join(running_honeypots['bad']))) + if not self.options.test: + logger.info("[x] Everything looks good!") + self._stats_loop(logs) + else: + self._stop_servers() + + def _setup_logging(self) -> logging.Logger: + uuid = f"honeypotslogger_main_{str(uuid4())[:8]}" + if "db_options" in self.config_data: + drop = "drop" in self.config_data["db_options"] + logger.info(f"[x] Setup Logger {uuid} with a db, drop is {drop}") + else: + drop = True + return setup_logger("main", uuid, self.options.config, drop) + + def _start_sniffer(self, sniffer_filter, sniffer_interface): + logger.info("[x] Starting sniffer") + sniffer = QBSniffer( + filter=sniffer_filter, + interface=sniffer_interface, + config=self.options.config, + ) + sniffer.run_sniffer(process=True) + self.honeypots.append((sniffer, "sniffer", True)) + + def _stats_loop(self, logs): + while True: + try: + network_stats = { + "type": "network", + "bytes_sent": net_io_counters().bytes_sent, + "bytes_recv": net_io_counters().bytes_recv, + "packets_sent": net_io_counters().packets_sent, + "packets_recv": net_io_counters().packets_recv, + } + logs.info(["system", network_stats]) + load_stats = { + server.__class__.__name__: { + "memory": Process(server.process.pid).memory_percent(), + "cpu": Process(server.process.pid).cpu_percent(), + } + for server, *_ in self.honeypots + } + logs.info(["system", load_stats]) + except Exception as error: + logger.exception(f"An error occurred during stats logging: {error}") + sleep(20) + + +def _fix_ip_tables(): + try: + logger.info("[x] Fixing iptables") + Popen( + "iptables -A OUTPUT -p tcp -m tcp --tcp-flags RST RST -j DROP", + shell=True, + ) + except Exception as error: + logger.exception(f"Could not fix iptables: {error}") - if len(running_honeypots['bad']) == 0: - print('[x] Everything looks good!') - if len(running_honeypots['good']) > 0: - if not ARGV.test: - Termination(ARGV.termination_strategy).await_termination() +def _check_interfaces(sniffer_interface): + current_interfaces = "unknown" + try: + current_interfaces = " ".join(interfaces()) + if sniffer_interface not in current_interfaces: + logger.error( + f"[!] Sniffer interface {sniffer_interface} not found in current interfaces" + ) + sys.exit(1) + ip_address = ifaddresses(sniffer_interface)[AF_INET][0]["addr"] + logger.info(f"[x] Your IP: {ip_address}") + mac_address = ifaddresses(sniffer_interface)[AF_LINK][0]["addr"] + logger.info(f"[x] Your MAC: {mac_address}") + except Exception as error: + logger.exception( + f"[!] Unable to detect IP or MAC for [{sniffer_interface}] interface, " + f"current interfaces are [{current_interfaces}]: {error}" + ) + sys.exit(1) + + +def _exit_handler(): + logger.info("[x] Cleaning") + clean_all() + sleep(1) + + +class _ArgumentParser(ArgumentParser): + def error(self, message): + logger.error(message) + self.exit(2, f"Error: {message}\n") + + +def _parse_args() -> tuple[Namespace, dict[str, str | int]]: + arg_parser = _ArgumentParser( + description=( + "Qeeqbox/honeypots customizable honeypots for monitoring network traffic, bots " + "activities, and username\\password credentials" + ), + usage=SUPPRESS, + ) + arg_parser._action_groups.pop() + arg_parser_setup = arg_parser.add_argument_group("Arguments") + arg_parser_setup.add_argument( + "--setup", + help="target honeypot E.g. ssh or you can have multiple E.g ssh,http,https", + metavar="", + default="", + ) + arg_parser_setup.add_argument( + "--list", action="store_true", help="list all available honeypots" + ) + arg_parser_setup.add_argument("--kill", action="store_true", help="kill all honeypots") + arg_parser_setup.add_argument("--verbose", action="store_true", help="Print error msgs") + arg_parser_optional = arg_parser.add_argument_group("Honeypots options") + arg_parser_optional.add_argument("--ip", help="Override the IP", metavar="", default="") + arg_parser_optional.add_argument( + "--port", + help="Override the Port (Do not use on multiple!)", + metavar="", + default="", + ) + arg_parser_optional.add_argument( + "--username", help="Override the username", metavar="", default="" + ) + arg_parser_optional.add_argument( + "--password", help="Override the password", metavar="", default="" + ) + arg_parser_optional.add_argument( + "--config", + help="Use a config file for honeypots settings", + metavar="", + default="", + ) + arg_parser_optional.add_argument( + "--options", type=str, help="Extra options", metavar="", default="" + ) + arg_parser_optional_2 = arg_parser.add_argument_group("General options") + arg_parser_optional_2.add_argument( + "--termination-strategy", + help="Determines the strategy to terminate by", + default="input", + choices=["input", "signal"], + ) + arg_parser_optional_2.add_argument("--test", action="store_true", help="Test a honeypot") + arg_parser_optional_2.add_argument( + "--auto", help="Setup the honeypot with random port", action="store_true" + ) + arg_parser_chameleon = arg_parser.add_argument_group("Chameleon") + arg_parser_chameleon.add_argument( + "--chameleon", action="store_true", help="reserved for chameleon project" + ) + arg_parser_chameleon.add_argument( + "--sniffer", + action="store_true", + help="sniffer - reserved for chameleon project", + ) + arg_parser_chameleon.add_argument( + "--iptables", + action="store_true", + help="iptables - reserved for chameleon project", + ) + argv = arg_parser.parse_args() + server_args = { + action.dest: getattr(argv, action.dest, "") + for action in arg_parser_optional._group_actions + } + return argv, server_args + - for server in temp_honeypots: - try: - if not ARGV.test: - print('[x] Killing {} honeypot'.format(server[0].__class__.__name__)) - else: - print('[x] Killing {} tester'.format(server[0].__class__.__name__)) - server[0].kill_server() - except Exception as e: - print(e) - print('[x] Please wait few seconds') - sleep(5) +def main_logic(): + argv, server_args = _parse_args() + manager = HoneypotsManager(argv, server_args) + manager.main() -if __name__ == '__main__': +if __name__ == "__main__": main_logic() diff --git a/honeypots/dhcp_server.py b/honeypots/dhcp_server.py index 89d0fdb..3a01a3d 100644 --- a/honeypots/dhcp_server.py +++ b/honeypots/dhcp_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,43 +8,54 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') -filterwarnings(action='ignore', module='.*socket.*') +""" from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor -from twisted.python import log as tlog from struct import unpack, error as StructError from socket import inet_aton from subprocess import Popen from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from honeypots.helper import ( + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + setup_logger, + set_local_vars, + check_if_server_is_running, +) from uuid import uuid4 -class QDHCPServer(): +class QDHCPServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 67 - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 67 + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def dhcp_server_main(self): _q_s = self class CustomDatagramProtocolProtocol(DatagramProtocol): - def check_bytes(self, string): if isinstance(string, bytes): return string.decode() @@ -52,34 +63,52 @@ def check_bytes(self, string): return str(string) def payload(self, value, message): - op, htype, hlen, hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr, chaddr = unpack('1s1s1s1s4s2s2s4s4s4s4s16s', message[:44]) - #op, htype, hlen, hops, xid, secs, flags, ciaddr - response = b'\x02\x01\x06\x00' + xid + b'\x00\x00\x00\x00\x00\x00\x00\x00' - #yiaddr, siaddr, giaddr, chaddr - response += inet_aton(_q_s.dhcp_ip_lease) + inet_aton(_q_s.dhcp_ip) + inet_aton('0.0.0.0') + chaddr - #sname, file, magic - response += b'\x00' * 64 + b'\x00' * 128 + b'\x63\x82\x53\x63' + ( + op, + htype, + hlen, + hops, + xid, + secs, + flags, + ciaddr, + yiaddr, + siaddr, + giaddr, + chaddr, + ) = unpack("1s1s1s1s4s2s2s4s4s4s4s16s", message[:44]) + # op, htype, hlen, hops, xid, secs, flags, ciaddr + response = b"\x02\x01\x06\x00" + xid + b"\x00\x00\x00\x00\x00\x00\x00\x00" + # yiaddr, siaddr, giaddr, chaddr + response += ( + inet_aton(_q_s.dhcp_ip_lease) + + inet_aton(_q_s.dhcp_ip) + + inet_aton("0.0.0.0") + + chaddr + ) + # sname, file, magic + response += b"\x00" * 64 + b"\x00" * 128 + b"\x63\x82\x53\x63" # options response += bytes([53, 1, value]) response += bytes([54, 4]) + inet_aton(_q_s.dhcp_ip) response += bytes([1, 4]) + inet_aton(_q_s.subnet_mask) response += bytes([3, 4]) + inet_aton(_q_s.router) response += bytes([6, 4]) + inet_aton(_q_s.dns_server) - response += bytes([51, 4]) + b'\x00\x00\xa8\xc0' # lease - response += b'\xff' + response += bytes([51, 4]) + b"\x00\x00\xa8\xc0" # lease + response += b"\xff" return response def parse_options(self, raw): options = {} tag_name = None tag_size = None - tag = '' + tag = "" for idx, b in enumerate(raw): if tag_name is None: tag_name = b elif tag_name is not None and tag_size is None: tag_size = b - tag = '' + tag = "" else: if tag_size: tag_size -= 1 @@ -88,24 +117,36 @@ def parse_options(self, raw): options.update({self.check_bytes(tag_name): self.check_bytes(tag)}) tag_name = None tag_size = None - tag = '' + tag = "" return options def datagramReceived(self, data, addr): try: - mac_address = unpack('!28x6s', data[:34])[0].hex(':') + mac_address = unpack("!28x6s", data[:34])[0].hex(":") except StructError: mac_address = "None" data = self.parse_options(data[240:]) - data.update({'mac_address': mac_address}) - _q_s.logs.info({'server': 'dhcp_server', 'action': 'query', 'status': 'success', 'src_ip': addr[0], 'src_port': addr[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': data}) - self.transport.loseConnection() - - reactor.listenUDP(port=self.port, protocol=CustomDatagramProtocolProtocol(), interface=self.ip) + data.update({"mac_address": mac_address}) + _q_s.logs.info( + { + "server": "dhcp_server", + "action": "query", + "status": "success", + "src_ip": addr[0], + "src_port": addr[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": data, + } + ) + + reactor.listenUDP( + port=self.port, protocol=CustomDatagramProtocolProtocol(), interface=self.ip + ) reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -117,13 +158,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'dhcp_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "dhcp_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -132,19 +199,21 @@ def run_server(self, process=False, auto=False): self.dhcp_server_main() def close_port(self): - ret = close_port_wrapper('dhcp_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("dhcp_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('dhcp_server', self.uuid, self.process) + ret = kill_server_wrapper("dhcp_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None): pass -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qdhcpserver = QDHCPServer(ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config) + qdhcpserver = QDHCPServer( + ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config + ) qdhcpserver.run_server() diff --git a/honeypots/dns_server.py b/honeypots/dns_server.py index ca85808..a05fcd4 100644 --- a/honeypots/dns_server.py +++ b/honeypots/dns_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,70 +8,111 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') +from __future__ import annotations from twisted.names import dns, error, client from twisted.names.server import DNSServerFactory from twisted.internet import defer, reactor -from twisted.python import log as tlog from subprocess import Popen from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from honeypots.helper import ( + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + setup_logger, + set_local_vars, + check_if_server_is_running, +) from uuid import uuid4 from contextlib import suppress -class QDNSServer(): +class QDNSServer: def __init__(self, **kwargs): self.auto_disabled = None - self.resolver_addresses = [('8.8.8.8', 53)] + self.resolver_addresses = [("8.8.8.8", 53)] self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 53 - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 53 + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def dns_server_main(self): _q_s = self - class CustomCilentResolver(client.Resolver): + class CustomClientResolver(client.Resolver): def queryUDP(self, queries, timeout=2): res = client.Resolver.queryUDP(self, queries, timeout) def queryFailed(reason): return defer.fail(error.DomainError()) + res.addErrback(queryFailed) return res class CustomDNSServerFactory(DNSServerFactory): def gotResolverResponse(self, response, protocol, message, address): - args = (self, response, protocol, message, address) - _q_s.logs.info({'server': 'dns_server', 'action': 'connection', 'src_ip': address[0], 'src_port': address[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - with suppress(Exception): - for items in response: - for item in items: - _q_s.logs.info({'server': 'dns_server', 'action': 'query', 'src_ip': address[0], 'src_port': address[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': item.payload}) - return DNSServerFactory.gotResolverResponse(*args) - - self.resolver = CustomCilentResolver(servers=self.resolver_addresses) + if address is None: + src_ip, src_port = "None", "None" + else: + src_ip, src_port = address + for items in response: + for item in items: + _q_s.logs.info( + { + "server": "dns_server", + "action": "query", + "src_ip": src_ip, + "src_port": src_port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": item.payload, + } + ) + return super().gotResolverResponse(response, protocol, message, address) + + class CustomDnsUdpProtocol(dns.DNSDatagramProtocol): + def datagramReceived(self, data: bytes, addr: tuple[str, int]): + _q_s.logs.info( + { + "server": "dns_server", + "action": "connection", + "src_ip": addr[0], + "src_port": addr[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": data.decode(errors="replace"), + } + ) + super().datagramReceived(data, addr) + + self.resolver = CustomClientResolver(servers=self.resolver_addresses) self.factory = CustomDNSServerFactory(clients=[self.resolver]) - self.protocol = dns.DNSDatagramProtocol(controller=self.factory) + self.protocol = CustomDnsUdpProtocol(controller=self.factory) reactor.listenUDP(self.port, self.protocol, interface=self.ip) reactor.listenTCP(self.port, self.factory, interface=self.ip) reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -83,13 +124,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'dns_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "dns_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -99,24 +166,25 @@ def run_server(self, process=False, auto=False): return None def close_port(self): - ret = close_port_wrapper('dns_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("dns_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('dns_server', self.uuid, self.process) + ret = kill_server_wrapper("dns_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, domain=None): with suppress(Exception): from dns.resolver import Resolver + res = Resolver(configure=False) res.nameservers = [self.ip] res.port = self.port - temp_domain = domain or 'example.org' - r = res.query(temp_domain, 'a') + temp_domain = domain or "example.org" + r = res.query(temp_domain, "a") -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: qdnsserver = QDNSServer(ip=parsed.ip, port=parsed.port, config=parsed.config) diff --git a/honeypots/elastic_server.py b/honeypots/elastic_server.py index d58f1a9..4b6b105 100644 --- a/honeypots/elastic_server.py +++ b/honeypots/elastic_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,14 +8,9 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') -filterwarnings(action='ignore', module='.*elasticsearch.*') +""" from base64 import b64encode, b64decode -from requests.packages.urllib3 import disable_warnings from json import dumps from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer from urllib.parse import urlparse @@ -26,40 +21,61 @@ from os import path, getenv from OpenSSL import crypto from tempfile import gettempdir, _get_candidate_names -from honeypots.helper import check_if_server_is_running, close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, set_local_vars, setup_logger +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) from contextlib import suppress -disable_warnings() - -class QElasticServer(): +class QElasticServer: def __init__(self, **kwargs): self.auto_disabled = None self.key = path.join(gettempdir(), next(_get_candidate_names())) self.cert = path.join(gettempdir(), next(_get_candidate_names())) self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 9200 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'elastic' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 9200 + ) + self.username = ( + kwargs.get("username", None) + or (hasattr(self, "username") and self.username) + or "elastic" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def CreateCert(self, host_name, key, cert): pk = crypto.PKey() pk.generate_key(crypto.TYPE_RSA, 2048) c = crypto.X509() - c.get_subject().C = 'US' - c.get_subject().ST = 'OR' - c.get_subject().L = 'None' - c.get_subject().O = 'None' - c.get_subject().OU = 'None' + c.get_subject().C = "US" + c.get_subject().ST = "OR" + c.get_subject().L = "None" + c.get_subject().O = "None" + c.get_subject().OU = "None" c.get_subject().CN = next(_get_candidate_names()) c.set_serial_number(0) before, after = (0, 60 * 60 * 24 * 365 * 2) @@ -67,21 +83,21 @@ def CreateCert(self, host_name, key, cert): c.gmtime_adj_notAfter(after) c.set_issuer(c.get_subject()) c.set_pubkey(pk) - c.sign(pk, 'sha256') - open(cert, 'wb').write(crypto.dump_certificate(crypto.FILETYPE_PEM, c)) - open(key, 'wb').write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pk)) + c.sign(pk, "sha256") + open(cert, "wb").write(crypto.dump_certificate(crypto.FILETYPE_PEM, c)) + open(key, "wb").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pk)) def elastic_server_main(self): _q_s = self class CustomElasticServerHandler(SimpleHTTPRequestHandler): - - server_version = '' - sys_version = '' + server_version = "" + sys_version = "" def _dump_headers(self): headers = {} with suppress(Exception): + def check_bytes(string): if isinstance(string, bytes): return string.decode() @@ -91,7 +107,18 @@ def check_bytes(string): for item, value in dict(self.headers).items(): headers.update({check_bytes(item): check_bytes(value)}) - _q_s.logs.info({'server': 'elastic_server', 'action': 'dump', 'data': check_bytes(self.raw_requestline), 'src_ip': self.client_address[0], 'src_port': self.client_address[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'headers': headers}) + _q_s.logs.info( + { + "server": "elastic_server", + "action": "dump", + "data": check_bytes(self.raw_requestline), + "src_ip": self.client_address[0], + "src_port": self.client_address[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "headers": headers, + } + ) return headers def _remove_headers(self, headers): @@ -107,75 +134,308 @@ def _compress_gzip(self, content): def _set_response_gzip(self, content, code): self.send_response(code) gzip_compressed_data = self._compress_gzip(content) - self.send_header('content-encoding', 'gzip') - self.send_header('content-length', str(len(gzip_compressed_data))) - self.send_header('content-type', 'application/json; charset=UTF-8') + self.send_header("content-encoding", "gzip") + self.send_header("content-length", str(len(gzip_compressed_data))) + self.send_header("content-type", "application/json; charset=UTF-8") self.end_headers() return gzip_compressed_data def do_HEAD(self): self.send_response(200) - self.send_header('content-encoding', 'gzip') - self.send_header('content-type', 'application/json; charset=UTF-8') + self.send_header("content-encoding", "gzip") + self.send_header("content-type", "application/json; charset=UTF-8") self.end_headers() def _set_response_gzip_auth(self, content, code): self._dump_headers() self.send_response(code) - self._remove_headers([b'server:', b'date:']) + self._remove_headers([b"server:", b"date:"]) gzip_compressed_data = self._compress_gzip(content) - self.send_header('content-encoding', 'gzip') - self.send_header('content-length', str(len(gzip_compressed_data))) - self.send_header('content-type', 'application/json; charset=UTF-8') - self.send_header('WWW-Authenticate', 'Basic realm="security" charset="UTF-8"') + self.send_header("content-encoding", "gzip") + self.send_header("content-length", str(len(gzip_compressed_data))) + self.send_header("content-type", "application/json; charset=UTF-8") + self.send_header("WWW-Authenticate", 'Basic realm="security" charset="UTF-8"') self.end_headers() return gzip_compressed_data def do_GET(self): - username = '' - password = '' - e_name = '045dffec8b60' - e_cluster_name = 'R&DBackup' - e_host = '172.17.0.2' - e_transport_address = e_host + ':9300' - e_build_type = 'en' - e_os_name = 'Linux' - e_os_pretty_name = 'CentOS Linux 8' - e_os_version = '5.8.0-53-generic' + username = "" + password = "" + e_name = "045dffec8b60" + e_cluster_name = "R&DBackup" + e_host = "172.17.0.2" + e_transport_address = e_host + ":9300" + e_build_type = "en" + e_os_name = "Linux" + e_os_pretty_name = "CentOS Linux 8" + e_os_version = "5.8.0-53-generic" key = self.server.get_auth_key() - if self.headers.get('Authorization') is None: - _q_s.logs.info({'server': 'elastic_server', 'action': 'login', 'status': 'failed', 'src_ip': self.client_address[0], 'src_port': self.client_address[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) - auth_paylaod = bytes(dumps({'error': {'root_cause': [{'type': 'security_exception', 'reason': 'unable to authenticate user [{}] for REST request [/]'.format(username), 'header': {'WWW-Authenticate': 'Basic realm=\"security\" charset=\"UTF-8\"'}}], 'type': 'security_exception', 'reason': 'unable to authenticate user [{}] for REST request [/]'.format(username), 'header': {'WWW-Authenticate': 'Basic realm=\"security\" charset=\"UTF-8\"'}}, 'status': 401}), 'utf-8') + if self.headers.get("Authorization") is None: + _q_s.logs.info( + { + "server": "elastic_server", + "action": "login", + "status": "failed", + "src_ip": self.client_address[0], + "src_port": self.client_address[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) + auth_paylaod = bytes( + dumps( + { + "error": { + "root_cause": [ + { + "type": "security_exception", + "reason": "unable to authenticate user [{}] for REST request [/]".format( + username + ), + "header": { + "WWW-Authenticate": 'Basic realm="security" charset="UTF-8"' + }, + } + ], + "type": "security_exception", + "reason": "unable to authenticate user [{}] for REST request [/]".format( + username + ), + "header": { + "WWW-Authenticate": 'Basic realm="security" charset="UTF-8"' + }, + }, + "status": 401, + } + ), + "utf-8", + ) self.wfile.write(self._set_response_gzip_auth(auth_paylaod, 401)) - elif self.headers.get('Authorization') == 'Basic ' + str(key): - extracted = '' - _q_s.logs.info({'server': 'elastic_server', 'action': 'login', 'status': 'success', 'src_ip': self.client_address[0], 'src_port': self.client_address[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': _q_s.username, 'password': _q_s.password}) + elif self.headers.get("Authorization") == "Basic " + str(key): + extracted = "" + _q_s.logs.info( + { + "server": "elastic_server", + "action": "login", + "status": "success", + "src_ip": self.client_address[0], + "src_port": self.client_address[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": _q_s.username, + "password": _q_s.password, + } + ) with suppress(Exception): extracted = urlparse(self.path).path - if extracted == '/': - normal_payload = bytes(dumps({'name': e_name, 'cluster_name': e_cluster_name, 'cluster_uuid': '09cf5BKcTCG2U8z2ndwGEw', 'version': {'number': '7.12.1', 'build_flavor': 'default', 'build_type': e_build_type, 'build_hash': '3186837139b9c6b6d23c3200870651f10d3343b7', 'build_date': '2021-04-20T20:56:39.040728659Z', 'build_snapshot': False, 'lucene_version': '8.8.0', 'minimum_wire_compatibility_version': '6.8.0', 'minimum_index_compatibility_version': '6.0.0-beta1'}, 'tagline': 'You Know, for Search'}), 'utf-8') + if extracted == "/": + normal_payload = bytes( + dumps( + { + "name": e_name, + "cluster_name": e_cluster_name, + "cluster_uuid": "09cf5BKcTCG2U8z2ndwGEw", + "version": { + "number": "7.12.1", + "build_flavor": "default", + "build_type": e_build_type, + "build_hash": "3186837139b9c6b6d23c3200870651f10d3343b7", + "build_date": "2021-04-20T20:56:39.040728659Z", + "build_snapshot": False, + "lucene_version": "8.8.0", + "minimum_wire_compatibility_version": "6.8.0", + "minimum_index_compatibility_version": "6.0.0-beta1", + }, + "tagline": "You Know, for Search", + } + ), + "utf-8", + ) self.wfile.write(self._set_response_gzip(normal_payload, 200)) - elif extracted.startswith('/_nodes'): - _nodes_payload = bytes(dumps({'_nodes': {'total': 1, 'successful': 1, 'failed': 0}, 'cluster_name': e_cluster_name, 'nodes': {'rvyTV3xvTgyt74ti4u12bw': {'name': e_name, 'transport_address': e_transport_address, 'host': e_host, 'src_ip': e_host, 'version': '7.12.1', 'build_flavor': 'default', 'build_type': e_build_type, 'build_hash': '3186837139b9c6b6d23c3200870651f10d3343b7', 'roles': ['data', 'data_cold', 'data_content', 'data_frozen', 'data_hot', 'data_warm', 'ingest', 'master', 'ml', 'remote_cluster_client', 'transform'], 'attributes': {'ml.machine_memory': '16685318144', 'xpack.installed': 'true', 'transform.node': 'true', 'ml.max_open_jobs': '20', 'ml.max_jvm_size': '8342470656'}, 'process': {'refresh_interval_in_millis': 1000, 'id': 7, 'mlockall': False}}, 'os': {'refresh_interval_in_millis': 1000, 'name': e_os_name, 'pretty_name': e_os_pretty_name, 'arch': 'amd64', 'version': e_os_version, 'available_processors': 32, 'allocated_processors': 8}, 'process': {'refresh_interval_in_millis': 1000, 'id': 7, 'mlockall': False}}}), 'utf-8') + elif extracted.startswith("/_nodes"): + _nodes_payload = bytes( + dumps( + { + "_nodes": {"total": 1, "successful": 1, "failed": 0}, + "cluster_name": e_cluster_name, + "nodes": { + "rvyTV3xvTgyt74ti4u12bw": { + "name": e_name, + "transport_address": e_transport_address, + "host": e_host, + "src_ip": e_host, + "version": "7.12.1", + "build_flavor": "default", + "build_type": e_build_type, + "build_hash": "3186837139b9c6b6d23c3200870651f10d3343b7", + "roles": [ + "data", + "data_cold", + "data_content", + "data_frozen", + "data_hot", + "data_warm", + "ingest", + "master", + "ml", + "remote_cluster_client", + "transform", + ], + "attributes": { + "ml.machine_memory": "16685318144", + "xpack.installed": "true", + "transform.node": "true", + "ml.max_open_jobs": "20", + "ml.max_jvm_size": "8342470656", + }, + "process": { + "refresh_interval_in_millis": 1000, + "id": 7, + "mlockall": False, + }, + }, + "os": { + "refresh_interval_in_millis": 1000, + "name": e_os_name, + "pretty_name": e_os_pretty_name, + "arch": "amd64", + "version": e_os_version, + "available_processors": 32, + "allocated_processors": 8, + }, + "process": { + "refresh_interval_in_millis": 1000, + "id": 7, + "mlockall": False, + }, + }, + } + ), + "utf-8", + ) self.wfile.write(self._set_response_gzip(_nodes_payload, 200)) - elif extracted.startswith('/_cluster/health'): - _cluster_health_payload = bytes(dumps({'cluster_name': e_cluster_name, 'status': 'green', 'timed_out': False, 'number_of_nodes': 1, 'number_of_data_nodes': 1, 'active_primary_shards': 0, 'active_shards': 0, 'relocating_shards': 0, 'initializing_shards': 0, 'unassigned_shards': 0, 'delayed_unassigned_shards': 0, 'number_of_pending_tasks': 0, 'number_of_in_flight_fetch': 0, 'task_max_waiting_in_queue_millis': 0, 'active_shards_percent_as_number': 100.0}), 'utf-8') + elif extracted.startswith("/_cluster/health"): + _cluster_health_payload = bytes( + dumps( + { + "cluster_name": e_cluster_name, + "status": "green", + "timed_out": False, + "number_of_nodes": 1, + "number_of_data_nodes": 1, + "active_primary_shards": 0, + "active_shards": 0, + "relocating_shards": 0, + "initializing_shards": 0, + "unassigned_shards": 0, + "delayed_unassigned_shards": 0, + "number_of_pending_tasks": 0, + "number_of_in_flight_fetch": 0, + "task_max_waiting_in_queue_millis": 0, + "active_shards_percent_as_number": 100.0, + } + ), + "utf-8", + ) self.wfile.write(self._set_response_gzip(_cluster_health_payload, 200)) - elif extracted.startswith('/_'): - _index = extracted.split('/')[1].lower() - _payload = bytes(dumps({'error': {'root_cause': [{'type': 'invalid_index_name_exception', 'reason': 'Invalid index name [{}], must not start with "_".'.format(_index), 'index_uuid': '_na_', 'index': _index}], 'type': 'invalid_index_name_exception', 'reason': 'Invalid index name [{}], must not start with "_".'.format(_index), 'index_uuid': '_na_', 'index': _index}, 'status': 400}), 'utf-8') + elif extracted.startswith("/_"): + _index = extracted.split("/")[1].lower() + _payload = bytes( + dumps( + { + "error": { + "root_cause": [ + { + "type": "invalid_index_name_exception", + "reason": f'Invalid index name [{_index}], must not start with "_".', + "index_uuid": "_na_", + "index": _index, + } + ], + "type": "invalid_index_name_exception", + "reason": f'Invalid index name [{_index}], must not start with "_".', + "index_uuid": "_na_", + "index": _index, + }, + "status": 400, + } + ), + "utf-8", + ) self.wfile.write(self._set_response_gzip(_payload, 400)) else: - _search = extracted.split('/')[1].lower() - _search_payload = bytes(dumps({'error': {'root_cause': [{'type': 'index_not_found_exception', 'reason': 'no such index [{}]'.format(_search), 'resource.type': 'index_or_alias', 'resource.id': _search, 'index_uuid': '_na_', 'index': _search}], 'type': 'index_not_found_exception', 'reason': 'no such index [{}]'.format(_search), 'resource.type': 'index_or_alias', 'resource.id': _search, 'index_uuid': '_na_', 'index': _search}, 'status': 404}), 'utf-8') + _search = extracted.split("/")[1].lower() + _search_payload = bytes( + dumps( + { + "error": { + "root_cause": [ + { + "type": "index_not_found_exception", + "reason": f"no such index [{_search}]", + "resource.type": "index_or_alias", + "resource.id": _search, + "index_uuid": "_na_", + "index": _search, + } + ], + "type": "index_not_found_exception", + "reason": f"no such index [{_search}]", + "resource.type": "index_or_alias", + "resource.id": _search, + "index_uuid": "_na_", + "index": _search, + }, + "status": 404, + } + ), + "utf-8", + ) self.wfile.write(self._set_response_gzip(_search_payload, 404)) else: - authorization_string = self.headers.get('Authorization').split(' ') - basic = b64decode(authorization_string[1]).decode('utf-8') - username, password = basic.split(':') - _q_s.logs.info({'server': 'elastic_server', 'action': 'login', 'status': 'failed', 'src_ip': self.client_address[0], 'src_port': self.client_address[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) - auth_paylaod = bytes(dumps({'error': {'root_cause': [{'type': 'security_exception', 'reason': 'missing authentication credentials for REST request [/]', 'header': {'WWW-Authenticate': 'Basic realm=\"security\" charset=\"UTF-8\"'}}], 'type': 'security_exception', 'reason': 'missing authentication credentials for REST request [/]', 'header': {'WWW-Authenticate': 'Basic realm=\"security\" charset=\"UTF-8\"'}}, 'status': 401}), 'utf-8') + authorization_string = self.headers.get("Authorization").split(" ") + basic = b64decode(authorization_string[1]).decode("utf-8") + username, password = basic.split(":") + _q_s.logs.info( + { + "server": "elastic_server", + "action": "login", + "status": "failed", + "src_ip": self.client_address[0], + "src_port": self.client_address[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) + auth_paylaod = bytes( + dumps( + { + "error": { + "root_cause": [ + { + "type": "security_exception", + "reason": "missing authentication credentials for REST request [/]", + "header": { + "WWW-Authenticate": 'Basic realm="security" charset="UTF-8"' + }, + } + ], + "type": "security_exception", + "reason": "missing authentication credentials for REST request [/]", + "header": { + "WWW-Authenticate": 'Basic realm="security" charset="UTF-8"' + }, + }, + "status": 401, + } + ), + "utf-8", + ) self.wfile.write(self._set_response_gzip_auth(auth_paylaod, 401)) do_POST = do_GET @@ -183,36 +443,50 @@ def do_GET(self): do_DELETE = do_GET def send_error(self, code, message=None): - self.error_message_format = 'Error!' + self.error_message_format = "Error!" SimpleHTTPRequestHandler.send_error(self, code, message) def log_message(self, format, *args): return def handle_one_request(self): - _q_s.logs.info({'server': 'elastic_server', 'action': 'connection', 'src_ip': self.client_address[0], 'src_port': self.client_address[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "elastic_server", + "action": "connection", + "src_ip": self.client_address[0], + "src_port": self.client_address[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) return SimpleHTTPRequestHandler.handle_one_request(self) class CustomElasticServer(ThreadingHTTPServer): - key = b64encode(bytes('%s:%s' % ('elastic', 'changeme'), 'utf-8')).decode('ascii') + key = b64encode(bytes("%s:%s" % ("elastic", "changeme"), "utf-8")).decode("ascii") def __init__(self, address, handlerClass=CustomElasticServerHandler): super().__init__(address, handlerClass) def set_auth_key(self, username, password): - self.key = b64encode('{}:{}'.format(username, password).encode('utf-8')).decode('ascii') + self.key = b64encode(f"{username}:{password}".encode()).decode("ascii") def get_auth_key(self): return self.key server = CustomElasticServer((self.ip, self.port)) server.set_auth_key(self.username, self.password) - self.CreateCert('localhost', self.key, self.cert) - server.socket = wrap_socket(server.socket, keyfile=self.key, certfile=self.cert, server_side=True,) + self.CreateCert("localhost", self.key, self.cert) + server.socket = wrap_socket( + server.socket, + keyfile=self.key, + certfile=self.cert, + server_side=True, + ) server.serve_forever() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -224,13 +498,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'elastic_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "elastic_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -240,26 +546,38 @@ def run_server(self, process=False, auto=False): return None def close_port(self): - ret = close_port_wrapper('elastic_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("elastic_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('elastic_server', self.uuid, self.process) + ret = kill_server_wrapper("elastic_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from elasticsearch import Elasticsearch + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password - es = Elasticsearch(['https://{}:{}'.format(_ip, _port)], http_auth=(_username, _password), verify_certs=False) - es.search(index='test', body={}, size=99) + es = Elasticsearch( + [f"https://{_ip}:{_port}"], + http_auth=(_username, _password), + verify_certs=False, + ) + es.search(index="test", body={}, size=99) -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qelasticserver = QElasticServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qelasticserver = QElasticServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qelasticserver.run_server() diff --git a/honeypots/ftp_server.py b/honeypots/ftp_server.py index f463f1f..f810312 100644 --- a/honeypots/ftp_server.py +++ b/honeypots/ftp_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,48 +8,81 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.protocols.ftp import FTPAnonymousShell, FTPFactory, FTP, AUTH_FAILURE, IFTPShell, GUEST_LOGGED_IN_PROCEED, AuthorizationError, BAD_CMD_SEQ, USR_LOGGED_IN_PROCEED +from twisted.protocols.ftp import ( + FTPAnonymousShell, + FTPFactory, + FTP, + IFTPShell, + GUEST_LOGGED_IN_PROCEED, + AuthorizationError, + USR_LOGGED_IN_PROCEED, +) from twisted.internet import reactor, defer from twisted.cred.portal import Portal from twisted.cred import portal, credentials -from twisted.cred.error import UnauthorizedLogin, UnauthorizedLogin, UnhandledCredentials +from twisted.cred.error import UnauthorizedLogin, UnhandledCredentials from twisted.cred.checkers import ICredentialsChecker from zope.interface import implementer from twisted.python import filepath -from twisted.python import log as tlog from random import choice from subprocess import Popen from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from honeypots.helper import ( + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + setup_logger, + set_local_vars, + check_if_server_is_running, +) from uuid import uuid4 from contextlib import suppress from tempfile import TemporaryDirectory -class QFTPServer(): +class QFTPServer: def __init__(self, **kwargs): self.auto_disabled = None - self.mocking_server = choice(['ProFTPD 1.2.10', 'ProFTPD 1.3.4a', 'FileZilla ftp 0.9.43', 'Gene6 ftpd 3.10.0', 'FileZilla ftp 0.9.33', 'ProFTPD 1.2.8']) + self.mocking_server = choice( + [ + "ProFTPD 1.2.10", + "ProFTPD 1.3.4a", + "FileZilla ftp 0.9.43", + "Gene6 ftpd 3.10.0", + "FileZilla ftp 0.9.33", + "ProFTPD 1.2.8", + ] + ) self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 21 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 21 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) self.temp_folder = TemporaryDirectory() - disable_logger(1, tlog) def ftp_server_main(self): _q_s = self @@ -63,7 +96,7 @@ def requestAvatar(self, avatarId, mind, *interfaces): for iface in interfaces: if iface is IFTPShell: avatar = FTPAnonymousShell(self.anonymousRoot) - return IFTPShell, avatar, getattr(avatar, 'logout', lambda: None) + return IFTPShell, avatar, getattr(avatar, "logout", lambda: None) raise NotImplementedError("Only IFTPShell interface is supported by this realm") @implementer(ICredentialsChecker) @@ -87,7 +120,6 @@ def requestAvatarId(self, credentials): return defer.fail(UnauthorizedLogin()) class CustomFTPProtocol(FTP): - def check_bytes(self, string): if isinstance(string, bytes): return string.decode() @@ -95,7 +127,16 @@ def check_bytes(self, string): return str(string) def connectionMade(self): - _q_s.logs.info({'server': 'ftp_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "ftp_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) self.state = self.UNAUTH self.setTimeout(self.timeOut) self.reply("220.2", self.factory.welcomeMessage) @@ -103,18 +144,43 @@ def connectionMade(self): def processCommand(self, cmd, *params): with suppress(Exception): if "capture_commands" in _q_s.options: - _q_s.logs.info({'server': 'ftp_server', 'action': 'command', 'data': {"cmd": self.check_bytes(cmd.upper()), "args": self.check_bytes(params)}, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "ftp_server", + "action": "command", + "data": { + "cmd": self.check_bytes(cmd.upper()), + "args": self.check_bytes(params), + }, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) return super().processCommand(cmd, *params) def ftp_PASS(self, password): username = self.check_bytes(self._user) password = self.check_bytes(password) - status = 'failed' + status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'ftp_server', 'action': 'login', 'status': status, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + status = "success" + _q_s.logs.info( + { + "server": "ftp_server", + "action": "login", + "status": status, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) if self.factory.allowAnonymous and self._user == self.factory.userAnonymous: creds = credentials.Anonymous() @@ -149,7 +215,7 @@ def _ebLogin(failure): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -161,13 +227,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' + status = "success" - self.logs.info({'server': 'ftp_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) + self.logs.info( + { + "server": "ftp_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) - if status == 'success': + if status == "success": return True else: self.kill_server() @@ -177,16 +275,17 @@ def run_server(self, process=False, auto=False): return None def close_port(self): - ret = close_port_wrapper('ftp_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("ftp_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('ftp_server', self.uuid, self.process) + ret = kill_server_wrapper("ftp_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from ftplib import FTP as FFTP + _ip = ip or self.ip _port = port or self.port _username = username or self.username @@ -198,8 +297,15 @@ def test_server(self, ip=None, port=None, username=None, password=None): f.quit() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - ftpserver = QFTPServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + ftpserver = QFTPServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) ftpserver.run_server() diff --git a/honeypots/helper.py b/honeypots/helper.py index 72590f9..ef48867 100644 --- a/honeypots/helper.py +++ b/honeypots/helper.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,104 +8,112 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - +""" +import logging import sys - -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*requests.*') - -from psutil import process_iter -from signal import SIGTERM from argparse import ArgumentParser -from socket import socket, AF_INET, SOCK_STREAM -from json import JSONEncoder, dumps, load -from logging import Handler, Formatter, DEBUG, getLogger, addLevelName, INFO, Logger -from sys import stdout +from collections.abc import Mapping +from contextlib import suppress from datetime import datetime +from json import dumps, JSONEncoder, load +from logging import DEBUG, Formatter, getLogger, Handler from logging.handlers import RotatingFileHandler, SysLogHandler +from os import getuid, makedirs, path, scandir +from pathlib import Path +from signal import SIGTERM +from socket import AF_INET, SOCK_STREAM, socket +from sqlite3 import connect as sqlite3_connect +from sys import stdout from tempfile import _get_candidate_names, gettempdir -from os import makedirs, path, scandir, devnull, getuid -from psycopg2 import sql -from psycopg2 import connect as psycopg2_connect from time import sleep -from traceback import format_exc -from collections.abc import Mapping from urllib.parse import urlparse -from sqlite3 import connect as sqlite3_connect -from pathlib import Path -from contextlib import suppress -old_stderr = sys.stderr -sys.stderr = open(devnull, 'w') +from psutil import process_iter +from psycopg2 import connect as psycopg2_connect, sql -def check_privileges(): +def is_privileged(): with suppress(Exception): return getuid() == 0 with suppress(Exception): import ctypes + return ctypes.windll.shell32.IsUserAnAdmin() != 0 return False +def set_up_error_logging(): + _logger = logging.getLogger("honeypots.error") + if not _logger.handlers: + _logger.setLevel(logging.INFO) + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(logging.INFO) + formatter = logging.Formatter("[%(levelname)s] %(message)s") + handler.setFormatter(formatter) + _logger.addHandler(handler) + return _logger + + def set_local_vars(self, config): - with suppress(Exception): - honeypot = None - if config and config != '': + try: + if config: with open(config) as f: config_data = load(f) - honeypots = config_data['honeypots'] + honeypots = config_data.get("honeypots", []) honeypot = self.__class__.__name__[1:-6].lower() if honeypot and honeypot in honeypots: - for var in honeypots[honeypot]: - setattr(self, var, honeypots[honeypot][var]) - if var == 'port': - setattr(self, 'auto_disabled', True) + for attr, value in honeypots[honeypot].items(): + setattr(self, attr, value) + if attr == "port": + self.auto_disabled = True + except Exception as error: + logging.exception(f"Setting local variables failed: {error}") def parse_record(record, custom_filter, type_): - timestamp = {'timestamp': datetime.utcnow().isoformat()} + timestamp = {"timestamp": datetime.utcnow().isoformat()} try: if custom_filter is not None: - if 'remove_errors' in custom_filter['honeypots']['options']: - if 'error' in record.msg: + if "remove_errors" in custom_filter["honeypots"]["options"]: + if "error" in record.msg: return None if isinstance(record.msg, Mapping): - if 'remove_init' in custom_filter['honeypots']['options']: - if record.msg.get('action', None) == 'process': + if "remove_init" in custom_filter["honeypots"]["options"]: + if record.msg.get("action", None) == "process": return None - if 'remove_word_server' in custom_filter['honeypots']['options']: - if 'server' in record.msg: - record.msg['server'] = record.msg['server'].replace('_server', '') - if 'honeypots' in custom_filter: + if "remove_word_server" in custom_filter["honeypots"]["options"]: + if "server" in record.msg: + record.msg["server"] = record.msg["server"].replace("_server", "") + if "honeypots" in custom_filter: for key in record.msg.copy(): - if key in custom_filter['honeypots']['change']: - record.msg[custom_filter['honeypots']['change'][key]] = record.msg.pop(key) + if key in custom_filter["honeypots"]["change"]: + record.msg[custom_filter["honeypots"]["change"][key]] = record.msg.pop( + key + ) for key in record.msg.copy(): - if key in custom_filter['honeypots']['remove']: + if key in custom_filter["honeypots"]["remove"]: del record.msg[key] - if custom_filter['honeypots']['contains']: - if not all(k in record.msg for k in custom_filter['honeypots']['contains']): + if custom_filter["honeypots"]["contains"]: + if not all(k in record.msg for k in custom_filter["honeypots"]["contains"]): return None if isinstance(record.msg, Mapping): record.msg = serialize_object({**timestamp, **record.msg}) else: record.msg = serialize_object(record.msg) except Exception as e: - record.msg = serialize_object({'name': record.name, 'error': repr(e)}) + record.msg = serialize_object({"name": record.name, "error": repr(e)}) with suppress(Exception): - if type_ == 'file': + if type_ == "file": if custom_filter is not None: - if 'dump_json_to_file' in custom_filter['honeypots']['options']: + if "dump_json_to_file" in custom_filter["honeypots"]["options"]: record.msg = dumps(record.msg, sort_keys=True, cls=ComplexEncoder) - elif type_ == 'db_postgres': + elif type_ == "db_postgres": pass - elif type_ == 'db_sqlite': - for item in ['data', 'error']: + elif type_ == "db_sqlite": + for item in ["data", "error"]: if item in record.msg: if not isinstance(record.msg[item], str): - record.msg[item] = repr(record.msg[item]).replace('\x00', ' ') + record.msg[item] = repr(record.msg[item]).replace("\x00", " ") else: record.msg = dumps(record.msg, sort_keys=True, cls=ComplexEncoder) return record @@ -114,87 +122,123 @@ def parse_record(record, custom_filter, type_): def get_running_servers(): temp_list = [] with suppress(Exception): - honeypots = ['QDNSServer', 'QFTPServer', 'QHTTPProxyServer', 'QHTTPServer', 'QHTTPSServer', 'QIMAPServer', 'QMysqlServer', 'QPOP3Server', 'QPostgresServer', 'QRedisServer', 'QSMBServer', 'QSMTPServer', 'QSOCKS5Server', 'QSSHServer', 'QTelnetServer', 'QVNCServer', 'QElasticServer', 'QMSSQLServer', 'QLDAPServer', 'QNTPServer', 'QMemcacheServer', 'QOracleServer', 'QSNMPServer'] + honeypots = [ + "QDNSServer", + "QFTPServer", + "QHTTPProxyServer", + "QHTTPServer", + "QHTTPSServer", + "QIMAPServer", + "QMysqlServer", + "QPOP3Server", + "QPostgresServer", + "QRedisServer", + "QSMBServer", + "QSMTPServer", + "QSOCKS5Server", + "QSSHServer", + "QTelnetServer", + "QVNCServer", + "QElasticServer", + "QMSSQLServer", + "QLDAPServer", + "QNTPServer", + "QMemcacheServer", + "QOracleServer", + "QSNMPServer", + ] for process in process_iter(): - cmdline = ' '.join(process.cmdline()) + cmdline = " ".join(process.cmdline()) for honeypot in honeypots: - if '--custom' in cmdline and honeypot in cmdline: - temp_list.append(cmdline.split(' --custom ')[1]) + if "--custom" in cmdline and honeypot in cmdline: + temp_list.append(cmdline.split(" --custom ")[1]) return temp_list def disable_logger(logger_type, object): if logger_type == 1: temp_name = path.join(gettempdir(), next(_get_candidate_names())) - object.startLogging(open(temp_name, 'w'), setStdout=False) + object.startLogging(open(temp_name, "w"), setStdout=False) def setup_logger(name, temp_name, config, drop=False): - logs = 'terminal' - logs_location = '' - syslog_address = '' - syslog_facility = '' + logs = "terminal" + logs_location = "" + syslog_address = "" + syslog_facility = "" config_data = None custom_filter = None - if config and config != '': + if config and config != "": with suppress(Exception): with open(config) as f: config_data = load(f) - logs = config_data.get('logs', logs) - logs_location = config_data.get('logs_location', logs_location) - syslog_address = config_data.get('syslog_address', syslog_address) - syslog_facility = config_data.get('syslog_facility', syslog_facility) - custom_filter = config_data.get('custom_filter', custom_filter) - if logs_location == '' or logs_location is None: - logs_location = path.join(gettempdir(), 'logs') + logs = config_data.get("logs", logs) + logs_location = config_data.get("logs_location", logs_location) + syslog_address = config_data.get("syslog_address", syslog_address) + syslog_facility = config_data.get("syslog_facility", syslog_facility) + custom_filter = config_data.get("custom_filter", custom_filter) + if logs_location == "" or logs_location is None: + logs_location = path.join(gettempdir(), "logs") if not path.exists(logs_location): makedirs(logs_location) file_handler = None ret_logs_obj = getLogger(temp_name) ret_logs_obj.setLevel(DEBUG) - if 'db_postgres' in logs or 'db_sqlite' in logs: + if "db_postgres" in logs or "db_sqlite" in logs: ret_logs_obj.addHandler(CustomHandler(temp_name, logs, custom_filter, config_data, drop)) - elif 'terminal' in logs: + elif "terminal" in logs: ret_logs_obj.addHandler(CustomHandler(temp_name, logs, custom_filter)) - if 'file' in logs: + if "file" in logs: max_bytes = 10000 backup_count = 10 with suppress(Exception): if config_data is not None: - if 'honeypots' in config_data: - temp_server_name = name[1:].lower().replace('server', '') - if temp_server_name in config_data['honeypots']: - if 'log_file_name' in config_data['honeypots'][temp_server_name]: - temp_name = config_data['honeypots'][temp_server_name]['log_file_name'] - if 'max_bytes' in config_data['honeypots'][temp_server_name]: - max_bytes = config_data['honeypots'][temp_server_name]['max_bytes'] - if 'backup_count' in config_data['honeypots'][temp_server_name]: - backup_count = config_data['honeypots'][temp_server_name]['backup_count'] - file_handler = CustomHandlerFileRotate(temp_name, logs, custom_filter, path.join(logs_location, temp_name), maxBytes=max_bytes, backupCount=backup_count) + if "honeypots" in config_data: + temp_server_name = name[1:].lower().replace("server", "") + if temp_server_name in config_data["honeypots"]: + if "log_file_name" in config_data["honeypots"][temp_server_name]: + temp_name = config_data["honeypots"][temp_server_name]["log_file_name"] + if "max_bytes" in config_data["honeypots"][temp_server_name]: + max_bytes = config_data["honeypots"][temp_server_name]["max_bytes"] + if "backup_count" in config_data["honeypots"][temp_server_name]: + backup_count = config_data["honeypots"][temp_server_name][ + "backup_count" + ] + file_handler = CustomHandlerFileRotate( + temp_name, + logs, + custom_filter, + path.join(logs_location, temp_name), + maxBytes=max_bytes, + backupCount=backup_count, + ) ret_logs_obj.addHandler(file_handler) - if 'syslog' in logs: - if syslog_address == '': - address = ('localhost', 514) + if "syslog" in logs: + if syslog_address == "": + address = ("localhost", 514) else: - address = (syslog_address.split('//')[1].split(':')[0], int(syslog_address.split('//')[1].split(':')[1])) + address = ( + syslog_address.split("//")[1].split(":")[0], + int(syslog_address.split("//")[1].split(":")[1]), + ) syslog = SysLogHandler(address=address, facility=syslog_facility) - formatter = Formatter('[%(name)s] [%(levelname)s] - %(message)s') + formatter = Formatter("[%(name)s] [%(levelname)s] - %(message)s") syslog.setFormatter(formatter) ret_logs_obj.addHandler(syslog) return ret_logs_obj def clean_all(): - for entry in scandir('.'): - if entry.is_file() and entry.name.endswith('_server.py'): + for entry in scandir("."): + if entry.is_file() and entry.name.endswith("_server.py"): kill_servers(entry.name) def kill_servers(name): with suppress(Exception): for process in process_iter(): - cmdline = ' '.join(process.cmdline()) - if '--custom' in cmdline and name in cmdline: + cmdline = " ".join(process.cmdline()) + if "--custom" in cmdline and name in cmdline: process.send_signal(SIGTERM) process.kill() @@ -202,8 +246,8 @@ def kill_servers(name): def check_if_server_is_running(uuid): with suppress(Exception): for process in process_iter(): - cmdline = ' '.join(process.cmdline()) - if '--custom' in cmdline and uuid in cmdline: + cmdline = " ".join(process.cmdline()) + if "--custom" in cmdline and uuid in cmdline: return True return False @@ -213,8 +257,8 @@ def kill_server_wrapper(server_name, name, process): if process is not None: process.kill() for process in process_iter(): - cmdline = ' '.join(process.cmdline()) - if '--custom' in cmdline and name in cmdline: + cmdline = " ".join(process.cmdline()) + if "--custom" in cmdline and name in cmdline: process.send_signal(SIGTERM) process.kill() return True @@ -225,7 +269,7 @@ def get_free_port(): port = 0 with suppress(Exception): tcp = socket(AF_INET, SOCK_STREAM) - tcp.bind(('', 0)) + tcp.bind(("", 0)) addr, port = tcp.getsockname() tcp.close() return port @@ -238,7 +282,7 @@ def close_port_wrapper(server_name, ip, port, logs): if sock.connect_ex((ip, port)) == 0: for process in process_iter(): with suppress(Exception): - for conn in process.connections(kind='inet'): + for conn in process.connections(kind="inet"): if port == conn.laddr.port: process.send_signal(SIGTERM) process.kill() @@ -249,18 +293,18 @@ def close_port_wrapper(server_name, ip, port, logs): if sock.connect_ex((ip, port)) != 0 and ret: return True else: - logs.error({'server': server_name, 'error': 'port_open.. {} still open..'.format(ip)}) + logs.error({"server": server_name, "error": f"port_open.. {ip} still open.."}) return False class ComplexEncoder(JSONEncoder): def default(self, obj): - return repr(obj).replace('\x00', ' ') + return repr(obj).replace("\x00", " ") class ComplexEncoder_db(JSONEncoder): def default(self, obj): - return 'Something wrong, deleted..' + return "Something wrong, deleted.." def serialize_object(_dict): @@ -271,81 +315,117 @@ def serialize_object(_dict): elif isinstance(_dict, (int, float)): return str(_dict) elif isinstance(_dict, str): - return _dict.replace('\x00', ' ') + return _dict.replace("\x00", " ") elif isinstance(_dict, bytes): - return _dict.decode('utf-8', 'ignore').replace('\x00', ' ') + return _dict.decode("utf-8", "ignore").replace("\x00", " ") else: - return repr(_dict).replace('\x00', ' ') + return repr(_dict).replace("\x00", " ") class CustomHandlerFileRotate(RotatingFileHandler): - def __init__(self, uuid='', logs='', custom_filter=None, filename='', mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False, errors=None): + def __init__( + self, + uuid="", + logs="", + custom_filter=None, + filename="", + mode="a", + maxBytes=0, + backupCount=0, + encoding=None, + delay=False, + errors=None, + ): self.logs = logs self.custom_filter = custom_filter RotatingFileHandler.__init__(self, filename, mode, maxBytes, backupCount, encoding, delay) def emit(self, record): - _record = parse_record(record, self.custom_filter, 'file') + _record = parse_record(record, self.custom_filter, "file") if _record is not None: super().emit(_record) class CustomHandler(Handler): - def __init__(self, uuid='', logs='', custom_filter=None, config=None, drop=False): - self.db = {'db_postgres': None, 'db_sqlite': None} + def __init__(self, uuid="", logs="", custom_filter=None, config=None, drop=False): + self.db = {"db_postgres": None, "db_sqlite": None} self.logs = logs self.uuid = uuid self.custom_filter = custom_filter - if config and config != '' and 'db_postgres' in self.logs: - parsed = urlparse(config['postgres']) - self.db['db_postgres'] = postgres_class(host=parsed.hostname, port=parsed.port, username=parsed.username, password=parsed.password, db=parsed.path[1:], uuid=self.uuid, drop=drop) - if config and config != '' and 'db_sqlite' in self.logs: - self.db['db_sqlite'] = sqlite_class(file=config["sqlite_file"], drop=drop, uuid=self.uuid) + if config and config != "" and "db_postgres" in self.logs: + parsed = urlparse(config["postgres"]) + self.db["db_postgres"] = postgres_class( + host=parsed.hostname, + port=parsed.port, + username=parsed.username, + password=parsed.password, + db=parsed.path[1:], + uuid=self.uuid, + drop=drop, + ) + if config and config != "" and "db_sqlite" in self.logs: + self.db["db_sqlite"] = sqlite_class( + file=config["sqlite_file"], drop=drop, uuid=self.uuid + ) Handler.__init__(self) def emit(self, record): try: - if 'db_postgres' in self.logs: - if self.db['db_postgres']: + if "db_postgres" in self.logs: + if self.db["db_postgres"]: if isinstance(record.msg, list): - if record.msg[0] == 'sniffer' or record.msg[0] == 'errors': - self.db['db_postgres'].insert_into_data_safe(record.msg[0], dumps(serialize_object(record.msg[1]), cls=ComplexEncoder)) + if record.msg[0] == "sniffer" or record.msg[0] == "errors": + self.db["db_postgres"].insert_into_data_safe( + record.msg[0], + dumps(serialize_object(record.msg[1]), cls=ComplexEncoder), + ) elif isinstance(record.msg, Mapping): - if 'server' in record.msg: - self.db['db_postgres'].insert_into_data_safe('servers', dumps(serialize_object(record.msg), cls=ComplexEncoder)) - if 'db_sqlite' in self.logs: - _record = parse_record(record, self.custom_filter, 'db_sqlite') + if "server" in record.msg: + self.db["db_postgres"].insert_into_data_safe( + "servers", dumps(serialize_object(record.msg), cls=ComplexEncoder) + ) + if "db_sqlite" in self.logs: + _record = parse_record(record, self.custom_filter, "db_sqlite") if _record: - self.db['db_sqlite'].insert_into_data_safe(_record.msg) - if 'terminal' in self.logs: - _record = parse_record(record, self.custom_filter, 'terminal') + self.db["db_sqlite"].insert_into_data_safe(_record.msg) + if "terminal" in self.logs: + _record = parse_record(record, self.custom_filter, "terminal") if _record: - stdout.write(_record.msg + '\n') - if 'syslog' in self.logs: - _record = parse_record(record, self.custom_filter, 'terminal') + stdout.write(_record.msg + "\n") + if "syslog" in self.logs: + _record = parse_record(record, self.custom_filter, "terminal") if _record: - stdout.write(_record.msg + '\n') + stdout.write(_record.msg + "\n") except Exception as e: if self.custom_filter is not None: - if 'honeypots' in self.custom_filter: - if 'remove_errors' in self.custom_filter['honeypots']['options']: - return None - stdout.write(dumps({'error': repr(e), 'logger': repr(record)}, sort_keys=True, cls=ComplexEncoder) + '\n') + if "honeypots" in self.custom_filter: + if "remove_errors" in self.custom_filter["honeypots"]["options"]: + return + stdout.write( + dumps( + {"error": repr(e), "logger": repr(record)}, sort_keys=True, cls=ComplexEncoder + ) + + "\n" + ) stdout.flush() -class postgres_class(): - def __init__(self, host=None, port=None, username=None, password=None, db=None, drop=False, uuid=None): +class postgres_class: + def __init__( + self, host=None, port=None, username=None, password=None, db=None, drop=False, uuid=None + ): self.host = host self.port = port self.username = username self.password = password self.db = db self.uuid = uuid - self.mapped_tables = ['errors', 'servers', 'sniffer', 'system'] + self.mapped_tables = ["errors", "servers", "sniffer", "system"] self.wait_until_up() if drop: - self.con = psycopg2_connect(host=self.host, port=self.port, user=self.username, password=self.password) + self.con = psycopg2_connect( + host=self.host, port=self.port, user=self.username, password=self.password + ) self.con.set_isolation_level(0) self.cur = self.con.cursor() self.drop_db() @@ -353,15 +433,23 @@ def __init__(self, host=None, port=None, username=None, password=None, db=None, self.create_db() self.con.close() else: - self.con = psycopg2_connect(host=self.host, port=self.port, user=self.username, password=self.password) + self.con = psycopg2_connect( + host=self.host, port=self.port, user=self.username, password=self.password + ) self.con.set_isolation_level(0) self.cur = self.con.cursor() if not self.check_db_if_exists(): self.create_db() self.con.close() - self.con = psycopg2_connect(host=self.host, port=self.port, user=self.username, password=self.password, database=self.db) + self.con = psycopg2_connect( + host=self.host, + port=self.port, + user=self.username, + password=self.password, + database=self.db, + ) self.con.set_isolation_level(0) - self.con.set_client_encoding('UTF8') + self.con.set_client_encoding("UTF8") self.cur = self.con.cursor() self.create_tables() @@ -369,13 +457,19 @@ def wait_until_up(self): test = True while test: with suppress(Exception): - print('{} - Waiting on postgres connection'.format(self.uuid)) + print(f"{self.uuid} - Waiting on postgres connection") stdout.flush() - conn = psycopg2_connect(host=self.host, port=self.port, user=self.username, password=self.password, connect_timeout=1) + conn = psycopg2_connect( + host=self.host, + port=self.port, + user=self.username, + password=self.password, + connect_timeout=1, + ) conn.close() test = False sleep(1) - print('{} - postgres connection is good'.format(self.uuid)) + print(f"{self.uuid} - postgres connection is good") def addattr(self, x, val): self.__dict__[x] = val @@ -383,50 +477,84 @@ def addattr(self, x, val): def check_db_if_exists(self): exists = False with suppress(Exception): - self.cur.execute('SELECT exists(SELECT 1 from pg_catalog.pg_database where datname = %s)', (self.db,)) + self.cur.execute( + "SELECT exists(SELECT 1 from pg_catalog.pg_database where datname = %s)", + (self.db,), + ) if self.cur.fetchone()[0]: exists = True return exists def drop_db(self): with suppress(Exception): - print('[x] Dropping {} db'.format(self.db)) + print(f"[x] Dropping {self.db} db") if self.check_db_if_exists(): - self.cur.execute(sql.SQL('drop DATABASE IF EXISTS {}').format(sql.Identifier(self.db))) + self.cur.execute( + sql.SQL("drop DATABASE IF EXISTS {}").format(sql.Identifier(self.db)) + ) sleep(2) - self.cur.execute(sql.SQL('CREATE DATABASE {}').format(sql.Identifier(self.db))) + self.cur.execute(sql.SQL("CREATE DATABASE {}").format(sql.Identifier(self.db))) def create_db(self): print("create") - self.cur.execute(sql.SQL('CREATE DATABASE {}').format(sql.Identifier(self.db))) + self.cur.execute(sql.SQL("CREATE DATABASE {}").format(sql.Identifier(self.db))) - def drop_tables(self,): + def drop_tables( + self, + ): for x in self.mapped_tables: - self.cur.execute(sql.SQL('drop TABLE IF EXISTS {}').format(sql.Identifier(x + '_table'))) + self.cur.execute( + sql.SQL("drop TABLE IF EXISTS {}").format(sql.Identifier(x + "_table")) + ) def create_tables(self): for x in self.mapped_tables: - self.cur.execute(sql.SQL('CREATE TABLE IF NOT EXISTS {} (id SERIAL NOT NULL,date timestamp with time zone DEFAULT now(),data json)').format(sql.Identifier(x + '_table'))) + self.cur.execute( + sql.SQL( + "CREATE TABLE IF NOT EXISTS {} (id SERIAL NOT NULL,date timestamp with time zone DEFAULT now(),data json)" + ).format(sql.Identifier(x + "_table")) + ) def insert_into_data_safe(self, table, obj): with suppress(Exception): - self.cur.execute(sql.SQL('INSERT INTO {} (id,date, data) VALUES (DEFAULT ,now(), %s)').format(sql.Identifier(table + '_table')), [obj]) + self.cur.execute( + sql.SQL("INSERT INTO {} (id,date, data) VALUES (DEFAULT ,now(), %s)").format( + sql.Identifier(table + "_table") + ), + [obj], + ) -class sqlite_class(): +class sqlite_class: def __init__(self, file=None, drop=False, uuid=None): self.file = file self.uuid = uuid - self.mapped_tables = ['servers'] - self.servers_table_template = {'server': 'servers_table', 'action': None, 'status': None, 'src_ip': None, 'src_port': None, 'username': None, 'password': None, 'dest_ip': None, 'dest_port': None, 'data': None, 'error': None} + self.mapped_tables = ["servers"] + self.servers_table_template = { + "server": "servers_table", + "action": None, + "status": None, + "src_ip": None, + "src_port": None, + "username": None, + "password": None, + "dest_ip": None, + "dest_port": None, + "data": None, + "error": None, + } self.wait_until_up() if drop: - self.con = sqlite3_connect(self.file, timeout=1, isolation_level=None, check_same_thread=False) + self.con = sqlite3_connect( + self.file, timeout=1, isolation_level=None, check_same_thread=False + ) self.cur = self.con.cursor() self.drop_db() self.drop_tables() self.con.close() - self.con = sqlite3_connect(self.file, timeout=1, isolation_level=None, check_same_thread=False) + self.con = sqlite3_connect( + self.file, timeout=1, isolation_level=None, check_same_thread=False + ) self.cur = self.con.cursor() self.create_tables() @@ -434,21 +562,21 @@ def wait_until_up(self): test = True while test: with suppress(Exception): - print('{} - Waiting on sqlite connection'.format(self.uuid)) + print(f"{self.uuid} - Waiting on sqlite connection") conn = sqlite3_connect(self.file, timeout=1, check_same_thread=False) conn.close() test = False sleep(1) - print('{} - sqlite connection is good'.format(self.uuid)) + print(f"{self.uuid} - sqlite connection is good") def drop_db_test(self): with suppress(Exception): file_exists = False sql_file = False - with open(self.file, 'rb') as f: + with open(self.file, "rb") as f: file_exists = True header = f.read(100) - if header[:16] == b'SQLite format 3\x00': + if header[:16] == b"SQLite format 3\x00": sql_file = True if sql_file: print("yes") @@ -458,43 +586,104 @@ def drop_db(self): file = Path(self.file) file.unlink(missing_ok=False) - def drop_tables(self,): + def drop_tables( + self, + ): with suppress(Exception): for x in self.mapped_tables: - self.cur.execute("DROP TABLE IF EXISTS '{:s}'".format(x)) + self.cur.execute(f"DROP TABLE IF EXISTS '{x:s}'") def create_tables(self): with suppress(Exception): - self.cur.execute("CREATE TABLE IF NOT EXISTS '{:s}' (id INTEGER PRIMARY KEY,date DATETIME DEFAULT CURRENT_TIMESTAMP,server text, action text, status text, src_ip text, src_port text,dest_ip text, dest_port text, username text, password text, data text, error text)".format('servers_table')) + self.cur.execute( + "CREATE TABLE IF NOT EXISTS '{:s}' (id INTEGER PRIMARY KEY,date DATETIME DEFAULT CURRENT_TIMESTAMP,server text, action text, status text, src_ip text, src_port text,dest_ip text, dest_port text, username text, password text, data text, error text)".format( + "servers_table" + ) + ) def insert_into_data_safe(self, obj): with suppress(Exception): parsed = {k: v for k, v in obj.items() if v is not None} dict_ = {**self.servers_table_template, **parsed} - self.cur.execute("INSERT INTO servers_table (server, action, status, src_ip, src_port, dest_ip, dest_port, username, password, data, error) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (dict_['server'], dict_['action'], dict_['status'], dict_['src_ip'], dict_['src_port'], dict_['dest_ip'], dict_['dest_port'], dict_['username'], dict_['password'], dict_['data'], dict_['error'])) + self.cur.execute( + "INSERT INTO servers_table (server, action, status, src_ip, src_port, dest_ip, dest_port, username, password, data, error) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + ( + dict_["server"], + dict_["action"], + dict_["status"], + dict_["src_ip"], + dict_["src_port"], + dict_["dest_ip"], + dict_["dest_port"], + dict_["username"], + dict_["password"], + dict_["data"], + dict_["error"], + ), + ) def server_arguments(): - _server_parser = ArgumentParser(prog='Server') - _server_parsergroupdeq = _server_parser.add_argument_group('Initialize Server') - _server_parsergroupdeq.add_argument('--ip', type=str, help='Change server ip, current is 0.0.0.0', required=False, metavar='') - _server_parsergroupdeq.add_argument('--port', type=int, help='Change port', required=False, metavar='') - _server_parsergroupdeq.add_argument('--username', type=str, help='Change username', required=False, metavar='') - _server_parsergroupdeq.add_argument('--password', type=str, help='Change password', required=False, metavar='') - _server_parsergroupdeq.add_argument('--resolver_addresses', type=str, help='Change resolver address', required=False, metavar='') - _server_parsergroupdeq.add_argument('--domain', type=str, help='A domain to test', required=False, metavar='') - _server_parsergroupdeq.add_argument('--folders', type=str, help='folders for smb as name:target,name:target', required=False, metavar='') - _server_parsergroupdeq.add_argument('--options', type=str, help='Extra options', metavar='', default='') - _server_parsergroupdes = _server_parser.add_argument_group('Sinffer options') - _server_parsergroupdes.add_argument('--filter', type=str, help='setup the Sinffer filter', required=False) - _server_parsergroupdes.add_argument('--interface', type=str, help='sinffer interface E.g eth0', required=False) - _server_parsergroupdef = _server_parser.add_argument_group('Initialize Loging') - _server_parsergroupdef.add_argument('--config', type=str, help='config file for logs and database', required=False, default='') - _server_parsergroupdea = _server_parser.add_argument_group('Auto Configuration') - _server_parsergroupdea.add_argument('--docker', action='store_true', help='Run project in docker', required=False) - _server_parsergroupdea.add_argument('--aws', action='store_true', help='Run project in aws', required=False) - _server_parsergroupdea.add_argument('--test', action='store_true', help='Test current server', required=False) - _server_parsergroupdea.add_argument('--custom', action='store_true', help='Run custom server', required=False) - _server_parsergroupdea.add_argument('--auto', action='store_true', help='Run auto configured with random port', required=False) - _server_parsergroupdef.add_argument('--uuid', type=str, help='unique id', required=False) + _server_parser = ArgumentParser(prog="Server") + _server_parsergroupdeq = _server_parser.add_argument_group("Initialize Server") + _server_parsergroupdeq.add_argument( + "--ip", type=str, help="Change server ip, current is 0.0.0.0", required=False, metavar="" + ) + _server_parsergroupdeq.add_argument( + "--port", type=int, help="Change port", required=False, metavar="" + ) + _server_parsergroupdeq.add_argument( + "--username", type=str, help="Change username", required=False, metavar="" + ) + _server_parsergroupdeq.add_argument( + "--password", type=str, help="Change password", required=False, metavar="" + ) + _server_parsergroupdeq.add_argument( + "--resolver_addresses", + type=str, + help="Change resolver address", + required=False, + metavar="", + ) + _server_parsergroupdeq.add_argument( + "--domain", type=str, help="A domain to test", required=False, metavar="" + ) + _server_parsergroupdeq.add_argument( + "--folders", + type=str, + help="folders for smb as name:target,name:target", + required=False, + metavar="", + ) + _server_parsergroupdeq.add_argument( + "--options", type=str, help="Extra options", metavar="", default="" + ) + _server_parsergroupdes = _server_parser.add_argument_group("Sinffer options") + _server_parsergroupdes.add_argument( + "--filter", type=str, help="setup the Sinffer filter", required=False + ) + _server_parsergroupdes.add_argument( + "--interface", type=str, help="sinffer interface E.g eth0", required=False + ) + _server_parsergroupdef = _server_parser.add_argument_group("Initialize Loging") + _server_parsergroupdef.add_argument( + "--config", type=str, help="config file for logs and database", required=False, default="" + ) + _server_parsergroupdea = _server_parser.add_argument_group("Auto Configuration") + _server_parsergroupdea.add_argument( + "--docker", action="store_true", help="Run project in docker", required=False + ) + _server_parsergroupdea.add_argument( + "--aws", action="store_true", help="Run project in aws", required=False + ) + _server_parsergroupdea.add_argument( + "--test", action="store_true", help="Test current server", required=False + ) + _server_parsergroupdea.add_argument( + "--custom", action="store_true", help="Run custom server", required=False + ) + _server_parsergroupdea.add_argument( + "--auto", action="store_true", help="Run auto configured with random port", required=False + ) + _server_parsergroupdef.add_argument("--uuid", type=str, help="unique id", required=False) return _server_parser.parse_args() diff --git a/honeypots/honeypots b/honeypots/honeypots deleted file mode 100644 index 9770d97..0000000 --- a/honeypots/honeypots +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python -from importlib import import_module -if __name__ == '__main__': - module = import_module('honeypots') - module.main_logic() diff --git a/honeypots/http_proxy_server.py b/honeypots/http_proxy_server.py index 59cac7d..66c63f2 100644 --- a/honeypots/http_proxy_server.py +++ b/honeypots/http_proxy_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,20 +8,28 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' -from pathlib import Path -from warnings import filterwarnings +""" +from __future__ import annotations -filterwarnings(action='ignore', module='.*OpenSSL.*') +from pathlib import Path +from shlex import split -from dns.resolver import query as dsnquery +from dns.resolver import resolve as dsnquery from twisted.internet import reactor from twisted.internet.protocol import Protocol, Factory -from twisted.python import log as tlog from subprocess import Popen from email.parser import BytesParser -from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from os import getenv +from honeypots.helper import ( + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_up_error_logging, + setup_logger, + set_local_vars, + check_if_server_is_running, +) from uuid import uuid4 from contextlib import suppress @@ -29,46 +37,88 @@ DUMMY_TEMPLATE = (Path(__file__).parent / "data" / "dummy_page.html").read_text() -class QHTTPProxyServer(): +class QHTTPProxyServer: + NAME = "http_proxy_server" + def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") + self.template: str | None = None if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 8080 - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 8080 + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) + self.logger = set_up_error_logging() + self.template_contents: str | None = self._load_template() + + def _load_template(self) -> str | None: + if self.template: + try: + template_contents = Path(self.template).read_text(errors="replace") + self.logger.debug( + f"[{self.NAME}]: Successfully loaded custom template {self.template}" + ) + return template_contents + except FileNotFoundError: + self.logger.error(f"[{self.NAME}]: Template file {self.template} not found") + return None def http_proxy_server_main(self): _q_s = self class CustomProtocolParent(Protocol): - def __init__(self): self.buffer = None self.client = None def resolve_domain(self, request_string): with suppress(Exception): - _, parsed_request = request_string.split(b'\r\n', 1) + _, parsed_request = request_string.split(b"\r\n", 1) headers = BytesParser().parsebytes(parsed_request) - host = headers['host'].split(':') - _q_s.logs.info({'server': 'http_proxy_server', 'action': 'query', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': host[0]}) - # return '127.0.0.1' - return dsnquery(host[0], 'A')[0].address + host = headers["host"].split(":") + _q_s.logs.info( + { + "server": _q_s.NAME, + "action": "query", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": host[0], + } + ) + return dsnquery(host[0], "A")[0].address return None - def dataReceived(self, data): - _q_s.logs.info({'server': 'http_proxy_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + def dataReceived(self, data): # noqa: N802 + _q_s.logs.info( + { + "server": _q_s.NAME, + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) ip = self.resolve_domain(data) if ip: - self.write(_create_dummy_response(DUMMY_TEMPLATE)) + self.write(_create_dummy_response(_q_s.template_contents or DUMMY_TEMPLATE)) else: self.transport.loseConnection() @@ -80,56 +130,67 @@ def dataReceived(self, data): def write(self, data): self.transport.write(data) - def write(self, data): - self.transport.write(data) - factory = Factory() factory.protocol = CustomProtocolParent reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - status = 'error' + def run_server(self, process=False, auto=False) -> bool | None: + status = "error" run = False - if process: - if auto and not self.auto_disabled: - port = get_free_port() - if port > 0: - self.port = port - run = True - elif self.close_port() and self.kill_server(): - run = True - - if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) - if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'http_proxy_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': - return True - else: - self.kill_server() - return False - else: + if not process: self.http_proxy_server_main() + return None + + if auto and not self.auto_disabled: + port = get_free_port() + if port > 0: + self.port = port + run = True + elif self.close_port() and self.kill_server(): + run = True + + if run: + self.process = Popen( + split( + f"python3 {Path(__file__)} --custom --ip {self.ip} --port {self.port} " + f"--options '{self.options}' --config '{self.config}' --uuid {self.uuid}" + ) + ) + if self.process.poll() is None and check_if_server_is_running(self.uuid): + status = "success" + + self.logs.info( + { + "server": self.NAME, + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": + return True + self.kill_server() + return False def close_port(self): - ret = close_port_wrapper('http_proxy_server', self.ip, self.port, self.logs) - return ret + return close_port_wrapper(self.NAME, self.ip, self.port, self.logs) def kill_server(self): - ret = kill_server_wrapper('http_proxy_server', self.uuid, self.process) - return ret + return kill_server_wrapper(self.NAME, self.uuid, self.process) def test_server(self, ip=None, port=None, domain=None): with suppress(Exception): from requests import get + _ip = ip or self.ip _port = port or self.port - _domain = domain or 'http://yahoo.com' - get(_domain, proxies={'http': 'http://{}:{}'.format(_ip, _port)}).text.encode('ascii', 'ignore') + _domain = domain or "http://yahoo.com" + get(_domain, proxies={"http": f"http://{_ip}:{_port}"}).text.encode("ascii", "ignore") def _create_dummy_response(content: str) -> bytes: @@ -142,8 +203,10 @@ def _create_dummy_response(content: str) -> bytes: return "\r\n".join(response).encode() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qhttpproxyserver = QHTTPProxyServer(ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config) + qhttpproxyserver = QHTTPProxyServer( + ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config + ) qhttpproxyserver.run_server() diff --git a/honeypots/http_server.py b/honeypots/http_server.py index c6adc51..494b1c3 100644 --- a/honeypots/http_server.py +++ b/honeypots/http_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,57 +8,93 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') +""" from cgi import FieldStorage -from requests.packages.urllib3 import disable_warnings -from twisted.internet import reactor -from twisted.web.server import Site -from twisted.web.resource import Resource -from twisted.python import log as tlog +from contextlib import suppress +from os import getenv, path from random import choice -from tempfile import gettempdir, _get_candidate_names from subprocess import Popen -from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from tempfile import _get_candidate_names, gettempdir from uuid import uuid4 -from contextlib import suppress -disable_warnings() +from twisted.internet import reactor +from twisted.web.resource import Resource +from twisted.web.server import Site +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QHTTPServer(): + +class QHTTPServer: def __init__(self, **kwargs): self.auto_disabled = None self.key = path.join(gettempdir(), next(_get_candidate_names())) self.cert = path.join(gettempdir(), next(_get_candidate_names())) - self.mocking_server = choice(['Apache', 'nginx', 'Microsoft-IIS/7.5', 'Microsoft-HTTPAPI/2.0', 'Apache/2.2.15', 'SmartXFilter', 'Microsoft-IIS/8.5', 'Apache/2.4.6', 'Apache-Coyote/1.1', 'Microsoft-IIS/7.0', 'Apache/2.4.18', 'AkamaiGHost', 'Apache/2.2.25', 'Microsoft-IIS/10.0', 'Apache/2.2.3', 'nginx/1.12.1', 'Apache/2.4.29', 'cloudflare', 'Apache/2.2.22']) + self.mocking_server = choice( + [ + "Apache", + "nginx", + "Microsoft-IIS/7.5", + "Microsoft-HTTPAPI/2.0", + "Apache/2.2.15", + "SmartXFilter", + "Microsoft-IIS/8.5", + "Apache/2.4.6", + "Apache-Coyote/1.1", + "Microsoft-IIS/7.0", + "Apache/2.4.18", + "AkamaiGHost", + "Apache/2.2.25", + "Microsoft-IIS/10.0", + "Apache/2.2.3", + "nginx/1.12.1", + "Apache/2.4.29", + "cloudflare", + "Apache/2.2.22", + ] + ) self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 80 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 80 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def http_server_main(self): _q_s = self class MainResource(Resource): - isLeaf = True - home_file = b''' + home_file = b""" @@ -79,9 +115,9 @@ class MainResource(Resource): -''' +""" - login_file = b''' + login_file = b""" @@ -110,7 +146,7 @@ class MainResource(Resource): -''' +""" def check_bytes(self, string): if isinstance(string, bytes): @@ -119,80 +155,139 @@ def check_bytes(self, string): return str(string) def render(self, request): - headers = {} client_ip = "" with suppress(Exception): + def check_bytes(string): if isinstance(string, bytes): return string.decode() else: return str(string) + for item, value in dict(request.requestHeaders.getAllRawHeaders()).items(): - headers.update({check_bytes(item): ','.join(map(check_bytes, value))}) - headers.update({'method': check_bytes(request.method)}) - headers.update({'uri': check_bytes(request.uri)}) + headers.update({check_bytes(item): ",".join(map(check_bytes, value))}) + headers.update({"method": check_bytes(request.method)}) + headers.update({"uri": check_bytes(request.uri)}) - if 'fix_get_client_ip' in _q_s.options: + if "fix_get_client_ip" in _q_s.options: with suppress(Exception): raw_headers = dict(request.requestHeaders.getAllRawHeaders()) - if b'X-Forwarded-For' in raw_headers: - client_ip = check_bytes(raw_headers[b'X-Forwarded-For'][0]) - elif b'X-Real-IP' in raw_headers: - client_ip = check_bytes(raw_headers[b'X-Real-IP'][0]) + if b"X-Forwarded-For" in raw_headers: + client_ip = check_bytes(raw_headers[b"X-Forwarded-For"][0]) + elif b"X-Real-IP" in raw_headers: + client_ip = check_bytes(raw_headers[b"X-Real-IP"][0]) if client_ip == "": client_ip = request.getClientAddress().host with suppress(Exception): if "capture_commands" in _q_s.options: - _q_s.logs.info({'server': 'http_server', 'action': 'connection', 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': headers}) + _q_s.logs.info( + { + "server": "http_server", + "action": "connection", + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": headers, + } + ) else: - _q_s.logs.info({'server': 'http_server', 'action': 'connection', 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - - if _q_s.mocking_server != '': - request.responseHeaders.removeHeader('Server') - request.responseHeaders.addRawHeader('Server', _q_s.mocking_server) - - if request.method == b'GET' or request.method == b'POST': - _q_s.logs.info({'server': 'http_server', 'action': request.method.decode(), 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - - if request.method == b'GET': - if request.uri == b'/login.html': - if _q_s.username != '' and _q_s.password != '': - request.responseHeaders.addRawHeader('Content-Type', 'text/html; charset=utf-8') + _q_s.logs.info( + { + "server": "http_server", + "action": "connection", + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + + if _q_s.mocking_server != "": + request.responseHeaders.removeHeader("Server") + request.responseHeaders.addRawHeader("Server", _q_s.mocking_server) + + if request.method == b"GET" or request.method == b"POST": + _q_s.logs.info( + { + "server": "http_server", + "action": request.method.decode(), + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + + if request.method == b"GET": + if request.uri == b"/login.html": + if _q_s.username != "" and _q_s.password != "": + request.responseHeaders.addRawHeader( + "Content-Type", "text/html; charset=utf-8" + ) return self.login_file - request.responseHeaders.addRawHeader('Content-Type', 'text/html; charset=utf-8') + request.responseHeaders.addRawHeader( + "Content-Type", "text/html; charset=utf-8" + ) return self.login_file - elif request.method == b'POST': + elif request.method == b"POST": self.headers = request.getAllHeaders() - if request.uri == b'/login.html' or b'/': - if _q_s.username != '' and _q_s.password != '': - form = FieldStorage(fp=request.content, headers=self.headers, environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers[b'content-type'], }) - if 'username' in form and 'password' in form: - username = self.check_bytes(form['username'].value) - password = self.check_bytes(form['password'].value) - status = 'failed' + if request.uri == b"/login.html" or b"/": + if _q_s.username != "" and _q_s.password != "": + form = FieldStorage( + fp=request.content, + headers=self.headers, + environ={ + "REQUEST_METHOD": "POST", + "CONTENT_TYPE": self.headers.get( + b"content-type", + b"application/x-www-form-urlencoded", + ), + }, + ) + if "username" in form and "password" in form: + username = self.check_bytes(form["username"].value) + password = self.check_bytes(form["password"].value) + status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'http_server', 'action': 'login', 'status': status, 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'username': username, 'password': password, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - - request.responseHeaders.addRawHeader('Content-Type', 'text/html; charset=utf-8') + status = "success" + _q_s.logs.info( + { + "server": "http_server", + "action": "login", + "status": status, + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "username": username, + "password": password, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + + request.responseHeaders.addRawHeader( + "Content-Type", "text/html; charset=utf-8" + ) return self.home_file else: - request.responseHeaders.addRawHeader('Content-Type', 'text/html; charset=utf-8') + request.responseHeaders.addRawHeader( + "Content-Type", "text/html; charset=utf-8" + ) return self.home_file reactor.listenTCP(self.port, Site(MainResource())) reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -204,13 +299,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'http_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "http_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -219,26 +346,37 @@ def run_server(self, process=False, auto=False): self.http_server_main() def close_port(self): - ret = close_port_wrapper('http_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("http_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('http_server', self.uuid, self.process) + ret = kill_server_wrapper("http_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from requests import get, post + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password - get('http://{}:{}'.format(_ip, _port), verify=False) - post('http://{}:{}/login.html'.format(_ip, _port), data={'username': (None, _username), 'password': (None, _password)}) + get(f"http://{_ip}:{_port}", verify=False) + post( + f"http://{_ip}:{_port}/login.html", + data={"username": (None, _username), "password": (None, _password)}, + ) -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qhttpserver = QHTTPServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qhttpserver = QHTTPServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qhttpserver.run_server() diff --git a/honeypots/https_server.py b/honeypots/https_server.py index 30b4d85..ec5e803 100644 --- a/honeypots/https_server.py +++ b/honeypots/https_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,59 +8,96 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from OpenSSL import crypto from cgi import FieldStorage -from requests.packages.urllib3 import disable_warnings -from tempfile import gettempdir, _get_candidate_names -from twisted.internet import reactor, ssl -from twisted.web.server import Site -from twisted.web.resource import Resource +from contextlib import suppress +from os import getenv, path from random import choice -from twisted.python import log as tlog from subprocess import Popen -from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from tempfile import _get_candidate_names, gettempdir from uuid import uuid4 -from contextlib import suppress -disable_warnings() +from OpenSSL import crypto +from twisted.internet import reactor, ssl +from twisted.web.resource import Resource +from twisted.web.server import Site + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QHTTPSServer(): +class QHTTPSServer: def __init__(self, **kwargs): self.auto_disabled = None self.key = path.join(gettempdir(), next(_get_candidate_names())) self.cert = path.join(gettempdir(), next(_get_candidate_names())) - self.mocking_server = choice(['Apache', 'nginx', 'Microsoft-IIS/7.5', 'Microsoft-HTTPAPI/2.0', 'Apache/2.2.15', 'SmartXFilter', 'Microsoft-IIS/8.5', 'Apache/2.4.6', 'Apache-Coyote/1.1', 'Microsoft-IIS/7.0', 'Apache/2.4.18', 'AkamaiGHost', 'Apache/2.2.25', 'Microsoft-IIS/10.0', 'Apache/2.2.3', 'nginx/1.12.1', 'Apache/2.4.29', 'cloudflare', 'Apache/2.2.22']) + self.mocking_server = choice( + [ + "Apache", + "nginx", + "Microsoft-IIS/7.5", + "Microsoft-HTTPAPI/2.0", + "Apache/2.2.15", + "SmartXFilter", + "Microsoft-IIS/8.5", + "Apache/2.4.6", + "Apache-Coyote/1.1", + "Microsoft-IIS/7.0", + "Apache/2.4.18", + "AkamaiGHost", + "Apache/2.2.25", + "Microsoft-IIS/10.0", + "Apache/2.2.3", + "nginx/1.12.1", + "Apache/2.4.29", + "cloudflare", + "Apache/2.2.22", + ] + ) self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 443 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 443 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def CreateCert(self, host_name, key, cert): pk = crypto.PKey() pk.generate_key(crypto.TYPE_RSA, 2048) c = crypto.X509() - c.get_subject().C = 'US' - c.get_subject().ST = 'OR' - c.get_subject().L = 'None' - c.get_subject().O = 'None' - c.get_subject().OU = 'None' + c.get_subject().C = "US" + c.get_subject().ST = "OR" + c.get_subject().L = "None" + c.get_subject().O = "None" + c.get_subject().OU = "None" c.get_subject().CN = next(_get_candidate_names()) c.set_serial_number(0) before, after = (0, 60 * 60 * 24 * 365 * 2) @@ -68,17 +105,16 @@ def CreateCert(self, host_name, key, cert): c.gmtime_adj_notAfter(after) c.set_issuer(c.get_subject()) c.set_pubkey(pk) - c.sign(pk, 'sha256') - open(cert, 'wb').write(crypto.dump_certificate(crypto.FILETYPE_PEM, c)) - open(key, 'wb').write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pk)) + c.sign(pk, "sha256") + open(cert, "wb").write(crypto.dump_certificate(crypto.FILETYPE_PEM, c)) + open(key, "wb").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pk)) def https_server_main(self): _q_s = self class MainResource(Resource): - isLeaf = True - home_file = b''' + home_file = b""" @@ -99,9 +135,9 @@ class MainResource(Resource): -''' +""" - login_file = b''' + login_file = b""" @@ -130,7 +166,7 @@ class MainResource(Resource): -''' +""" def check_bytes(self, string): if isinstance(string, bytes): @@ -139,11 +175,11 @@ def check_bytes(self, string): return str(string) def render(self, request): - headers = {} client_ip = "" with suppress(Exception): + def check_bytes(string): if isinstance(string, bytes): return string.decode() @@ -151,71 +187,129 @@ def check_bytes(string): return str(string) for item, value in dict(request.requestHeaders.getAllRawHeaders()).items(): - headers.update({check_bytes(item): ','.join(map(check_bytes, value))}) - headers.update({'method': check_bytes(request.method)}) - headers.update({'uri': check_bytes(request.uri)}) + headers.update({check_bytes(item): ",".join(map(check_bytes, value))}) + headers.update({"method": check_bytes(request.method)}) + headers.update({"uri": check_bytes(request.uri)}) - if 'fix_get_client_ip' in _q_s.options: + if "fix_get_client_ip" in _q_s.options: with suppress(Exception): raw_headers = dict(request.requestHeaders.getAllRawHeaders()) - if b'X-Forwarded-For' in raw_headers: - client_ip = check_bytes(raw_headers[b'X-Forwarded-For'][0]) - elif b'X-Real-IP' in raw_headers: - client_ip = check_bytes(raw_headers[b'X-Real-IP'][0]) + if b"X-Forwarded-For" in raw_headers: + client_ip = check_bytes(raw_headers[b"X-Forwarded-For"][0]) + elif b"X-Real-IP" in raw_headers: + client_ip = check_bytes(raw_headers[b"X-Real-IP"][0]) if client_ip == "": client_ip = request.getClientAddress().host with suppress(Exception): if "capture_commands" in _q_s.options: - _q_s.logs.info({'server': 'https_server', 'action': 'connection', 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': headers}) + _q_s.logs.info( + { + "server": "https_server", + "action": "connection", + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": headers, + } + ) else: - _q_s.logs.info({'server': 'https_server', 'action': 'connection', 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - - if _q_s.mocking_server != '': - request.responseHeaders.removeHeader('Server') - request.responseHeaders.addRawHeader('Server', _q_s.mocking_server) - - if request.method == b'GET' or request.method == b'POST': - _q_s.logs.info({'server': 'https_server', 'action': request.method.decode(), 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - - if request.method == b'GET': - if request.uri == b'/login.html': - if _q_s.username != '' and _q_s.password != '': - request.responseHeaders.addRawHeader('Content-Type', 'text/html; charset=utf-8') + _q_s.logs.info( + { + "server": "https_server", + "action": "connection", + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + + if _q_s.mocking_server != "": + request.responseHeaders.removeHeader("Server") + request.responseHeaders.addRawHeader("Server", _q_s.mocking_server) + + if request.method == b"GET" or request.method == b"POST": + _q_s.logs.info( + { + "server": "https_server", + "action": request.method.decode(), + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + + if request.method == b"GET": + if request.uri == b"/login.html": + if _q_s.username != "" and _q_s.password != "": + request.responseHeaders.addRawHeader( + "Content-Type", "text/html; charset=utf-8" + ) return self.login_file - request.responseHeaders.addRawHeader('Content-Type', 'text/html; charset=utf-8') + request.responseHeaders.addRawHeader( + "Content-Type", "text/html; charset=utf-8" + ) return self.login_file - elif request.method == b'POST': + elif request.method == b"POST": self.headers = request.getAllHeaders() - if request.uri == b'/login.html' or b'/': - if _q_s.username != '' and _q_s.password != '': - form = FieldStorage(fp=request.content, headers=self.headers, environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers[b'content-type'], }) - if 'username' in form and 'password' in form: - username = self.check_bytes(form['username'].value) - password = self.check_bytes(form['password'].value) - status = 'failed' + if request.uri == b"/login.html" or b"/": + if _q_s.username != "" and _q_s.password != "": + form = FieldStorage( + fp=request.content, + headers=self.headers, + environ={ + "REQUEST_METHOD": "POST", + "CONTENT_TYPE": self.headers.get( + b"content-type", + b"application/x-www-form-urlencoded", + ), + }, + ) + if "username" in form and "password" in form: + username = self.check_bytes(form["username"].value) + password = self.check_bytes(form["password"].value) + status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'https_server', 'action': 'login', 'status': status, 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'username': username, 'password': password, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - - request.responseHeaders.addRawHeader('Content-Type', 'text/html; charset=utf-8') + status = "success" + _q_s.logs.info( + { + "server": "https_server", + "action": "login", + "status": status, + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "username": username, + "password": password, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + + request.responseHeaders.addRawHeader( + "Content-Type", "text/html; charset=utf-8" + ) return self.home_file else: - request.responseHeaders.addRawHeader('Content-Type', 'text/html; charset=utf-8') + request.responseHeaders.addRawHeader( + "Content-Type", "text/html; charset=utf-8" + ) return self.home_file - self.CreateCert('localhost', self.key, self.cert) + self.CreateCert("localhost", self.key, self.cert) ssl_context = ssl.DefaultOpenSSLContextFactory(self.key, self.cert) reactor.listenSSL(self.port, Site(MainResource()), ssl_context) reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -227,13 +321,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'https_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "https_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -242,26 +368,38 @@ def run_server(self, process=False, auto=False): self.https_server_main() def close_port(self): - ret = close_port_wrapper('https_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("https_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('https_server', self.uuid, self.process) + ret = kill_server_wrapper("https_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from requests import get, post + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password - get('https://{}:{}'.format(_ip, _port), verify=False) - post('https://{}:{}'.format(_ip, _port), data={'username': (None, _username), 'password': (None, _password)}, verify=False) + get(f"https://{_ip}:{_port}", verify=False) + post( + f"https://{_ip}:{_port}", + data={"username": (None, _username), "password": (None, _password)}, + verify=False, + ) -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qhttpsserver = QHTTPSServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qhttpsserver = QHTTPSServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qhttpsserver.run_server() diff --git a/honeypots/imap_server.py b/honeypots/imap_server.py index 4593fcb..2e122d7 100644 --- a/honeypots/imap_server.py +++ b/honeypots/imap_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,10 +8,7 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') +""" from twisted.mail.imap4 import IMAP4Server from twisted.internet.protocol import Factory @@ -20,35 +17,56 @@ from twisted import cred from subprocess import Popen from os import path, getenv -from honeypots.helper import check_if_server_is_running, close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, set_local_vars, setup_logger +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) from uuid import uuid4 from contextlib import suppress -class QIMAPServer(): +class QIMAPServer: def __init__(self, **kwargs): self.auto_disabled = None - self.mocking_server = choice([b'OK Microsoft Exchange Server 2003 IMAP4rev1 server version 6.5.6944.0 DC9 ready']) + self.mocking_server = choice( + [b"OK Microsoft Exchange Server 2003 IMAP4rev1 server version 6.5.6944.0 DC9 ready"] + ) self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 143 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 143 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def imap_server_main(self): - _q_s = self class CustomIMAP4Server(IMAP4Server): - def check_bytes(self, string): if isinstance(string, bytes): return string.decode() @@ -65,47 +83,84 @@ def parse_command(self, line): tag, cmd = args elif len(args) == 1: tag = args[0] - self.sendBadResponse(tag, 'Missing command') + self.sendBadResponse(tag, "Missing command") return None else: - self.sendBadResponse(None, 'Null command') + self.sendBadResponse(None, "Null command") return None cmd = cmd.upper() with suppress(Exception): if "capture_commands" in _q_s.options: - _q_s.logs.info({'server': 'imap_server', 'action': 'command', 'data': {"cmd": self.check_bytes(cmd), "tag": self.check_bytes(tag), "data": self.check_bytes(rest)}, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "imap_server", + "action": "command", + "data": { + "cmd": self.check_bytes(cmd), + "tag": self.check_bytes(tag), + "data": self.check_bytes(rest), + }, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) try: return self.dispatchCommand(tag, cmd, rest) except IllegalClientResponse as e: - self.sendBadResponse(tag, 'Illegal syntax: ' + str(e)) + self.sendBadResponse(tag, "Illegal syntax: " + str(e)) except IllegalOperation as e: - self.sendNegativeResponse(tag, 'Illegal operation: ' + str(e)) + self.sendNegativeResponse(tag, "Illegal operation: " + str(e)) except IllegalMailboxEncoding as e: - self.sendNegativeResponse(tag, 'Illegal mailbox name: ' + str(e)) + self.sendNegativeResponse(tag, "Illegal mailbox name: " + str(e)) def connectionMade(self): - _q_s.logs.info({'server': 'imap_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "imap_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) self.sendPositiveResponse(message=_q_s.mocking_server) def authenticateLogin(self, user, passwd): username = self.check_bytes(user) password = self.check_bytes(passwd) - status = 'failed' + status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'imap_server', 'action': 'login', 'status': status, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + status = "success" + _q_s.logs.info( + { + "server": "imap_server", + "action": "login", + "status": status, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) raise cred.error.UnauthorizedLogin() def lineReceived(self, line): try: - _line = line.split(b' ')[1] - if _line.lower().startswith(b'login') or _line.lower().startswith(b'capability'): + _line = line.split(b" ")[1] + if _line.lower().startswith(b"login") or _line.lower().startswith( + b"capability" + ): IMAP4Server.lineReceived(self, line) except BaseException: pass @@ -125,7 +180,7 @@ def buildProtocol(self, address): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -137,13 +192,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' + status = "success" - self.logs.info({'server': 'imap_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) + self.logs.info( + { + "server": "imap_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) - if status == 'success': + if status == "success": return True else: self.kill_server() @@ -152,16 +239,17 @@ def run_server(self, process=False, auto=False): self.imap_server_main() def close_port(self): - ret = close_port_wrapper('imap_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("imap_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('imap_server', self.uuid, self.process) + ret = kill_server_wrapper("imap_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from imaplib import IMAP4 + _ip = ip or self.ip _port = port or self.port _username = username or self.username @@ -171,8 +259,15 @@ def test_server(self, ip=None, port=None, username=None, password=None): imap_test.login(_username, _password) -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qimapserver = QIMAPServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qimapserver = QIMAPServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qimapserver.run_server() diff --git a/honeypots/ipp_server.py b/honeypots/ipp_server.py index 0edff8b..4a2c9a5 100644 --- a/honeypots/ipp_server.py +++ b/honeypots/ipp_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,70 +8,277 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') +""" +from contextlib import suppress +from os import getenv, path +from struct import unpack +from subprocess import Popen from typing import Dict -from requests.packages.urllib3 import disable_warnings +from uuid import uuid4 + from twisted.internet import reactor -from twisted.web.server import Site from twisted.web.resource import Resource -from twisted.python import log as tlog -from subprocess import Popen -from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running -from uuid import uuid4 -from contextlib import suppress -from struct import unpack +from twisted.web.server import Site -disable_warnings() +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) STATUS_CODE_OK = b"\x00\x00" STATUS_CODE_BAD_REQUEST = b"\x04\x00" -class QIPPServer(): +class QIPPServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 631 - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 631 + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def ipp_server_main(self): _q_s = self class MainResource(Resource): isLeaf = True - operations_supported = {0x0001: 'Reserved', 0x0002: 'Print-Job', 0x0003: 'Print-URI', 0x0004: 'Validate-Job', 0x0005: 'Create-Job', 0x0006: 'Send-Document', 0x0007: 'Send-URI', 0x0008: 'Cancel-Job', 0x0009: 'Get-Job-Attributes', 0x000A: 'Get-Jobs', 0x000B: 'Get-Printer-Attributes', 0x000C: 'Hold-Job', 0x000D: 'Release-Job', 0x000E: 'Restart-Job', 0x000F: 'Reserved', 0x0010: 'Pause-Printer', 0x0011: 'Resume-Printer', 0x0012: 'Purge-Jobs', 0x0013: 'Set-Printer-Attributes', 0x0014: 'Set-Job-Attributes', 0x0015: 'Get-Printer-Supported-Values', 0x0016: 'Create-Printer-Subscriptions', 0x0017: 'Create-Job-Subscriptions', 0x0018: 'Get-Subscription-Attributes', 0x0019: 'Get-Subscriptions', 0x001A: 'Renew-Subscription', 0x001B: 'Cancel-Subscription', 0x001C: 'Get-Notifications', 0x001D: 'ipp-indp-method', 0x001E: 'Get-Resource-Attributes', 0x001F: 'ipp-get-resources', 0x0020: 'Get-Resources', 0x0021: 'ipp-install', 0x0022: 'Enable-Printer', 0x0023: 'Disable-Printer', 0x0024: 'Pause-Printer-After-Current-Job', 0x0025: 'Hold-New-Jobs', 0x0026: 'Release-Held-New-Jobs', 0x0027: 'Deactivate-Printer', 0x0028: 'Activate-Printer', 0x0029: 'Restart-Printer', 0x002A: 'Shutdown-Printer', 0x002B: 'Startup-Printer', 0x002C: 'Reprocess-Job', 0x002D: 'Cancel-Current-Job', 0x002E: 'Suspend-Current-Job', 0x002F: 'Resume-Job', 0x0030: 'Promote-Job', 0x0031: 'Schedule-Job-After', 0x0033: 'Cancel-Document', 0x0034: 'Get-Document-Attributes', 0x0035: 'Get-Documents', 0x0036: 'Delete-Document', 0x0037: 'Set-Document-Attributes', 0x0038: 'Cancel-Jobs', 0x0039: 'Cancel-My-Jobs', 0x003A: 'Resubmit-Job', 0x003B: 'Close-Job', 0x003C: 'Identify-Printer', 0x003D: 'Validate-Document', 0x003E: 'Add-Document-Images', 0x003F: 'Acknowledge-Document', 0x0040: 'Acknowledge-Identify-Printer', 0x0041: 'Acknowledge-Job', 0x0042: 'Fetch-Document', 0x0043: 'Fetch-Job', 0x0044: 'Get-Output-Device-Attributes', 0x0045: 'Update-Active-Jobs', 0x0046: 'Deregister-Output-Device', 0x0047: 'Update-Document-Status', 0x0048: 'Update-Job-Status', 0x0049: 'Update-Output-Device-Attributes', 0x004A: 'Get-Next-Document-Data', 0x004B: 'Allocate-Printer-Resources', 0x004C: 'Create-Printer', 0x004D: 'Deallocate-Printer-Resources', 0x004E: 'Delete-Printer', 0x004F: 'Get-Printers', 0x0050: 'Shutdown-One-Printer', 0x0051: 'Startup-One-Printer', 0x0052: 'Cancel-Resource', 0x0053: 'Create-Resource', 0x0054: 'Install-Resource', 0x0055: 'Send-Resource-Data', 0x0056: 'Set-Resource-Attributes', 0x0057: 'Create-Resource-Subscriptions', 0x0058: 'Create-System-Subscriptions', 0x0059: 'Disable-All-Printers', 0x005A: 'Enable-All-Printers', 0x005B: 'Get-System-Attributes', 0x005C: 'Get-System-Supported-Values', 0x005D: 'Pause-All-Printers', 0x005E: 'Pause-All-Printers-After-Current-Job', 0x005F: 'Register-Output-Device', 0x0060: 'Restart-System', 0x0061: 'Resume-All-Printers', 0x0062: 'Set-System-Attributes', 0x0063: 'Shutdown-All-Printers', 0x0064: 'Startup-All-Printers', 0x0065: 'Get-Printer-Resources', 0x0066: 'Get-User-Printer-Attributes', 0x0067: 'Restart-One-Printer'} - - attribute_group_tags = {0x00: 'Reserved', 0x01: 'operation-attributes-tag', 0x02: 'job-attributes-tag', 0x03: 'end-of-attributes-tag', 0x04: 'printer-attributes-tag', 0x05: 'unsupported-attributes-tag', 0x06: 'subscription-attributes-tag', 0x07: 'event-notification-attributes-tag', 0x08: 'resource-attributes-tag', 0x09: 'document-attributes-tag', 0x0A: 'system-attributes-tag'} - - attribute_syntaxes = {0x20: 'Unassigned', 0x21: 'integer', 0x22: 'boolean', 0x23: 'enum', 0x30: 'octetString', 0x31: 'dateTime', 0x32: 'resolution', 0x33: 'rangeOfInteger', 0x34: 'begCollection', 0x35: 'textWithLanguage', 0x36: 'nameWithLanguage', 0x37: 'endCollection', 0x40: 'Unassigned', 0x41: 'textWithoutLanguage', 0x42: 'nameWithoutLanguage', 0x43: 'Unassigned', 0x44: 'keyword', 0x45: 'uri', 0x46: 'uriScheme', 0x47: 'charset', 0x48: 'naturalLanguage', 0x49: 'mimeMediaType', 0x4A: 'memberAttrName', 0x7F: 'extension'} - - status_codes = {0x0000: 'successful-ok', 0x0001: 'successful-ok-ignored-or-substituted-attributes', 0x0002: 'successful-ok-conflicting-attributes', 0x0003: 'successful-ok-ignored-subscriptions', 0x0004: 'ipp-indp-method', 0x0005: 'successful-ok-too-many-events', 0x0006: 'ipp-indp-method', 0x0007: 'successful-ok-events-complete', 0x0300: 'ipp-get-method', 0x0400: 'client-error-bad-request', 0x0401: 'client-error-forbidden', 0x0402: 'client-error-not-authenticated', 0x0403: 'client-error-not-authorized', 0x0404: 'client-error-not-possible', 0x0405: 'client-error-timeout', 0x0406: 'client-error-not-found', 0x0407: 'client-error-gone', 0x0408: 'client-error-request-entity-too-large', 0x0409: 'client-error-request-value-too-long', 0x040A: 'client-error-document-format-not-supported', 0x040B: 'client-error-attributes-or-values-not-supported', 0x040C: 'client-error-uri-scheme-not-supported', 0x040D: 'client-error-charset-not-supported', 0x040E: 'client-error-conflicting-attributes', 0x040F: 'client-error-compression-not-supported', 0x0410: 'client-error-compression-error', 0x0411: 'client-error-document-format-error', 0x0412: 'client-error-document-access-error', 0x0413: 'client-error-attributes-not-settable', 0x0414: 'client-error-ignored-all-subscriptions', 0x0415: 'client-error-too-many-subscriptions', 0x0416: 'ipp-indp-method', 0x0417: 'ipp-install', 0x0418: 'client-error-document-password-error', 0x0419: 'client-error-document-permission-error', 0x041A: 'client-error-document-security-error', 0x041B: 'client-error-document-unprintable-error', 0x041C: 'client-error-account-info-needed', 0x041D: 'client-error-account-closed', 0x041E: 'client-error-account-limit-reached', 0x041F: 'client-error-account-authorization-failed', 0x0420: 'client-error-not-fetchable', 0x0500: 'server-error-internal-error', 0x0501: 'server-error-operation-not-supported', 0x0502: 'server-error-service-unavailable', 0x0503: 'server-error-version-not-supported', 0x0504: 'server-error-device-error', 0x0505: 'server-error-temporary-error', 0x0506: 'server-error-not-accepting-jobs', 0x0507: 'server-error-busy', 0x0508: 'server-error-job-canceled', 0x0509: 'server-error-multiple-document-jobs-not-supported', 0x050A: 'server-error-printer-is-deactivated', 0x050B: 'server-error-too-many-jobs', 0x050C: 'server-error-too-many-documents'} + operations_supported = { + 0x0001: "Reserved", + 0x0002: "Print-Job", + 0x0003: "Print-URI", + 0x0004: "Validate-Job", + 0x0005: "Create-Job", + 0x0006: "Send-Document", + 0x0007: "Send-URI", + 0x0008: "Cancel-Job", + 0x0009: "Get-Job-Attributes", + 0x000A: "Get-Jobs", + 0x000B: "Get-Printer-Attributes", + 0x000C: "Hold-Job", + 0x000D: "Release-Job", + 0x000E: "Restart-Job", + 0x000F: "Reserved", + 0x0010: "Pause-Printer", + 0x0011: "Resume-Printer", + 0x0012: "Purge-Jobs", + 0x0013: "Set-Printer-Attributes", + 0x0014: "Set-Job-Attributes", + 0x0015: "Get-Printer-Supported-Values", + 0x0016: "Create-Printer-Subscriptions", + 0x0017: "Create-Job-Subscriptions", + 0x0018: "Get-Subscription-Attributes", + 0x0019: "Get-Subscriptions", + 0x001A: "Renew-Subscription", + 0x001B: "Cancel-Subscription", + 0x001C: "Get-Notifications", + 0x001D: "ipp-indp-method", + 0x001E: "Get-Resource-Attributes", + 0x001F: "ipp-get-resources", + 0x0020: "Get-Resources", + 0x0021: "ipp-install", + 0x0022: "Enable-Printer", + 0x0023: "Disable-Printer", + 0x0024: "Pause-Printer-After-Current-Job", + 0x0025: "Hold-New-Jobs", + 0x0026: "Release-Held-New-Jobs", + 0x0027: "Deactivate-Printer", + 0x0028: "Activate-Printer", + 0x0029: "Restart-Printer", + 0x002A: "Shutdown-Printer", + 0x002B: "Startup-Printer", + 0x002C: "Reprocess-Job", + 0x002D: "Cancel-Current-Job", + 0x002E: "Suspend-Current-Job", + 0x002F: "Resume-Job", + 0x0030: "Promote-Job", + 0x0031: "Schedule-Job-After", + 0x0033: "Cancel-Document", + 0x0034: "Get-Document-Attributes", + 0x0035: "Get-Documents", + 0x0036: "Delete-Document", + 0x0037: "Set-Document-Attributes", + 0x0038: "Cancel-Jobs", + 0x0039: "Cancel-My-Jobs", + 0x003A: "Resubmit-Job", + 0x003B: "Close-Job", + 0x003C: "Identify-Printer", + 0x003D: "Validate-Document", + 0x003E: "Add-Document-Images", + 0x003F: "Acknowledge-Document", + 0x0040: "Acknowledge-Identify-Printer", + 0x0041: "Acknowledge-Job", + 0x0042: "Fetch-Document", + 0x0043: "Fetch-Job", + 0x0044: "Get-Output-Device-Attributes", + 0x0045: "Update-Active-Jobs", + 0x0046: "Deregister-Output-Device", + 0x0047: "Update-Document-Status", + 0x0048: "Update-Job-Status", + 0x0049: "Update-Output-Device-Attributes", + 0x004A: "Get-Next-Document-Data", + 0x004B: "Allocate-Printer-Resources", + 0x004C: "Create-Printer", + 0x004D: "Deallocate-Printer-Resources", + 0x004E: "Delete-Printer", + 0x004F: "Get-Printers", + 0x0050: "Shutdown-One-Printer", + 0x0051: "Startup-One-Printer", + 0x0052: "Cancel-Resource", + 0x0053: "Create-Resource", + 0x0054: "Install-Resource", + 0x0055: "Send-Resource-Data", + 0x0056: "Set-Resource-Attributes", + 0x0057: "Create-Resource-Subscriptions", + 0x0058: "Create-System-Subscriptions", + 0x0059: "Disable-All-Printers", + 0x005A: "Enable-All-Printers", + 0x005B: "Get-System-Attributes", + 0x005C: "Get-System-Supported-Values", + 0x005D: "Pause-All-Printers", + 0x005E: "Pause-All-Printers-After-Current-Job", + 0x005F: "Register-Output-Device", + 0x0060: "Restart-System", + 0x0061: "Resume-All-Printers", + 0x0062: "Set-System-Attributes", + 0x0063: "Shutdown-All-Printers", + 0x0064: "Startup-All-Printers", + 0x0065: "Get-Printer-Resources", + 0x0066: "Get-User-Printer-Attributes", + 0x0067: "Restart-One-Printer", + } + + attribute_group_tags = { + 0x00: "Reserved", + 0x01: "operation-attributes-tag", + 0x02: "job-attributes-tag", + 0x03: "end-of-attributes-tag", + 0x04: "printer-attributes-tag", + 0x05: "unsupported-attributes-tag", + 0x06: "subscription-attributes-tag", + 0x07: "event-notification-attributes-tag", + 0x08: "resource-attributes-tag", + 0x09: "document-attributes-tag", + 0x0A: "system-attributes-tag", + } + + attribute_syntaxes = { + 0x20: "Unassigned", + 0x21: "integer", + 0x22: "boolean", + 0x23: "enum", + 0x30: "octetString", + 0x31: "dateTime", + 0x32: "resolution", + 0x33: "rangeOfInteger", + 0x34: "begCollection", + 0x35: "textWithLanguage", + 0x36: "nameWithLanguage", + 0x37: "endCollection", + 0x40: "Unassigned", + 0x41: "textWithoutLanguage", + 0x42: "nameWithoutLanguage", + 0x43: "Unassigned", + 0x44: "keyword", + 0x45: "uri", + 0x46: "uriScheme", + 0x47: "charset", + 0x48: "naturalLanguage", + 0x49: "mimeMediaType", + 0x4A: "memberAttrName", + 0x7F: "extension", + } + + status_codes = { + 0x0000: "successful-ok", + 0x0001: "successful-ok-ignored-or-substituted-attributes", + 0x0002: "successful-ok-conflicting-attributes", + 0x0003: "successful-ok-ignored-subscriptions", + 0x0004: "ipp-indp-method", + 0x0005: "successful-ok-too-many-events", + 0x0006: "ipp-indp-method", + 0x0007: "successful-ok-events-complete", + 0x0300: "ipp-get-method", + 0x0400: "client-error-bad-request", + 0x0401: "client-error-forbidden", + 0x0402: "client-error-not-authenticated", + 0x0403: "client-error-not-authorized", + 0x0404: "client-error-not-possible", + 0x0405: "client-error-timeout", + 0x0406: "client-error-not-found", + 0x0407: "client-error-gone", + 0x0408: "client-error-request-entity-too-large", + 0x0409: "client-error-request-value-too-long", + 0x040A: "client-error-document-format-not-supported", + 0x040B: "client-error-attributes-or-values-not-supported", + 0x040C: "client-error-uri-scheme-not-supported", + 0x040D: "client-error-charset-not-supported", + 0x040E: "client-error-conflicting-attributes", + 0x040F: "client-error-compression-not-supported", + 0x0410: "client-error-compression-error", + 0x0411: "client-error-document-format-error", + 0x0412: "client-error-document-access-error", + 0x0413: "client-error-attributes-not-settable", + 0x0414: "client-error-ignored-all-subscriptions", + 0x0415: "client-error-too-many-subscriptions", + 0x0416: "ipp-indp-method", + 0x0417: "ipp-install", + 0x0418: "client-error-document-password-error", + 0x0419: "client-error-document-permission-error", + 0x041A: "client-error-document-security-error", + 0x041B: "client-error-document-unprintable-error", + 0x041C: "client-error-account-info-needed", + 0x041D: "client-error-account-closed", + 0x041E: "client-error-account-limit-reached", + 0x041F: "client-error-account-authorization-failed", + 0x0420: "client-error-not-fetchable", + 0x0500: "server-error-internal-error", + 0x0501: "server-error-operation-not-supported", + 0x0502: "server-error-service-unavailable", + 0x0503: "server-error-version-not-supported", + 0x0504: "server-error-device-error", + 0x0505: "server-error-temporary-error", + 0x0506: "server-error-not-accepting-jobs", + 0x0507: "server-error-busy", + 0x0508: "server-error-job-canceled", + 0x0509: "server-error-multiple-document-jobs-not-supported", + 0x050A: "server-error-printer-is-deactivated", + 0x050B: "server-error-too-many-jobs", + 0x050C: "server-error-too-many-documents", + } def get_uint8_t(self, index, data): - return index + 1, unpack('b', data[index:index + 1])[0] + return index + 1, unpack("b", data[index : index + 1])[0] def get_uint16_t(self, index, data): - return index + 2, unpack('>H', data[index:index + 2])[0] + return index + 2, unpack(">H", data[index : index + 2])[0] def get_uint32_t(self, index, data): - return index + 4, unpack('>I', data[index:index + 4])[0] + return index + 4, unpack(">I", data[index : index + 4])[0] def get_string(self, index, length, data): - return index + length, data[index:index + length] + return index + length, data[index : index + length] def check_bytes(self, string): if isinstance(string, bytes): @@ -80,11 +287,11 @@ def check_bytes(self, string): return str(string) def render_POST(self, request): - headers = {} client_ip = "" with suppress(Exception): + def check_bytes(string): if isinstance(string, bytes): return string.decode() @@ -92,37 +299,56 @@ def check_bytes(string): return str(string) for item, value in dict(request.requestHeaders.getAllRawHeaders()).items(): - headers.update({check_bytes(item): ','.join(map(check_bytes, value))}) - headers.update({'method': check_bytes(request.method)}) - headers.update({'uri': check_bytes(request.uri)}) + headers.update({check_bytes(item): ",".join(map(check_bytes, value))}) + headers.update({"method": check_bytes(request.method)}) + headers.update({"uri": check_bytes(request.uri)}) - if 'fix_get_client_ip' in _q_s.options: + if "fix_get_client_ip" in _q_s.options: with suppress(Exception): raw_headers = dict(request.requestHeaders.getAllRawHeaders()) - if b'X-Forwarded-For' in raw_headers: - client_ip = check_bytes(raw_headers[b'X-Forwarded-For'][0]) - elif b'X-Real-IP' in raw_headers: - client_ip = check_bytes(raw_headers[b'X-Real-IP'][0]) + if b"X-Forwarded-For" in raw_headers: + client_ip = check_bytes(raw_headers[b"X-Forwarded-For"][0]) + elif b"X-Real-IP" in raw_headers: + client_ip = check_bytes(raw_headers[b"X-Real-IP"][0]) if client_ip == "": client_ip = request.getClientAddress().host with suppress(Exception): if "capture_commands" in _q_s.options: - _q_s.logs.info({'server': 'ipp_server', 'action': 'connection', 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': headers}) + _q_s.logs.info( + { + "server": "ipp_server", + "action": "connection", + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": headers, + } + ) else: - _q_s.logs.info({'server': 'ipp_server', 'action': 'connection', 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "ipp_server", + "action": "connection", + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) data = request.content.read() - response = '' + response = "" version = [0, 0] request_id = 0 - group = '' + group = "" groups = [] - groups_parsed = '' - operation = '' - status = 'success' + groups_parsed = "" + operation = "" + status = "success" with suppress(Exception): index, version[0] = self.get_uint8_t(0, data) @@ -137,45 +363,58 @@ def check_bytes(string): if uint8_t in self.attribute_syntaxes: while index < to_parse_len: try: - attribute = '' - value = '' - if self.attribute_syntaxes[uint8_t] == 'integer': + attribute = "" + value = "" + if self.attribute_syntaxes[uint8_t] == "integer": index, attribute = self.get_uint32_t(index, data) - elif self.attribute_syntaxes[uint8_t] == 'boolean': + elif self.attribute_syntaxes[uint8_t] == "boolean": index, attribute = self.get_uint8_t(index, data) else: index, uint16_t = self.get_uint16_t(index, data) index, attribute = self.get_string(index, uint16_t, data) index, uint16_t = self.get_uint16_t(index, data) index, value = self.get_string(index, uint16_t, data) - if attribute == b'': + if attribute == b"": groups[-1][1].append(self.check_bytes(value)) else: - groups.append([self.check_bytes(attribute), [self.check_bytes(value)]]) + groups.append( + [self.check_bytes(attribute), [self.check_bytes(value)]] + ) index, uint8_t = self.get_uint8_t(index, data) if uint8_t in self.attribute_group_tags: break except BaseException: - status = 'failed' + status = "failed" break with suppress(Exception): - response += '' - response = 'VERSION {}.{}|'.format(version[0], version[1]) - response += 'REQUEST {}|'.format(hex(request_id)) - response += 'OPERATION {}|'.format(operation) - response += 'GROUP {}|'.format(group) + response += "" + response = f"VERSION {version[0]}.{version[1]}|" + response += f"REQUEST {hex(request_id)}|" + response += f"OPERATION {operation}|" + response += f"GROUP {group}|" if len(groups) > 0: for i in groups: - groups_parsed += 'ATTR ' + i[0] + ' ' + ','.join(i[1]) + '|' + groups_parsed += "ATTR " + i[0] + " " + ",".join(i[1]) + "|" groups_parsed = groups_parsed.strip() response += groups_parsed with suppress(Exception): - if response[-1] == '|': + if response[-1] == "|": response = response[0:-1] if len(response) > 0: - _q_s.logs.info({'server': 'ipp_server', 'action': 'query', 'status': status, 'src_ip': client_ip, 'src_port': request.getClientAddress().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': {'request': response}}) + _q_s.logs.info( + { + "server": "ipp_server", + "action": "query", + "status": status, + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": {"request": response}, + } + ) return self.send_response(data, status != "failed") @staticmethod @@ -184,7 +423,9 @@ def send_response(request: bytes, successful: bool) -> bytes: if version not in [b"\x01\x01", b"\x02\x00", b"\x02\x01", b"\x02\x02"]: version = b"\x02\x00" status_code = STATUS_CODE_OK if successful else STATUS_CODE_BAD_REQUEST - attributes = attributes_dict_to_bytes({"attributes-charset": "utf-8", "attributes-natural-language": "en-us"}) + attributes = attributes_dict_to_bytes( + {"attributes-charset": "utf-8", "attributes-natural-language": "en-us"} + ) response = version + status_code + request_id + attributes return response @@ -192,7 +433,7 @@ def send_response(request: bytes, successful: bool) -> bytes: reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -204,13 +445,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'ipp_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "ipp_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -219,28 +486,29 @@ def run_server(self, process=False, auto=False): self.ipp_server_main() def close_port(self): - ret = close_port_wrapper('ipp_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("ipp_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('ipp_server', self.uuid, self.process) + ret = kill_server_wrapper("ipp_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None): with suppress(): from socket import socket, AF_INET, SOCK_STREAM + _ip = ip or self.ip _port = port or self.port - body = b'\x02\x00\x00\x0b\x00\x01/p\x01G\x00\x12attributes-charset\x00\x05utf-8H\x00\x1battributes-natural-language\x00\x02enE\x00\x0bprinter-uri\x00\x15ipp://127.0.0.1:631/D\x00\x14requested-attributes\x00\x03allD\x00\x00\x00\x12media-col-database\x03' + body = b"\x02\x00\x00\x0b\x00\x01/p\x01G\x00\x12attributes-charset\x00\x05utf-8H\x00\x1battributes-natural-language\x00\x02enE\x00\x0bprinter-uri\x00\x15ipp://127.0.0.1:631/D\x00\x14requested-attributes\x00\x03allD\x00\x00\x00\x12media-col-database\x03" - headers = """\ + headers = f"""\ POST / HTTP/1.1\r Content-Type: application/x-www-form-urlencoded\r - Content-Length: {}\r - Host: {}:{}\r + Content-Length: {len(body)}\r + Host: {_ip}:{_port}\r Connection: close\r - \r\n""".format(len(body), _ip, _port).encode() + \r\n""".encode() s = socket(AF_INET, SOCK_STREAM) s.connect((_ip, _port)) @@ -264,8 +532,10 @@ def attributes_dict_to_bytes(attributes: Dict[str, str]) -> bytes: return attributes_str -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qippserver = QIPPServer(ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config) + qippserver = QIPPServer( + ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config + ) qippserver.run_server() diff --git a/honeypots/irc_server.py b/honeypots/irc_server.py index 2dd0fa5..9c86188 100644 --- a/honeypots/irc_server.py +++ b/honeypots/irc_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,85 +8,126 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') -filterwarnings(action='ignore', module='.*socket.*') - -from twisted.internet.protocol import Protocol, Factory +from twisted.internet.protocol import Factory from twisted.internet import reactor from twisted.words import service -from time import time -from twisted.python import log as tlog from subprocess import Popen from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from honeypots.helper import ( + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_up_error_logging, + setup_logger, + set_local_vars, + check_if_server_is_running, +) from uuid import uuid4 from contextlib import suppress -class QIRCServer(): +class QIRCServer: + NAME = "irc_server" + def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = f"honeypotslogger_{__class__.__name__}_{str(uuid4())[:8]}" + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 6667 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 6667 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) + self.logger = set_up_error_logging() def irc_server_main(self): _q_s = self class CustomIRCProtocol(service.IRCUser): - def connectionMade(self): - _q_s.logs.info({'server': 'irc_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": _q_s.NAME, + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def handleCommand(self, command, prefix, params): - - def check_bytes(string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - - with suppress(Exception): - if "capture_commands" in _q_s.options: - _q_s.logs.info({'server': 'irc_server', 'action': 'command', 'data': {"command": check_bytes(command), "prefix": check_bytes(prefix), "params": check_bytes(params)}, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + if "capture_commands" in _q_s.options: + _q_s.logs.info( + { + "server": _q_s.NAME, + "action": "command", + "data": { + "command": check_bytes(command), + "prefix": check_bytes(prefix), + "params": check_bytes(params), + }, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) service.IRCUser.handleCommand(self, command, prefix, params) - def dataReceived(self, data): - #_q_s.logs.info({'server': 'irc_server', 'action': 'command', 'data': check_bytes(data), 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - service.IRCUser.dataReceived(self, data) + def dataReceived(self, data: bytes): + try: + service.IRCUser.dataReceived(self, data) + except UnicodeDecodeError: + _q_s.logger.debug( + f"[{_q_s.NAME}]: Could not decode data as utf-8: {data.hex(' ')}" + ) def irc_unknown(self, prefix, command, params): pass def irc_NICK(self, prefix, params): - - def check_bytes(string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - status = False - username = check_bytes(''.join(params)) + username = check_bytes("".join(params)) password = check_bytes(self.password) if password == check_bytes(_q_s.password): if username == _q_s.username: status = True - _q_s.logs.info({'server': 'irc_server', 'action': 'login', 'status': status, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'username': username, 'password': password, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": _q_s.NAME, + "action": "login", + "status": status, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "username": username, + "password": password, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) factory = Factory() factory.protocol = CustomIRCProtocol @@ -94,7 +135,7 @@ def check_bytes(string): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -106,13 +147,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'irc_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": self.NAME, + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -121,19 +188,19 @@ def run_server(self, process=False, auto=False): self.irc_server_main() def close_port(self): - ret = close_port_wrapper('irc_server', self.ip, self.port, self.logs) + ret = close_port_wrapper(self.NAME, self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('irc_server', self.uuid, self.process) + ret = kill_server_wrapper(self.NAME, self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from warnings import filterwarnings - filterwarnings(action='ignore', module='.*socket.*') + + filterwarnings(action="ignore", module=".*socket.*") from socket import socket, AF_INET, SOCK_STREAM - from time import sleep _ip = ip or self.ip _port = port or self.port @@ -142,12 +209,26 @@ def test_server(self, ip=None, port=None, username=None, password=None): c = socket(AF_INET, SOCK_STREAM) c.connect((_ip, _port)) c.setblocking(False) - c.send("PASS {}\n".format(_password).encode()) + c.send(f"PASS {_password}\n".encode()) c.close() -if __name__ == '__main__': +def check_bytes(string): + if isinstance(string, bytes): + return string.decode(errors="replace") + else: + return str(string) + + +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - QIRCServer = QIRCServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + QIRCServer = QIRCServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) QIRCServer.run_server() diff --git a/honeypots/ldap_server.py b/honeypots/ldap_server.py index ca0b0f3..e625750 100644 --- a/honeypots/ldap_server.py +++ b/honeypots/ldap_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,46 +8,61 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') +""" from twisted.internet.protocol import Protocol, Factory from twisted.internet import reactor -from twisted.python import log as tlog from subprocess import Popen from os import path, getenv from struct import unpack from binascii import unhexlify -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from honeypots.helper import ( + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + setup_logger, + set_local_vars, + check_if_server_is_running, +) from uuid import uuid4 from contextlib import suppress -class QLDAPServer(): +class QLDAPServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 389 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 389 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def ldap_server_main(self): _q_s = self class CustomLDAProtocol(Protocol): - _state = None def check_bytes(self, string): @@ -58,33 +73,51 @@ def check_bytes(self, string): def connectionMade(self): self._state = 1 - _q_s.logs.info({'server': 'ldap_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "ldap_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def parse_ldap_packet(self, data): - # V # 30[20] 0201[02] 60[1b] 0201[03] 04[0a] 7379736261636b757031 [80][0a] 7379736261636b757032 - username = '' - password = '' + username = "" + password = "" username_start = 0 username_end = 0 password_start = 0 password_end = 0 with suppress(Exception): - version = data.find(b'\x02\x01\x03') + version = data.find(b"\x02\x01\x03") if version > 0: username_start = version + 5 - username_end = unpack('b', data[version + 4:username_start])[0] + username_start + username_end = ( + unpack("b", data[version + 4 : username_start])[0] + username_start + ) username = data[username_start:username_end] auth_type = data[username_end] if auth_type == 0x80: if data[username_end + 1] == 0x82: password_start = username_end + 4 - password_end = unpack('>H', data[username_end + 2:username_end + 4])[0] + username_end + 4 + password_end = ( + unpack(">H", data[username_end + 2 : username_end + 4])[0] + + username_end + + 4 + ) else: password_start = username_end + 2 - password_end = unpack('b', data[username_end + 2:username_end + 3])[0] + username_start + 2 + password_end = ( + unpack("b", data[username_end + 2 : username_end + 3])[0] + + username_start + + 2 + ) password = data[password_start:password_end] return username, password @@ -95,23 +128,71 @@ def dataReceived(self, data): username, password = self.parse_ldap_packet(data) username = self.check_bytes(username) password = self.check_bytes(password) - if username != '' or password != '': + if username != "" or password != "": if username == _q_s.username and password == _q_s.password: - _q_s.logs.info({'server': 'ldap_server', 'action': 'login', 'status': 'success', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': _q_s.username, 'password': _q_s.password}) + _q_s.logs.info( + { + "server": "ldap_server", + "action": "login", + "status": "success", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": _q_s.username, + "password": _q_s.password, + } + ) else: - _q_s.logs.info({'server': 'ldap_server', 'action': 'login', 'status': 'failed', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) - self.transport.write(unhexlify(b'300c02010165070a013204000400')) + _q_s.logs.info( + { + "server": "ldap_server", + "action": "login", + "status": "failed", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) + self.transport.write(unhexlify(b"300c02010165070a013204000400")) elif self._state == 2: self._state = 3 username, password = self.parse_ldap_packet(data) username = self.check_bytes(username) password = self.check_bytes(password) - if username != '' or password != '': + if username != "" or password != "": if username == _q_s.username and password == _q_s.password: - _q_s.logs.info({'server': 'ldap_server', 'action': 'login', 'status': 'success', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': _q_s.username, 'password': _q_s.password}) + _q_s.logs.info( + { + "server": "ldap_server", + "action": "login", + "status": "success", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": _q_s.username, + "password": _q_s.password, + } + ) else: - _q_s.logs.info({'server': 'ldap_server', 'action': 'login', 'status': 'failed', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) - self.transport.write(unhexlify(b'300c02010265070a013204000400')) + _q_s.logs.info( + { + "server": "ldap_server", + "action": "login", + "status": "failed", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) + self.transport.write(unhexlify(b"300c02010265070a013204000400")) else: self.transport.loseConnection() @@ -124,7 +205,7 @@ def connectionLost(self, reason): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -136,13 +217,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' + status = "success" - self.logs.info({'server': 'ldap_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) + self.logs.info( + { + "server": "ldap_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) - if status == 'success': + if status == "success": return True else: self.kill_server() @@ -151,27 +264,44 @@ def run_server(self, process=False, auto=False): self.ldap_server_main() def close_port(self): - ret = close_port_wrapper('ldap_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("ldap_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('ldap_server', self.uuid, self.process) + ret = kill_server_wrapper("ldap_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from ldap3 import Server, Connection, ALL + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password - c = Connection(Server(_ip, port=_port, get_info=ALL), authentication='SIMPLE', user=_username, password=_password, check_names=True, lazy=False, client_strategy='SYNC', raise_exceptions=True) + c = Connection( + Server(_ip, port=_port, get_info=ALL), + authentication="SIMPLE", + user=_username, + password=_password, + check_names=True, + lazy=False, + client_strategy="SYNC", + raise_exceptions=True, + ) c.open() c.bind() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - QLDAPServer = QLDAPServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + QLDAPServer = QLDAPServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) QLDAPServer.run_server() diff --git a/honeypots/memcache_server.py b/honeypots/memcache_server.py index fef771c..8aac179 100644 --- a/honeypots/memcache_server.py +++ b/honeypots/memcache_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,87 +8,198 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor -from twisted.python import log as tlog -from subprocess import Popen -from os import path, getenv +from contextlib import suppress +from os import getenv, path from random import randint, uniform +from subprocess import Popen from time import time -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running from uuid import uuid4 -from contextlib import suppress + +from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QMemcacheServer(): +class QMemcacheServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 11211 - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 11211 + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def memcache_server_main(self): _q_s = self class CustomRedisProtocol(Protocol): - _state = None def get_stats(self): items = randint(80000000, 90000000) - ret = '' - temp = {b'pid': randint(5, 400), b'uptime': randint(1000, 2000), b'time': int(time()), b'version': b'1.5.6', b'libevent': b'2.1.8-stable', b'pointer_size': 64, b'rusage_user': round(uniform(0.1, 0.9), 4), b'rusage_system': round(uniform(0.1, 0.9), 6), b'max_connections': 1024, b'curr_connections': randint(1, 1024), b'total_connections': 5, b'rejected_connections': 0, b'connection_structures': 2, b'reserved_fds': 20, b'cmd_get': 0, b'cmd_set': 40, b'cmd_flush': 0, b'cmd_touch': 0, b'get_hits': 0, b'get_misses': 0, b'get_expired': 0, b'get_flushed': 0, b'delete_misses': 0, b'delete_hits': 0, b'incr_misses': 0, b'incr_hits': 0, b'decr_misses': 0, b'decr_hits': 0, b'cas_misses': 0, b'cas_hits': 0, b'cas_badval': 0, b'touch_hits': 0, b'touch_misses': 0, b'auth_cmds': 0, b'auth_errors': 0, b'bytes_read': randint(7000000, 8000000), b'bytes_written': randint(500000, 1000000), b'limit_maxbytes': 33554432, b'accepting_conns': 1, b'listen_disabled_num': 0, b'time_in_listen_disabled_us': 0, b'threads': randint(4, 9000), b'conn_yields': 0, b'hash_power_level': 16, b'hash_bytes': 524288, b'hash_is_expanding': False, b'slab_reassign_rescues': 0, b'slab_reassign_chunk_rescues': 0, b'slab_reassign_evictions_nomem': 0, b'slab_reassign_inline_reclaim': 0, b'slab_reassign_busy_items': 0, b'slab_reassign_busy_deletes': 0, b'slab_reassign_running': False, b'slabs_moved': 0, b'lru_crawler_running': 0, b'lru_crawler_starts': randint(500000, 700000), b'lru_maintainer_juggles': randint(400000, 500000), b'malloc_fails': 0, b'log_worker_dropped': 0, b'log_worker_written': 0, b'log_watcher_skipped': 0, b'log_watcher_sent': 0, b'bytes': randint(13554432, 33554432), b'curr_items': items, b'total_items': items, b'slab_global_page_pool': 0, b'expired_unfetched': 0, b'evicted_unfetched': 0, b'evicted_active': 0, b'evictions': 0, b'reclaimed': 0, b'crawler_reclaimed': 0, b'crawler_items_checked': randint(5000, 6000), b'lrutail_reflocked': 0, b'moves_to_cold': randint(5000, 6000), b'moves_to_warm': randint(5000, 6000), b'moves_within_lru': 0, b'direct_reclaims': 0, b'lru_bumps_dropped': 0} + ret = "" + temp = { + b"pid": randint(5, 400), + b"uptime": randint(1000, 2000), + b"time": int(time()), + b"version": b"1.5.6", + b"libevent": b"2.1.8-stable", + b"pointer_size": 64, + b"rusage_user": round(uniform(0.1, 0.9), 4), + b"rusage_system": round(uniform(0.1, 0.9), 6), + b"max_connections": 1024, + b"curr_connections": randint(1, 1024), + b"total_connections": 5, + b"rejected_connections": 0, + b"connection_structures": 2, + b"reserved_fds": 20, + b"cmd_get": 0, + b"cmd_set": 40, + b"cmd_flush": 0, + b"cmd_touch": 0, + b"get_hits": 0, + b"get_misses": 0, + b"get_expired": 0, + b"get_flushed": 0, + b"delete_misses": 0, + b"delete_hits": 0, + b"incr_misses": 0, + b"incr_hits": 0, + b"decr_misses": 0, + b"decr_hits": 0, + b"cas_misses": 0, + b"cas_hits": 0, + b"cas_badval": 0, + b"touch_hits": 0, + b"touch_misses": 0, + b"auth_cmds": 0, + b"auth_errors": 0, + b"bytes_read": randint(7000000, 8000000), + b"bytes_written": randint(500000, 1000000), + b"limit_maxbytes": 33554432, + b"accepting_conns": 1, + b"listen_disabled_num": 0, + b"time_in_listen_disabled_us": 0, + b"threads": randint(4, 9000), + b"conn_yields": 0, + b"hash_power_level": 16, + b"hash_bytes": 524288, + b"hash_is_expanding": False, + b"slab_reassign_rescues": 0, + b"slab_reassign_chunk_rescues": 0, + b"slab_reassign_evictions_nomem": 0, + b"slab_reassign_inline_reclaim": 0, + b"slab_reassign_busy_items": 0, + b"slab_reassign_busy_deletes": 0, + b"slab_reassign_running": False, + b"slabs_moved": 0, + b"lru_crawler_running": 0, + b"lru_crawler_starts": randint(500000, 700000), + b"lru_maintainer_juggles": randint(400000, 500000), + b"malloc_fails": 0, + b"log_worker_dropped": 0, + b"log_worker_written": 0, + b"log_watcher_skipped": 0, + b"log_watcher_sent": 0, + b"bytes": randint(13554432, 33554432), + b"curr_items": items, + b"total_items": items, + b"slab_global_page_pool": 0, + b"expired_unfetched": 0, + b"evicted_unfetched": 0, + b"evicted_active": 0, + b"evictions": 0, + b"reclaimed": 0, + b"crawler_reclaimed": 0, + b"crawler_items_checked": randint(5000, 6000), + b"lrutail_reflocked": 0, + b"moves_to_cold": randint(5000, 6000), + b"moves_to_warm": randint(5000, 6000), + b"moves_within_lru": 0, + b"direct_reclaims": 0, + b"lru_bumps_dropped": 0, + } for key, value in temp.items(): key = key.decode() if isinstance(value, bytes): value = value.decode() - ret += 'STAT {} {}\r\n'.format(key, value) + ret += f"STAT {key} {value}\r\n" - ret = ret.encode() + b'END\r\n' + ret = ret.encode() + b"END\r\n" return ret def get_key(self, key): - ret = b'' + ret = b"" with suppress(Exception): random = randint(80000000, 90000000) - temp = 'VALUE {} 0 {}\r\n{}\r\nEND\r\n'.format(key.decode(), len(str(random)), random) + temp = f"VALUE {key.decode()} 0 {len(str(random))}\r\n{random}\r\nEND\r\n" ret = temp.encode() return ret def connectionMade(self): - _q_s.logs.info({'server': 'memcache_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "memcache_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def dataReceived(self, data): with suppress(Exception): - _data = data.split(b'\r\n')[0].split(b' ') - if _data[0] == b'stats': + _data = data.split(b"\r\n")[0].split(b" ") + if _data[0] == b"stats": self.transport.write(self.get_stats()) - elif _data[0] == b'get': + elif _data[0] == b"get": self.transport.write(self.get_key(_data[1])) - elif _data[0] == b'set': + elif _data[0] == b"set": name = _data[1] size = _data[4] - value = data.split(b'\r\n')[1] - self.transport.write(b'STORED\r\n') + value = data.split(b"\r\n")[1] + self.transport.write(b"STORED\r\n") else: - self.transport.write(b'ERROR\r\n') - if _data[0] != b'': - _q_s.logs.info({'server': 'memcache_server', 'action': _data[0].decode(), 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + self.transport.write(b"ERROR\r\n") + if _data[0] != b"": + _q_s.logs.info( + { + "server": "memcache_server", + "action": _data[0].decode(), + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) self.transport.loseConnection() factory = Factory() @@ -97,7 +208,7 @@ def dataReceived(self, data): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -109,13 +220,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' + status = "success" - self.logs.info({'server': 'memcache_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) + self.logs.info( + { + "server": "memcache_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) - if status == 'success': + if status == "success": return True else: self.kill_server() @@ -124,30 +261,33 @@ def run_server(self, process=False, auto=False): self.memcache_server_main() def close_port(self): - ret = close_port_wrapper('memcache_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("memcache_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('memcache_server', self.uuid, self.process) + ret = kill_server_wrapper("memcache_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from warnings import filterwarnings - filterwarnings(action='ignore', module='.*socket.*') + + filterwarnings(action="ignore", module=".*socket.*") from socket import socket, AF_INET, SOCK_STREAM _ip = ip or self.ip _port = port or self.port c = socket(AF_INET, SOCK_STREAM) c.connect((_ip, _port)) - c.send(b'stats\r\n') + c.send(b"stats\r\n") data, address = c.recvfrom(10000) c.close() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - QMemcacheServer = QMemcacheServer(ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config) + QMemcacheServer = QMemcacheServer( + ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config + ) QMemcacheServer.run_server() diff --git a/honeypots/mssql_server.py b/honeypots/mssql_server.py index 3bc6a14..8b0e0e9 100644 --- a/honeypots/mssql_server.py +++ b/honeypots/mssql_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,48 +8,64 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor -from twisted.python import log as tlog -from struct import pack +from binascii import hexlify, unhexlify +from contextlib import suppress +from os import getenv, path +from struct import pack, unpack from subprocess import Popen -from os import path, getenv -from struct import unpack, pack -from binascii import unhexlify, hexlify -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running from uuid import uuid4 -from contextlib import suppress +from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol -class QMSSQLServer(): +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) + + +class QMSSQLServer: def __init__(self, **kwargs): self.auto_disabled = None self.file_name = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 1433 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 1433 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def mssql_server_main(self): _q_s = self class CustomMSSQLProtocol(Protocol): - _state = None def check_bytes(self, string): @@ -58,53 +74,112 @@ def check_bytes(self, string): else: return str(string) - def create_payload(self, server_name=b'', token_error_msg=b'', error_code=2): - ret = '040100c000350100aaa80002000000010e440041006e0020006500720072006f007200200068006100730020006f00630063007500720072006500640020007700680069006c0065002000650073007400610062006c0069007300680069006e00670020006100200063006f006e006e0065006300740069006f006e00200074006f00200074006800650020007300650072007600650072002e00095200260044006200610063006b00750070000001000000fd020000000000000000000000' + def create_payload(self, server_name=b"", token_error_msg=b"", error_code=2): + ret = "040100c000350100aaa80002000000010e440041006e0020006500720072006f007200200068006100730020006f00630063007500720072006500640020007700680069006c0065002000650073007400610062006c0069007300680069006e00670020006100200063006f006e006e0065006300740069006f006e00200074006f00200074006800650020007300650072007600650072002e00095200260044006200610063006b00750070000001000000fd020000000000000000000000" with suppress(Exception): - if server_name == b'': - server_name = b'R&Dbackup' - if token_error_msg == b'': - token_error_msg = b'An error has occurred while establishing a connection to the server.' - server_name_hex = ('00'.join(hex(c)[2:] for c in server_name)).encode('utf-8') + b'00' - server_name_hex_len = hexlify(pack('b', len(server_name))) - token_error_msg_hex = ('00'.join(hex(c)[2:] for c in token_error_msg)).encode('utf-8') + b'00' - token_error_msg_hex_len = hexlify(pack('H', len(unhexlify(data_stream)))) + data_stream[8:] + if server_name == b"": + server_name = b"R&Dbackup" + if token_error_msg == b"": + token_error_msg = ( + b"An error has occurred while establishing a connection to the server." + ) + server_name_hex = ("00".join(hex(c)[2:] for c in server_name)).encode( + "utf-8" + ) + b"00" + server_name_hex_len = hexlify(pack("b", len(server_name))) + token_error_msg_hex = ("00".join(hex(c)[2:] for c in token_error_msg)).encode( + "utf-8" + ) + b"00" + token_error_msg_hex_len = hexlify(pack("H", len(unhexlify(data_stream)))) + + data_stream[8:] + ) return ret def connectionMade(self): self._state = 1 - _q_s.logs.info({'server': 'mssql_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "mssql_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def dataReceived(self, data): if self._state == 1: - version = b'11000000' - if(data[0] == 0x12): - self.transport.write(unhexlify(b'0401002500000100000015000601001b000102001c000103001d0000ff' + version + b'00000200')) - elif(data[0] == 0x10): - value_start, value_length = unpack('=HH', data[48:52]) - username = data[8 + value_start:8 + value_start + (value_length * 2)].replace(b'\x00', b'').decode('utf-8') - value_start, value_length = unpack('=HH', data[52:56]) - password = data[8 + value_start:8 + value_start + (value_length * 2)] - password = password.replace(b'\x00', b'').replace(b'\xa5', b'') - password_decrypted = '' + version = b"11000000" + if data[0] == 0x12: + self.transport.write( + unhexlify( + b"0401002500000100000015000601001b000102001c000103001d0000ff" + + version + + b"00000200" + ) + ) + elif data[0] == 0x10: + value_start, value_length = unpack("=HH", data[48:52]) + username = ( + data[8 + value_start : 8 + value_start + (value_length * 2)] + .replace(b"\x00", b"") + .decode("utf-8") + ) + value_start, value_length = unpack("=HH", data[52:56]) + password = data[8 + value_start : 8 + value_start + (value_length * 2)] + password = password.replace(b"\x00", b"").replace(b"\xa5", b"") + password_decrypted = "" for x in password: - password_decrypted += chr(((x ^ 0xa5) & 0x0F) << 4 | ((x ^ 0xa5) & 0xF0) >> 4) + password_decrypted += chr( + ((x ^ 0xA5) & 0x0F) << 4 | ((x ^ 0xA5) & 0xF0) >> 4 + ) username = self.check_bytes(username) password = self.check_bytes(password_decrypted) - status = 'failed' + status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'mssql_server', 'action': 'login', 'status': status, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + status = "success" + _q_s.logs.info( + { + "server": "mssql_server", + "action": "login", + "status": status, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) - self.transport.write(unhexlify(self.create_payload(token_error_msg=b'Login Failed', error_code=18456))) + self.transport.write( + unhexlify( + self.create_payload( + token_error_msg=b"Login Failed", error_code=18456 + ) + ) + ) else: self.transport.loseConnection() @@ -117,7 +192,7 @@ def connectionLost(self, reason): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -129,13 +204,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' + status = "success" - self.logs.info({'server': 'mssql_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) + self.logs.info( + { + "server": "mssql_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) - if status == 'success': + if status == "success": return True else: self.kill_server() @@ -144,26 +251,36 @@ def run_server(self, process=False, auto=False): self.mssql_server_main() def close_port(self): - ret = close_port_wrapper('mssql_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("mssql_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('mssql_server', self.uuid, self.process) + ret = kill_server_wrapper("mssql_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from pymssql import connect as pconnect + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password - conn = pconnect(host=_ip, port=str(_port), user=_username, password=_password, database='dbname') + conn = pconnect( + host=_ip, port=str(_port), user=_username, password=_password, database="dbname" + ) cursor = conn.cursor() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - QMSSQLServer = QMSSQLServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + QMSSQLServer = QMSSQLServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) QMSSQLServer.run_server() diff --git a/honeypots/mysql_server.py b/honeypots/mysql_server.py index 0bc56e8..8742d71 100644 --- a/honeypots/mysql_server.py +++ b/honeypots/mysql_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,78 +8,129 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor -from twisted.python import log as tlog -from struct import pack +from contextlib import suppress from hashlib import sha1 +from os import getenv, path +from struct import pack from subprocess import Popen -from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running from uuid import uuid4 -from contextlib import suppress + +from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QMysqlServer(): +class QMysqlServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 3306 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 3306 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) self.words = [self.password.encode()] - disable_logger(1, tlog) - def load_words(self,): - with open(self.file_name, 'r', encoding='utf-8') as file: + def load_words( + self, + ): + with open(self.file_name, encoding="utf-8") as file: self.words = file.read().splitlines() def greeting(self): - base = ['\x0a', '5.7.00' + '\0', '\x36\x00\x00\x00', '12345678' + '\0', '\xff\xf7', '\x21', '\x02\x00', '\x0f\x81', '\x15', '\0' * 10, '123456789012' + '\0', 'mysql_native_password' + '\0'] - payload_len = list(pack('> 32) - 2208988800.0 - f = float(int(i) & 0xffffffff) / (4294967296) + f = float(int(i) & 0xFFFFFFFF) / 4294967296 return i, f def datagramReceived(self, data, addr): - version = 'UnKnown' - mode = 'UnKnown' - success = 'failed' - unpacked = None - _q_s.logs.info({'server': 'ntp_server', 'action': 'connection', 'src_ip': addr[0], 'src_port': addr[1]}) - if len(data) == calcsize('!B B B b I I I Q Q Q Q'): + version = "UnKnown" + mode = "UnKnown" + _q_s.logs.info( + { + "server": "ntp_server", + "action": "connection", + "src_ip": addr[0], + "src_port": addr[1], + } + ) + try: version = data[0] >> 3 & 0x7 mode = data[0] & 0x7 - unpacked = unpack('!B B B b I I I Q Q Q Q', data) - if unpacked is not None: - i, f = self.system_time_to_ntp(time()) - response = pack('!B B B b I I I Q Q Q Q', 0 << 6 | 3 << 3 | 2, data[1], data[2], data[3], 0, 0, 0, 0, data[10], 0, i + f) - self.transport.write(response, addr) - success = 'success' - - _q_s.logs.info({'server': 'ntp_server', 'action': 'query', 'status': 'success', 'src_ip': addr[0], 'src_port': addr[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': {'version': version, 'mode': mode}}) - self.transport.loseConnection() - - reactor.listenUDP(port=self.port, protocol=CustomDatagramProtocolProtocol(), interface=self.ip) + i, f = self.system_time_to_ntp(time()) + response = pack( + "!B B B b I I I Q Q Q Q", + 0 << 6 | 3 << 3 | 2, + data[1], + data[2], + data[3], + 0, + 0, + 0, + 0, + data[10], + 0, + i + f, + ) + self.transport.write(response, addr) + status = "success" + except (struct.error, TypeError, IndexError): + status = "error" + + _q_s.logs.info( + { + "server": "ntp_server", + "action": "query", + "status": status, + "src_ip": addr[0], + "src_port": addr[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": {"version": version, "mode": mode}, + } + ) + + reactor.listenUDP( + port=self.port, protocol=CustomDatagramProtocolProtocol(), interface=self.ip + ) reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -92,13 +140,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'ntp_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "ntp_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -107,30 +181,38 @@ def run_server(self, process=False, auto=False): self.ntp_server_main() def close_port(self): - ret = close_port_wrapper('ntp_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("ntp_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('ntp_server', self.uuid, self.process) + ret = kill_server_wrapper("ntp_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from warnings import filterwarnings - filterwarnings(action='ignore', module='.*socket.*') + + filterwarnings(action="ignore", module=".*socket.*") from socket import socket, AF_INET, SOCK_DGRAM _ip = ip or self.ip _port = port or self.port c = socket(AF_INET, SOCK_DGRAM) - c.sendto(b'\x1b' + 47 * b'\0', (_ip, _port)) + c.sendto(b"\x1b" + 47 * b"\0", (_ip, _port)) data, address = c.recvfrom(256) - ret_time = unpack('!12I', data)[10] - 2208988800 + ret_time = unpack("!12I", data)[10] - 2208988800 c.close() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - QNTPServer = QNTPServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + QNTPServer = QNTPServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) QNTPServer.run_server() diff --git a/honeypots/oracle_server.py b/honeypots/oracle_server.py index 2eea583..803cbfa 100644 --- a/honeypots/oracle_server.py +++ b/honeypots/oracle_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,54 +8,71 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor -from twisted.python import log as tlog -from subprocess import Popen -from os import path, getenv -from struct import unpack +from contextlib import suppress +from os import getenv, path from re import findall -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running, set_local_vars +from struct import unpack +from subprocess import Popen from uuid import uuid4 -from contextlib import suppress +from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QOracleServer(): + +class QOracleServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 1521 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 1521 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def oracle_server_main(self): _q_s = self class CustomRedisProtocol(Protocol): - _state = None def wrong_password(self): - payload = b'\x02B\xc5\xbb\xe7\x7f\x02B\xac\x11\x00\x02\x08\x00E\x00\x01\x02Md@\x00@\x06\x94l\xac\x11\x00\x02\xac\x11\x00\x01\x05\xf1\xa5\xa8\xab\xf5\xff\x94\x98\xdf\xd5\xa1\x80\x18\x01\xf5Y\x1a\x00\x00\x01\x01\x08\nJ\xe7\xf0,\xb2,\xfe\x08\x00\x00\x00\xce\x06\x00\x00\x00\x00\x00\x04\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\xf9\x03\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x006\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x82\x1c\x86u\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf9\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003ORA-01017: invalid username/password; logon denied\n' + payload = b"\x02B\xc5\xbb\xe7\x7f\x02B\xac\x11\x00\x02\x08\x00E\x00\x01\x02Md@\x00@\x06\x94l\xac\x11\x00\x02\xac\x11\x00\x01\x05\xf1\xa5\xa8\xab\xf5\xff\x94\x98\xdf\xd5\xa1\x80\x18\x01\xf5Y\x1a\x00\x00\x01\x01\x08\nJ\xe7\xf0,\xb2,\xfe\x08\x00\x00\x00\xce\x06\x00\x00\x00\x00\x00\x04\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\xf9\x03\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x006\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x82\x1c\x86u\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf9\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003ORA-01017: invalid username/password; logon denied\n" return payload def refuse_payload(self): - payload = b'\x00\x08\x00\x00\x04\x00\x00\x00' + payload = b"\x00\x08\x00\x00\x04\x00\x00\x00" return payload def parse_payload(self, data): @@ -63,30 +80,59 @@ def parse_payload(self, data): program = None local_user = None with suppress(Exception): - packet_len, packet_checksum, packet_type, packet_reserved_bytes, packet_header_checksum = unpack('>hhbbh', data[0:8]) - if b'(DESCRIPTION=' in data: - connect = data[data.index(b'(DESCRIPTION='):].split(b'\0')[0] - found_temp = findall(rb'[^\(\)]+', connect) + ( + packet_len, + packet_checksum, + packet_type, + packet_reserved_bytes, + packet_header_checksum, + ) = unpack(">hhbbh", data[0:8]) + if b"(DESCRIPTION=" in data: + connect = data[data.index(b"(DESCRIPTION=") :].split(b"\0")[0] + found_temp = findall(rb"[^\(\)]+", connect) if len(found_temp) > 0: - found_fixed = [item for item in found_temp if not item.endswith(b'=')] + found_fixed = [item for item in found_temp if not item.endswith(b"=")] if len(found_fixed) > 0: for item in found_fixed: - name, value = item.split(b'=') - if name.startswith(b'SERVICE_NAME'): + name, value = item.split(b"=") + if name.startswith(b"SERVICE_NAME"): service_name = value.decode() - elif name.startswith(b'PROGRAM'): + elif name.startswith(b"PROGRAM"): program = value.decode() - elif name.startswith(b'USER'): + elif name.startswith(b"USER"): local_user = value.decode() return service_name, program, local_user def connectionMade(self): - _q_s.logs.info({'server': 'oracle_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "oracle_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def dataReceived(self, data): service_name, program, local_user = self.parse_payload(data) if service_name or program or local_user: - _q_s.logs.info({'server': 'oracle_server', 'action': 'login', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': {'local_user': local_user, 'program': program, 'service_name': service_name}}) + _q_s.logs.info( + { + "server": "oracle_server", + "action": "login", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": { + "local_user": local_user, + "program": program, + "service_name": service_name, + }, + } + ) self.transport.write(self.refuse_payload()) self.transport.loseConnection() @@ -96,7 +142,7 @@ def dataReceived(self, data): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -108,13 +154,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'oracle_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "oracle_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -123,20 +195,21 @@ def run_server(self, process=False, auto=False): self.oracle_server_main() def close_port(self): - ret = close_port_wrapper('oracle_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("oracle_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('oracle_server', self.uuid, self.process) + ret = kill_server_wrapper("oracle_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from warnings import filterwarnings - filterwarnings(action='ignore', module='.*socket.*') + + filterwarnings(action="ignore", module=".*socket.*") from socket import socket, AF_INET, SOCK_STREAM - payload = b'\x00\x00\x03\x04\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x01F\xb9\xd9@\x00@\x06\x81\xd6\x7f\x00\x00\x01\x7f\x00\x00\x01\xbf\xce\x06\x13\xacW\xde\xc0Z\xb5\x0cI\x80\x18\x02\x00\xff:\x00\x00\x01\x01\x08\n\x1bdZ^\x1bdZ^\x01\x12\x00\x00\x01\x00\x00\x00\x01>\x01,\x0cA \x00\xff\xff\x7f\x08\x00\x00\x01\x00\x00\xc8\x00J\x00\x00\x14\x00AA\xa7C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x01(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=xe)(CID=(PROGRAM=linux_1)(HOST=xxxxxxxxxxxxxx)(USER=xxxxxxxxxxxxxx))(CONNECTION_ID=xxxxxxxxxxxxxxxxxxxxxxxx))(ADDRESS=(PROTOCOL=tcp)(HOST=xxxxxxx)(PORT=xxxx)))' + payload = b"\x00\x00\x03\x04\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x01F\xb9\xd9@\x00@\x06\x81\xd6\x7f\x00\x00\x01\x7f\x00\x00\x01\xbf\xce\x06\x13\xacW\xde\xc0Z\xb5\x0cI\x80\x18\x02\x00\xff:\x00\x00\x01\x01\x08\n\x1bdZ^\x1bdZ^\x01\x12\x00\x00\x01\x00\x00\x00\x01>\x01,\x0cA \x00\xff\xff\x7f\x08\x00\x00\x01\x00\x00\xc8\x00J\x00\x00\x14\x00AA\xa7C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x01(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=xe)(CID=(PROGRAM=linux_1)(HOST=xxxxxxxxxxxxxx)(USER=xxxxxxxxxxxxxx))(CONNECTION_ID=xxxxxxxxxxxxxxxxxxxxxxxx))(ADDRESS=(PROTOCOL=tcp)(HOST=xxxxxxx)(PORT=xxxx)))" _ip = ip or self.ip _port = port or self.port c = socket(AF_INET, SOCK_STREAM) @@ -146,8 +219,15 @@ def test_server(self, ip=None, port=None, username=None, password=None): c.close() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - QOracleServer = QOracleServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + QOracleServer = QOracleServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) QOracleServer.run_server() diff --git a/honeypots/pjl_server.py b/honeypots/pjl_server.py index da3cbc1..c7dd6bd 100644 --- a/honeypots/pjl_server.py +++ b/honeypots/pjl_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,85 +8,117 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor -from twisted.python import log as tlog +from contextlib import suppress +from os import getenv, path from subprocess import Popen -from os import path, getenv -from re import split as resplit -from struct import unpack -from binascii import unhexlify -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running from uuid import uuid4 -from contextlib import suppress +from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QPJLServer(): + +class QPJLServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.printer = b'Brother HL-L2360' - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.printer = b"Brother HL-L2360" + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 9100 - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - self.template = {'ProductName': 'Brother HL-L2360', - 'FormatterNumber': 'Q910CHL', - 'PrinterNumber': 'L2360', - 'ProductSerialNumber': 'VNB1897514', - 'ServiceID': '20157', - 'FirmwareDateCode': '20051103', - 'MaxPrintResolution': '900', - 'ControllerNumber': 'Q910CHL', - 'DeviceDescription': 'Brother HL-L2360', - 'DeviceLang': 'ZJS PJL', - 'TotalMemory': '6890816', - 'AvailableMemory': '3706526', - 'Personality': '0', - 'EngFWVer': '10', - 'IPAddress': '172.17.0.2', - 'HWAddress': '0025B395EA01'} - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 9100 + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) + self.template = { + "ProductName": "Brother HL-L2360", + "FormatterNumber": "Q910CHL", + "PrinterNumber": "L2360", + "ProductSerialNumber": "VNB1897514", + "ServiceID": "20157", + "FirmwareDateCode": "20051103", + "MaxPrintResolution": "900", + "ControllerNumber": "Q910CHL", + "DeviceDescription": "Brother HL-L2360", + "DeviceLang": "ZJS PJL", + "TotalMemory": "6890816", + "AvailableMemory": "3706526", + "Personality": "0", + "EngFWVer": "10", + "IPAddress": "172.17.0.2", + "HWAddress": "0025B395EA01", + } def pjl_server_main(self): _q_s = self class Custompjlrotocol(Protocol): - _state = None def check_bytes(self, string): if isinstance(string, bytes): - return string.decode() + return string.decode(errors="replace") else: return str(string) def connectionMade(self): self._state = 1 - _q_s.logs.info({'server': 'pjl_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "pjl_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def dataReceived(self, data): # Control to PJL (Removed) - data = data.replace(b'\x1b%-12345X', b'') - if data.lower().startswith(b'@pjl echo'): - self.transport.write(b'@PJL ' + data[10:] + b'\x1b') - elif data.lower().startswith(b'@pjl info id'): - self.transport.write(b'@PJL INFO ID\r\n' + _q_s.printer + b'\r\n\x1b') - elif data.lower().startswith(b'@pjl prodinfo'): - prodinfo = '\r\n'.join([k + " = " + v for k, v in _q_s.template.items()]) - self.transport.write(prodinfo.encode('utf-8') + b'\x1b') - _q_s.logs.info({'server': 'ntp_server', 'action': 'query', 'status': 'success', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': {'command': self.check_bytes(data)}}) + data = data.replace(b"\x1b%-12345X", b"") + if data.lower().startswith(b"@pjl echo"): + self.transport.write(b"@PJL " + data[10:] + b"\x1b") + elif data.lower().startswith(b"@pjl info id"): + self.transport.write(b"@PJL INFO ID\r\n" + _q_s.printer + b"\r\n\x1b") + elif data.lower().startswith(b"@pjl prodinfo"): + prodinfo = "\r\n".join([k + " = " + v for k, v in _q_s.template.items()]) + self.transport.write(prodinfo.encode("utf-8") + b"\x1b") + _q_s.logs.info( + { + "server": "pjl_server", + "action": "query", + "status": "success", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": {"command": self.check_bytes(data)}, + } + ) self.transport.loseConnection() def connectionLost(self, reason): @@ -98,7 +130,7 @@ def connectionLost(self, reason): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -110,13 +142,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'pjl_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "pjl_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -125,28 +183,31 @@ def run_server(self, process=False, auto=False): self.pjl_server_main() def close_port(self): - ret = close_port_wrapper('pjl_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("pjl_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('pjl_server', self.uuid, self.process) + ret = kill_server_wrapper("pjl_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from warnings import filterwarnings - filterwarnings(action='ignore', module='.*socket.*') + + filterwarnings(action="ignore", module=".*socket.*") from socket import socket, AF_INET, SOCK_STREAM _ip = ip or self.ip _port = port or self.port c = socket(AF_INET, SOCK_STREAM) - c.sendto(b'\x1b%-12345X@PJL prodinfo', (_ip, _port)) + c.sendto(b"\x1b%-12345X@PJL prodinfo", (_ip, _port)) c.close() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qpjlserver = QPJLServer(ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config) + qpjlserver = QPJLServer( + ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config + ) qpjlserver.run_server() diff --git a/honeypots/pop3_server.py b/honeypots/pop3_server.py index 1087454..09ffaee 100644 --- a/honeypots/pop3_server.py +++ b/honeypots/pop3_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,47 +8,64 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' -from typing import Tuple -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.mail.pop3 import POP3, POP3Error -from twisted.internet.protocol import Factory -from twisted.internet import reactor +""" +from contextlib import suppress +from os import getenv, path from random import choice -from twisted.python import log as tlog from subprocess import Popen -from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from typing import Tuple from uuid import uuid4 -from contextlib import suppress +from twisted.internet import reactor +from twisted.internet.protocol import Factory +from twisted.mail.pop3 import POP3, POP3Error + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QPOP3Server(): + +class QPOP3Server: def __init__(self, **kwargs): self.auto_disabled = None - self.mocking_server = choice(['Microsoft Exchange POP3 service is ready']) + self.mocking_server = choice(["Microsoft Exchange POP3 service is ready"]) self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 110 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 110 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def pop3_server_main(self): _q_s = self class CustomPOP3Protocol(POP3): - self._user = None def check_bytes(self, string): @@ -58,50 +75,85 @@ def check_bytes(self, string): return str(string) def connectionMade(self): - _q_s.logs.info({'server': 'pop3_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "pop3_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) self._user = None - self.successResponse('{}'.format(_q_s.mocking_server)) + self.successResponse(f"{_q_s.mocking_server}") def processCommand(self, command: bytes, *args): - with suppress(Exception): if "capture_commands" in _q_s.options: - _q_s.logs.info({'server': 'pop3_server', 'action': 'command', 'data': {"cmd": self.check_bytes(command), "args": self.check_bytes(b" ".join(args))}, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "pop3_server", + "action": "command", + "data": { + "cmd": self.check_bytes(command), + "args": self.check_bytes(b" ".join(args)), + }, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) - if not (command.lower().startswith(b'user') or command.lower().startswith(b'pass')): - self.failResponse('Authentication failed') - return + if not ( + command.lower().startswith(b"user") or command.lower().startswith(b"pass") + ): + self.failResponse("Authentication failed") + return None if self.blocked is not None: self.blocked.append((command, args)) - return + return None command = command.upper() authCmd = command in self.AUTH_CMDS if not self.mbox and not authCmd: raise POP3Error(b"not authenticated yet: cannot do " + command) - f = getattr(self, "do_{}".format(self.check_bytes(command)), None) + f = getattr(self, f"do_{self.check_bytes(command)}", None) if f: return f(*args) raise POP3Error(b"Unknown protocol command: " + command) def do_USER(self, user): self._user = user - self.successResponse('USER Ok') + self.successResponse("USER Ok") def do_PASS(self, password: bytes, *words: Tuple[bytes]): if self._user: username = self.check_bytes(self._user) password = self.check_bytes(b" ".join((password,) + words)) - status = 'failed' + status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'pop3_server', 'action': 'login', 'status': status, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) - self.failResponse('Authentication failed') + status = "success" + _q_s.logs.info( + { + "server": "pop3_server", + "action": "login", + "status": status, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) + self.failResponse("Authentication failed") else: - self.failResponse('USER first, then PASS') + self.failResponse("USER first, then PASS") self._user = None @@ -120,7 +172,7 @@ def buildProtocol(self, address): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -132,13 +184,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' + status = "success" - self.logs.info({'server': 'pop3_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) + self.logs.info( + { + "server": "pop3_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) - if status == 'success': + if status == "success": return True else: self.kill_server() @@ -147,16 +231,17 @@ def run_server(self, process=False, auto=False): self.pop3_server_main() def close_port(self): - ret = close_port_wrapper('pop3_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("pop3_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('pop3_server', self.uuid, self.process) + ret = kill_server_wrapper("pop3_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from poplib import POP3 as poplibPOP3 + _ip = ip or self.ip _port = port or self.port _username = username or self.username @@ -167,8 +252,15 @@ def test_server(self, ip=None, port=None, username=None, password=None): pp.pass_(_password) -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qpop3server = QPOP3Server(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qpop3server = QPOP3Server( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qpop3server.run_server() diff --git a/honeypots/postgres_server.py b/honeypots/postgres_server.py index 79412ef..d0dd44c 100644 --- a/honeypots/postgres_server.py +++ b/honeypots/postgres_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,45 +8,62 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor +from contextlib import suppress +from os import getenv, path from struct import unpack -from twisted.python import log as tlog from subprocess import Popen -from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running from uuid import uuid4 -from contextlib import suppress +from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QPostgresServer(): + +class QPostgresServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 5432 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 5432 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def postgres_server_main(self): _q_s = self class CustomPostgresProtocol(Protocol): - _state = None _variables = {} @@ -57,39 +74,60 @@ def check_bytes(self, string): return str(string) def read_data_custom(self, data): - _data = data.decode('utf-8') - length = unpack('!I', data[0:4]) - encoded_list = (_data[8:-1].split('\x00')) + _data = data.decode("utf-8") + length = unpack("!I", data[0:4]) + encoded_list = _data[8:-1].split("\x00") self._variables = dict(zip(*([iter(encoded_list)] * 2))) def read_password_custom(self, data): - data = data.decode('utf-8') - self._variables['password'] = data[5:].split('\x00')[0] + data = data.decode("utf-8") + self._variables["password"] = data[5:].split("\x00")[0] def connectionMade(self): self._state = 1 self._variables = {} - _q_s.logs.info({'server': 'postgres_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "postgres_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def dataReceived(self, data): if self._state == 1: self._state = 2 - self.transport.write(b'N') + self.transport.write(b"N") elif self._state == 2: self.read_data_custom(data) self._state = 3 - self.transport.write(b'R\x00\x00\x00\x08\x00\x00\x00\x03') + self.transport.write(b"R\x00\x00\x00\x08\x00\x00\x00\x03") elif self._state == 3: - if data[0] == 112 and 'user' in self._variables: + if data[0] == 112 and "user" in self._variables: self.read_password_custom(data) - username = self.check_bytes(self._variables['user']) - password = self.check_bytes(self._variables['password']) - status = 'failed' + username = self.check_bytes(self._variables["user"]) + password = self.check_bytes(self._variables["password"]) + status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'postgres_server', 'action': 'login', 'status': status, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + status = "success" + _q_s.logs.info( + { + "server": "postgres_server", + "action": "login", + "status": status, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) self.transport.loseConnection() else: @@ -105,7 +143,7 @@ def connectionLost(self, reason): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -117,13 +155,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'postgres_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "postgres_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -132,16 +202,17 @@ def run_server(self, process=False, auto=False): self.postgres_server_main() def close_port(self): - ret = close_port_wrapper('postgres_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("postgres_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('postgres_server', self.uuid, self.process) + ret = kill_server_wrapper("postgres_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): - from psycopg2 import sql, connect + from psycopg2 import connect + _ip = ip or self.ip _port = port or self.port _username = username or self.username @@ -149,8 +220,15 @@ def test_server(self, ip=None, port=None, username=None, password=None): x = connect(host=_ip, port=_port, user=_username, password=_password) -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qpostgresserver = QPostgresServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qpostgresserver = QPostgresServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qpostgresserver.run_server() diff --git a/honeypots/qbsniffer.py b/honeypots/qbsniffer.py index 3896b45..b0c27ef 100644 --- a/honeypots/qbsniffer.py +++ b/honeypots/qbsniffer.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,36 +8,75 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" from warnings import filterwarnings -filterwarnings(action='ignore', module='.*scapy.*') +from cryptography.utils import CryptographyDeprecationWarning + +filterwarnings(action="ignore", category=CryptographyDeprecationWarning) -from scapy.all import * -from sys import stdout -from binascii import hexlify -from netifaces import ifaddresses, AF_INET, AF_LINK from binascii import hexlify from multiprocessing import Process -from re import search as rsearch -from re import compile as rcompile -from honeypots.helper import server_arguments, setup_logger +from re import compile as rcompile, search as rsearch +from sys import stdout from uuid import uuid4 +from netifaces import AF_INET, AF_LINK, ifaddresses +from scapy.all import * + +from honeypots.helper import setup_logger -class QBSniffer(): - def __init__(self, filter=None, interface=None, config=''): - self.current_ip = ifaddresses(interface)[AF_INET][0]['addr'].encode('utf-8') - self.current_mac = ifaddresses(interface)[AF_LINK][0]['addr'].encode('utf-8') + +class QBSniffer: + def __init__(self, filter=None, interface=None, config=""): + self.current_ip = ifaddresses(interface)[AF_INET][0]["addr"].encode("utf-8") + self.current_mac = ifaddresses(interface)[AF_LINK][0]["addr"].encode("utf-8") self.filter = filter self.interface = interface - self.method = 'TCPUDP' - self.ICMP_codes = [(0, 0, 'Echo/Ping reply'), (3, 0, 'Destination network unreachable'), (3, 1, 'Destination host unreachable'), (3, 2, 'Desination protocol unreachable'), (3, 3, 'Destination port unreachable'), (3, 4, 'Fragmentation required'), (3, 5, 'Source route failed'), (3, 6, 'Destination network unknown'), (3, 7, 'Destination host unknown'), (3, 8, 'Source host isolated'), (3, 9, 'Network administratively prohibited'), (3, 10, 'Host administratively prohibited'), (3, 11, 'Network unreachable for TOS'), (3, 12, 'Host unreachable for TOS'), (3, 13, 'Communication administratively prohibited'), (3, 14, 'Host Precedence Violation'), (3, 15, 'Precendence cutoff in effect'), (4, 0, 'Source quench'), - (5, 0, 'Redirect Datagram for the Network'), (5, 1, 'Redirect Datagram for the Host'), (5, 2, 'Redirect Datagram for the TOS & network'), (5, 3, 'Redirect Datagram for the TOS & host'), (8, 0, 'Echo/Ping Request'), (9, 0, 'Router advertisement'), (10, 0, 'Router discovery/selection/solicitation'), (11, 0, 'TTL expired in transit'), (11, 1, 'Fragment reassembly time exceeded'), (12, 0, 'Pointer indicates the error'), (12, 1, 'Missing a required option'), (12, 2, 'Bad length'), (13, 0, 'Timestamp'), (14, 0, 'Timestamp Reply'), (15, 0, 'Information Request'), (16, 0, 'Information Reply'), (17, 0, 'Address Mask Request'), (18, 0, 'Address Mask Reply'), (30, 0, 'Information Request')] + self.method = "TCPUDP" + self.ICMP_codes = [ + (0, 0, "Echo/Ping reply"), + (3, 0, "Destination network unreachable"), + (3, 1, "Destination host unreachable"), + (3, 2, "Desination protocol unreachable"), + (3, 3, "Destination port unreachable"), + (3, 4, "Fragmentation required"), + (3, 5, "Source route failed"), + (3, 6, "Destination network unknown"), + (3, 7, "Destination host unknown"), + (3, 8, "Source host isolated"), + (3, 9, "Network administratively prohibited"), + (3, 10, "Host administratively prohibited"), + (3, 11, "Network unreachable for TOS"), + (3, 12, "Host unreachable for TOS"), + (3, 13, "Communication administratively prohibited"), + (3, 14, "Host Precedence Violation"), + (3, 15, "Precendence cutoff in effect"), + (4, 0, "Source quench"), + (5, 0, "Redirect Datagram for the Network"), + (5, 1, "Redirect Datagram for the Host"), + (5, 2, "Redirect Datagram for the TOS & network"), + (5, 3, "Redirect Datagram for the TOS & host"), + (8, 0, "Echo/Ping Request"), + (9, 0, "Router advertisement"), + (10, 0, "Router discovery/selection/solicitation"), + (11, 0, "TTL expired in transit"), + (11, 1, "Fragment reassembly time exceeded"), + (12, 0, "Pointer indicates the error"), + (12, 1, "Missing a required option"), + (12, 2, "Bad length"), + (13, 0, "Timestamp"), + (14, 0, "Timestamp Reply"), + (15, 0, "Information Request"), + (16, 0, "Information Reply"), + (17, 0, "Address Mask Request"), + (18, 0, "Address Mask Reply"), + (30, 0, "Information Request"), + ] self.allowed_ports = [] self.allowed_ips = [] - self.common = rcompile(rb'pass|user|login') - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] + self.common = rcompile(rb"pass|user|login") + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] self.config = config if config: self.logs = setup_logger(__class__.__name__, self.uuid, config) @@ -48,7 +87,7 @@ def find_ICMP(self, x1, x2): for _ in self.ICMP_codes: if x1 == _[0] and x2 == _[1]: return _[2] - return 'None' + return "None" def get_layers(self, packet): try: @@ -63,48 +102,176 @@ def scapy_sniffer_main(self): _q_s = self def capture_logic(packet): - _layers, hex_payloads, raw_payloads, _fields, _raw, _hex = [], {}, {}, {}, 'None', 'None' + _layers, hex_payloads, raw_payloads, _fields, _raw, _hex = ( + [], + {}, + {}, + {}, + "None", + "None", + ) _layers = list(self.get_layers(packet)) for layer in _layers: try: _fields[layer] = packet[layer].fields - if 'load' in _fields[layer]: - raw_payloads[layer] = _fields[layer]['load'] - hex_payloads[layer] = hexlify(_fields[layer]['load']) + if "load" in _fields[layer]: + raw_payloads[layer] = _fields[layer]["load"] + hex_payloads[layer] = hexlify(_fields[layer]["load"]) if rsearch(self.common, raw_payloads[layer]): - _q_s.logs.info(['sniffer', {'action': 'creds_check', 'payload': raw_payloads[layer]}]) + _q_s.logs.info( + [ + "sniffer", + {"action": "creds_check", "payload": raw_payloads[layer]}, + ] + ) except Exception as e: - _q_s.logs.error(['errors', {'error': 'capture_logic_1', 'type': 'error -> ' + repr(e)}]) + _q_s.logs.error( + ["errors", {"error": "capture_logic_1", "type": "error -> " + repr(e)}] + ) try: - if _q_s.method == 'ALL': + if _q_s.method == "ALL": try: - _q_s.logs.info(['sniffer', {'action': 'all', 'ip': _q_s.current_ip, 'mac': _q_s.current_mac, 'layers': _layers, 'fields': _fields, 'payload': hex_payloads}]) + _q_s.logs.info( + [ + "sniffer", + { + "action": "all", + "ip": _q_s.current_ip, + "mac": _q_s.current_mac, + "layers": _layers, + "fields": _fields, + "payload": hex_payloads, + }, + ] + ) except Exception as e: - _q_s.logs.error(['errors', {'error': 'capture_logic_2', 'type': 'error -> ' + repr(e)}]) - elif _q_s.method == 'TCPUDP': - if packet.haslayer('IP') and len(hex_payloads) > 0 and packet['IP'].src != _q_s.current_ip: - if packet.haslayer('TCP'): + _q_s.logs.error( + ["errors", {"error": "capture_logic_2", "type": "error -> " + repr(e)}] + ) + elif _q_s.method == "TCPUDP": + if ( + packet.haslayer("IP") + and len(hex_payloads) > 0 + and packet["IP"].src != _q_s.current_ip + ): + if packet.haslayer("TCP"): try: - _q_s.logs.info(['sniffer', {'action': 'tcppayload', 'ip': _q_s.current_ip, 'mac': _q_s.current_mac, 'dest_ip': packet['IP'].src, 'dest_port':packet['TCP'].sport, 'dst_ip':packet['IP'].dst, 'dst_port':packet['TCP'].dport, 'raw_payload':raw_payloads, 'payload':hex_payloads}]) + _q_s.logs.info( + [ + "sniffer", + { + "action": "tcppayload", + "ip": _q_s.current_ip, + "mac": _q_s.current_mac, + "dest_ip": packet["IP"].src, + "dest_port": packet["TCP"].sport, + "dst_ip": packet["IP"].dst, + "dst_port": packet["TCP"].dport, + "raw_payload": raw_payloads, + "payload": hex_payloads, + }, + ] + ) except Exception as e: - _q_s.logs.error(['errors', {'error': 'capture_logic_3', 'type': 'error -> ' + repr(e)}]) - elif packet.haslayer('UDP'): + _q_s.logs.error( + [ + "errors", + { + "error": "capture_logic_3", + "type": "error -> " + repr(e), + }, + ] + ) + elif packet.haslayer("UDP"): try: - _q_s.logs.info(['sniffer', {'action': 'udppayload', 'ip': _q_s.current_ip, 'mac': _q_s.current_mac, 'dest_ip': packet['IP'].src, 'dest_port':packet['UDP'].sport, 'dst_ip':packet['IP'].dst, 'dst_port':packet['UDP'].dport, 'raw_payload':raw_payloads, 'payload':hex_payloads}]) + _q_s.logs.info( + [ + "sniffer", + { + "action": "udppayload", + "ip": _q_s.current_ip, + "mac": _q_s.current_mac, + "dest_ip": packet["IP"].src, + "dest_port": packet["UDP"].sport, + "dst_ip": packet["IP"].dst, + "dst_port": packet["UDP"].dport, + "raw_payload": raw_payloads, + "payload": hex_payloads, + }, + ] + ) except Exception as e: - _q_s.logs.error(['errors', {'error': 'capture_logic_4', 'type': 'error -> ' + repr(e)}]) + _q_s.logs.error( + [ + "errors", + { + "error": "capture_logic_4", + "type": "error -> " + repr(e), + }, + ] + ) - if packet.haslayer('IP') and packet.haslayer('ICMP') and packet['IP'].src != _q_s.current_ip: - _q_s.logs.info(['sniffer', {'action': 'icmp', 'ip': _q_s.current_ip, 'mac': _q_s.current_mac, 'dest_ip': packet['IP'].src, 'dst_ip':packet['IP'].dst, 'ICMP_Code':packet['ICMP'].code, 'ICMP_Type':packet['ICMP'].type, 'ICMP_MSG':self.find_ICMP(packet['ICMP'].type, packet['ICMP'].code)}]) + if ( + packet.haslayer("IP") + and packet.haslayer("ICMP") + and packet["IP"].src != _q_s.current_ip + ): + _q_s.logs.info( + [ + "sniffer", + { + "action": "icmp", + "ip": _q_s.current_ip, + "mac": _q_s.current_mac, + "dest_ip": packet["IP"].src, + "dst_ip": packet["IP"].dst, + "ICMP_Code": packet["ICMP"].code, + "ICMP_Type": packet["ICMP"].type, + "ICMP_MSG": self.find_ICMP( + packet["ICMP"].type, packet["ICMP"].code + ), + }, + ] + ) - if packet.haslayer('IP') and packet.haslayer('TCP') and packet['IP'].src != _q_s.current_ip: - if packet['TCP'].flags == 2: - _q_s.logs.info(['sniffer', {'action': 'tcpscan', 'ip': _q_s.current_ip, 'mac': _q_s.current_mac, 'dest_ip': packet['IP'].src, 'dest_port':packet['TCP'].sport, 'dst_ip':packet['IP'].dst, 'dst_port':packet['TCP'].dport, 'raw_payload':raw_payloads, 'payload':hex_payloads}]) - send(IP(dst=packet['IP'].src, src=packet['IP'].dst) / TCP(dport=packet['TCP'].sport, sport=packet['TCP'].dport, ack=(packet['TCP'].seq + 1), flags='SA'), verbose=False) + if ( + packet.haslayer("IP") + and packet.haslayer("TCP") + and packet["IP"].src != _q_s.current_ip + ): + if packet["TCP"].flags == 2: + _q_s.logs.info( + [ + "sniffer", + { + "action": "tcpscan", + "ip": _q_s.current_ip, + "mac": _q_s.current_mac, + "dest_ip": packet["IP"].src, + "dest_port": packet["TCP"].sport, + "dst_ip": packet["IP"].dst, + "dst_port": packet["TCP"].dport, + "raw_payload": raw_payloads, + "payload": hex_payloads, + }, + ] + ) + send( + IP(dst=packet["IP"].src, src=packet["IP"].dst) + / TCP( + dport=packet["TCP"].sport, + sport=packet["TCP"].dport, + ack=(packet["TCP"].seq + 1), + flags="SA", + ), + verbose=False, + ) except Exception as e: - _q_s.logs.error(['errors', {'error': 'capture_logic_5', 'type': 'error -> ' + repr(e)}]) + _q_s.logs.error( + ["errors", {"error": "capture_logic_5", "type": "error -> " + repr(e)}] + ) stdout.flush() @@ -112,7 +279,7 @@ def capture_logic(packet): def run_sniffer(self, process=None): if process: - self.process = Process(name='QSniffer_', target=self.scapy_sniffer_main) + self.process = Process(name="QSniffer_", target=self.scapy_sniffer_main) self.process.start() else: self.scapy_sniffer_main() @@ -122,8 +289,9 @@ def kill_sniffer(self): self.process.join() -if __name__ == '__main__': +if __name__ == "__main__": from server_options import server_arguments + parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: qsniffer = QSniffer(filter=parsed.filter, interface=parsed.interface, config=parsed.config) diff --git a/honeypots/rdp_server.py b/honeypots/rdp_server.py index 642330c..31a5bb2 100644 --- a/honeypots/rdp_server.py +++ b/honeypots/rdp_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,43 +8,60 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*paramiko.*') +""" from socket import socket, SHUT_RDWR, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR from ssl import SSLContext, PROTOCOL_TLSv1_2, CERT_NONE from subprocess import Popen from os import path, getenv -from honeypots.helper import check_if_server_is_running, close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, set_local_vars, setup_logger +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) from uuid import uuid4 from contextlib import suppress -from threading import Event, Thread -from binascii import hexlify +from threading import Thread from struct import unpack from OpenSSL import crypto from tempfile import gettempdir, _get_candidate_names -class QRDPServer(): +class QRDPServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None self.key = path.join(gettempdir(), next(_get_candidate_names())) self.cert = path.join(gettempdir(), next(_get_candidate_names())) - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 3389 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 3389 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def rdp_server_main(self): _q_s = self @@ -53,11 +70,11 @@ def CreateCert(host_name, key, cert): pk = crypto.PKey() pk.generate_key(crypto.TYPE_RSA, 2048) c = crypto.X509() - c.get_subject().C = 'US' - c.get_subject().ST = 'OR' - c.get_subject().L = 'None' - c.get_subject().O = 'None' - c.get_subject().OU = 'None' + c.get_subject().C = "US" + c.get_subject().ST = "OR" + c.get_subject().L = "None" + c.get_subject().O = "None" + c.get_subject().OU = "None" c.get_subject().CN = next(_get_candidate_names()) c.set_serial_number(0) before, after = (0, 60 * 60 * 24 * 365 * 2) @@ -65,9 +82,9 @@ def CreateCert(host_name, key, cert): c.gmtime_adj_notAfter(after) c.set_issuer(c.get_subject()) c.set_pubkey(pk) - c.sign(pk, 'sha256') - open(cert, 'wb').write(crypto.dump_certificate(crypto.FILETYPE_PEM, c)) - open(key, 'wb').write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pk)) + c.sign(pk, "sha256") + open(cert, "wb").write(crypto.dump_certificate(crypto.FILETYPE_PEM, c)) + open(key, "wb").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pk)) class ConnectionHandle(Thread): def __init__(self, sock, key, cert): @@ -84,7 +101,7 @@ def check_bytes(self, string): def get_value(self, length, data): with suppress(Exception): - var = b'' + var = b"" for idx, _ in enumerate(data): if _ == 0 and data[idx + 1] == 0: break @@ -94,10 +111,10 @@ def get_value(self, length, data): var += bytes([_]) if length / 2 == len(var): return var - return b'' + return b"" def extract_cookie(self, data): - cookie = b'' + cookie = b"" with suppress(Exception): for idx, _ in enumerate(data): if _ == 13 and data[idx + 1] == 10: @@ -107,10 +124,20 @@ def extract_cookie(self, data): return cookie def extract_creds(self, data): - user = '' - password = '' + user = "" + password = "" with suppress(Exception): - flag, flags, code_page, option_flags, domain_length, user_length, password_length, shell_length, working_dir_length = unpack('HHIIHHHHH', data[15:37]) + ( + flag, + flags, + code_page, + option_flags, + domain_length, + user_length, + password_length, + shell_length, + working_dir_length, + ) = unpack("HHIIHHHHH", data[15:37]) location = 37 domain = self.get_value(domain_length, data[location:]) location = location + domain_length + 2 @@ -125,20 +152,40 @@ def extract_creds(self, data): def run(self): # There is no good documentation on how RDP protocol works (It took a bit of time to figure it out - Use b1105eb1-d1f7-414b-ad68-fd0c5a7823e4 test case) - cookie = '' + cookie = "" rdpdr = False cliprdr = False rdpsnd = False - initiator = b'\x00\x06' + initiator = b"\x00\x06" with suppress(Exception): - _q_s.logs.info({'server': 'rdp_server', 'action': 'connection', 'src_ip': self.sock.getpeername()[0], 'src_port': self.sock.getpeername()[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "rdp_server", + "action": "connection", + "src_ip": self.sock.getpeername()[0], + "src_port": self.sock.getpeername()[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) # Client X.224 Connection Request PDU data = self.sock.recv(1024) - if b'Cookie' in data: + if b"Cookie" in data: cookie = self.extract_cookie(data[11:]) cookie = self.check_bytes(cookie) - _q_s.logs.info({'server': 'rdp_server', 'action': 'stshash', 'mstshash': 'success', 'src_ip': self.sock.getpeername()[0], 'src_port': self.sock.getpeername()[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': {'stshash': cookie}}) + _q_s.logs.info( + { + "server": "rdp_server", + "action": "stshash", + "mstshash": "success", + "src_ip": self.sock.getpeername()[0], + "src_port": self.sock.getpeername()[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": {"stshash": cookie}, + } + ) # Server X.224 Connection Confirm PDU # data[0] version @@ -146,21 +193,25 @@ def run(self): # 14 (x0e) X.224 len # TLS only \x02\x00\x08\x00\x01\x00\x00\x00 - self.sock.send(b'\x03\x00\x00\x13\x0e\xd0\x00\x00\x12\x34\x00\x02\x00\x08\x00\x01\x00\x00\x00') + self.sock.send( + b"\x03\x00\x00\x13\x0e\xd0\x00\x00\x12\x34\x00\x02\x00\x08\x00\x01\x00\x00\x00" + ) ctx = SSLContext(PROTOCOL_TLSv1_2) - ctx.set_ciphers('RSA:!aNULL') + ctx.set_ciphers("RSA:!aNULL") ctx.check_hostname = False ctx.verify_mode = CERT_NONE ctx.load_cert_chain(certfile=self.cert, keyfile=self.key) - self.sock = ctx.wrap_socket(self.sock, server_side=True, do_handshake_on_connect=True) + self.sock = ctx.wrap_socket( + self.sock, server_side=True, do_handshake_on_connect=True + ) data = self.sock.recv(1024) - if b'rdpdr' in data: + if b"rdpdr" in data: rdpdr = True - if b'cliprdr' in data: + if b"cliprdr" in data: cliprdr = True - if b'rdpsnd' in data: + if b"rdpsnd" in data: rdpsnd = True # MCS Connect Response PDU with GCC Conference Create Response @@ -185,14 +236,16 @@ def run(self): # \x08\x0c SC_MULTITRANSPORT # \x08\x00\x00\x00\x00\x00 - self.sock.send(b'\x03\x00\x00\x7c\x02\xf0\x80\x7f\x66\x74\x0a\x01\x00\x02\x01\x00\x30\x1a\x02\x01\x22\x02\x01\x03\x02\x01\x00\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x03\x00\xff\xf8\x02\x01\x02\x04\x4e\x00\x05\x00\x14\x7c\x00\x01\x2a\x14\x76\x0a\x01\x01\x00\x01\xc0\x00\x4d\x63\x44\x6e\x38\x01\x0c\x0e\x00\x04\x00\x08\x00\x03\x00\x00\x00\x03\x00\x02\x0c\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x0c\x10\x00\xeb\x03\x04\x00\xec\x03\xed\x03\xee\x03\xef\x03\x04\x0c\x06\x00\xf0\x03\x08\x0c\x08\x00\x00\x00\x00\x00') + self.sock.send( + b"\x03\x00\x00\x7c\x02\xf0\x80\x7f\x66\x74\x0a\x01\x00\x02\x01\x00\x30\x1a\x02\x01\x22\x02\x01\x03\x02\x01\x00\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x03\x00\xff\xf8\x02\x01\x02\x04\x4e\x00\x05\x00\x14\x7c\x00\x01\x2a\x14\x76\x0a\x01\x01\x00\x01\xc0\x00\x4d\x63\x44\x6e\x38\x01\x0c\x0e\x00\x04\x00\x08\x00\x03\x00\x00\x00\x03\x00\x02\x0c\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x0c\x10\x00\xeb\x03\x04\x00\xec\x03\xed\x03\xee\x03\xef\x03\x04\x0c\x06\x00\xf0\x03\x08\x0c\x08\x00\x00\x00\x00\x00" + ) data = self.sock.recv(1024) data = self.sock.recv(1024) # Server MCS Attach-User Confirm PDU # 03 00 00 0b 02 f0 80 2e 00 00 06 - self.sock.send(b'\x03\x00\x00\x0b\x02\xf0\x80\x2e\x00' + initiator) + self.sock.send(b"\x03\x00\x00\x0b\x02\xf0\x80\x2e\x00" + initiator) # Multiple channel join # 03 00 00 0c 02 f0 80 38 00 06 03 eb @@ -204,30 +257,49 @@ def run(self): data = self.sock.recv(1024) if len(data) > 14: if data[15] == 64: - username = '' - password = '' - status = 'failed' + username = "" + password = "" + status = "failed" username, password = self.extract_creds(data) username = self.check_bytes(username) password = self.check_bytes(password) if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'rdp_server', 'action': 'login', 'status': status, 'src_ip': self.sock.getpeername()[0], 'src_port': self.sock.getpeername()[1], 'username': username, 'password': password, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + status = "success" + _q_s.logs.info( + { + "server": "rdp_server", + "action": "login", + "status": status, + "src_ip": self.sock.getpeername()[0], + "src_port": self.sock.getpeername()[1], + "username": username, + "password": password, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) break else: - self.sock.send(b'\x03\x00\x00\x0f\x02\xf0\x80\x3e\x00' + initiator + b'\x03' + bytes([data[-1]]) + b'\x03' + bytes([data[-1]])) + self.sock.send( + b"\x03\x00\x00\x0f\x02\xf0\x80\x3e\x00" + + initiator + + b"\x03" + + bytes([data[-1]]) + + b"\x03" + + bytes([data[-1]]) + ) # MCS Disconnect Provider Ultimatum PDU - self.sock.send(b'\x03\x00\x00\x09\x02\xf0\x80\x21\x80') + self.sock.send(b"\x03\x00\x00\x09\x02\xf0\x80\x21\x80") with suppress(Exception): self.sock.shutdown(SHUT_RDWR) with suppress(Exception): self.sock.close() - CreateCert('localhost', self.key, self.cert) + CreateCert("localhost", self.key, self.cert) rpdserver = socket(AF_INET, SOCK_STREAM) rpdserver.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) rpdserver.bind((self.ip, self.port)) @@ -240,7 +312,7 @@ def run(self): ConnectionHandle(client, self.key, self.cert).start() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -252,13 +324,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'rdp_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "rdp_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -267,28 +371,36 @@ def run_server(self, process=False, auto=False): self.rdp_server_main() def close_port(self): - ret = close_port_wrapper('rdp_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("rdp_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('rdp_server', self.uuid, self.process) + ret = kill_server_wrapper("rdp_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None): with suppress(Exception): from warnings import filterwarnings - filterwarnings(action='ignore', module='.*socket.*') + + filterwarnings(action="ignore", module=".*socket.*") from socket import socket, AF_INET, SOCK_STREAM _ip = ip or self.ip _port = port or self.port c = socket(AF_INET, SOCK_STREAM) - c.sendto(b'test', (_ip, _port)) + c.sendto(b"test", (_ip, _port)) c.close() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qrdpserver = QRDPServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qrdpserver = QRDPServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qrdpserver.run_server() diff --git a/honeypots/redis_server.py b/honeypots/redis_server.py index 2f36d57..c691ea3 100644 --- a/honeypots/redis_server.py +++ b/honeypots/redis_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,44 +8,61 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor -from twisted.python import log as tlog +from contextlib import suppress +from os import getenv, path from subprocess import Popen -from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running, set_local_vars from uuid import uuid4 -from contextlib import suppress +from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QRedisServer(): + +class QRedisServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 6379 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 6379 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def redis_server_main(self): _q_s = self class CustomRedisProtocol(Protocol): - def check_bytes(self, string): if isinstance(string, bytes): return string.decode() @@ -54,51 +71,74 @@ def check_bytes(self, string): def get_command(self, data): with suppress(Exception): - _data = data.decode('utf-8').split('\x0d\x0a') - if _data[0][0] == '*': + _data = data.decode("utf-8").split("\x0d\x0a") + if _data[0][0] == "*": _count = int(_data[0][1]) - 1 _data.pop(0) - if _data[0::2][0][0] == '$' and len(_data[1::2][0]) == int(_data[0::2][0][1]): + if _data[0::2][0][0] == "$" and len(_data[1::2][0]) == int( + _data[0::2][0][1] + ): return _count, _data[1::2][0] - return 0, '' + return 0, "" def parse_data(self, c, data): - _data = data.decode('utf-8').split('\r\n')[3::] - username, password = '', '' + _data = data.decode("utf-8").split("\r\n")[3::] + username, password = "", "" if c == 2: _ = 0 - if _data[0::2][_][0] == '$' and len(_data[1::2][_]) == int(_data[0::2][_][1]): - username = (_data[1::2][_]) + if _data[0::2][_][0] == "$" and len(_data[1::2][_]) == int(_data[0::2][_][1]): + username = _data[1::2][_] _ = 1 - if _data[0::2][_][0] == '$' and len(_data[1::2][_]) == int(_data[0::2][_][1]): - password = (_data[1::2][_]) + if _data[0::2][_][0] == "$" and len(_data[1::2][_]) == int(_data[0::2][_][1]): + password = _data[1::2][_] if c == 1: _ = 0 - if _data[0::2][_][0] == '$' and len(_data[1::2][_]) == int(_data[0::2][_][1]): - password = (_data[1::2][_]) + if _data[0::2][_][0] == "$" and len(_data[1::2][_]) == int(_data[0::2][_][1]): + password = _data[1::2][_] if c == 2 or c == 1: username = self.check_bytes(username) password = self.check_bytes(password) - status = 'failed' + status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'redis_server', 'action': 'login', 'status': status, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + status = "success" + _q_s.logs.info( + { + "server": "redis_server", + "action": "login", + "status": status, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) def connectionMade(self): self._state = 1 self._variables = {} - _q_s.logs.info({'server': 'redis_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "redis_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def dataReceived(self, data): c, command = self.get_command(data) - if command == 'AUTH': + if command == "AUTH": self.parse_data(c, data) - self.transport.write(b'-ERR invalid password\r\n') + self.transport.write(b"-ERR invalid password\r\n") else: - self.transport.write(b'-ERR unknown command "{}"\r\n'.format(command)) + self.transport.write(f'-ERR unknown command "{command}"\r\n'.encode()) self.transport.loseConnection() factory = Factory() @@ -107,7 +147,7 @@ def dataReceived(self, data): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -119,13 +159,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'redis_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "redis_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -134,27 +206,35 @@ def run_server(self, process=False, auto=False): self.redis_server_main() def close_port(self): - ret = close_port_wrapper('redis_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("redis_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('redis_server', self.uuid, self.process) + ret = kill_server_wrapper("redis_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from redis import StrictRedis + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password - r = StrictRedis.from_url('redis://{}:{}@{}:{}/1'.format(_username, _password, _ip, _port)) - for key in r.scan_iter('user:*'): + r = StrictRedis.from_url(f"redis://{_username}:{_password}@{_ip}:{_port}/1") + for key in r.scan_iter("user:*"): pass -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qredisserver = QRedisServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qredisserver = QRedisServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qredisserver.run_server() diff --git a/honeypots/sip_server.py b/honeypots/sip_server.py index 2a78db6..b0a345a 100644 --- a/honeypots/sip_server.py +++ b/honeypots/sip_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,39 +8,56 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.protocols.sip import Base -from twisted.internet import reactor -from time import time -from twisted.python import log as tlog +from contextlib import suppress +from os import getenv, path from subprocess import Popen -from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running from uuid import uuid4 -from contextlib import suppress + +from twisted.internet import reactor +from twisted.protocols.sip import Base + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QSIPServer(): +class QSIPServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 5060 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 5060 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def sip_server_main(self): _q_s = self @@ -49,17 +66,33 @@ class CustomSIPServer(Base): def handle_request(self, message, addr): headers = {} - _q_s.logs.info({'server': 'sip_server', 'action': 'connection', 'src_ip': addr[0], 'src_port': addr[1]}) + _q_s.logs.info( + { + "server": "sip_server", + "action": "connection", + "src_ip": addr[0], + "src_port": addr[1], + } + ) def check_bytes(string): if isinstance(string, bytes): return string.decode() else: return str(string) - for item, value in message.headers.items(): - headers.update({check_bytes(item): ','.join(map(check_bytes, value))}) - _q_s.logs.info({'server': 'sip_server', 'action': 'request', 'src_ip': addr[0], 'src_port': addr[1], 'data': headers}) + for item, value in message.headers.items(): + headers.update({check_bytes(item): ",".join(map(check_bytes, value))}) + + _q_s.logs.info( + { + "server": "sip_server", + "action": "request", + "src_ip": addr[0], + "src_port": addr[1], + "data": headers, + } + ) response = self.responseFromRequest(200, message) response.creationFinished() self.deliverResponse(response) @@ -68,7 +101,7 @@ def check_bytes(string): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -80,13 +113,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'sip_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "sip_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -95,27 +154,38 @@ def run_server(self, process=False, auto=False): self.sip_server_main() def close_port(self): - ret = close_port_wrapper('sip_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("sip_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('sip_server', self.uuid, self.process) + ret = kill_server_wrapper("sip_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from socket import socket, AF_INET, SOCK_DGRAM, IPPROTO_UDP + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) - sock.sendto(b'INVITE sip:user_1@test.test SIP/2.0\r\nTo: \r\nFrom: sip:user_3@test.test.test;tag=none\r\nCall-ID: 1@0.0.0.0\r\nCSeq: 1 INVITE\r\nContact: sip:user_3@test.test.test\r\nVia: SIP/2.0/TCP 0.0.0.0;branch=34uiddhjczqw3mq23\r\nContent-Length: 1\r\n\r\nT', (_ip, _port)) + sock.sendto( + b"INVITE sip:user_1@test.test SIP/2.0\r\nTo: \r\nFrom: sip:user_3@test.test.test;tag=none\r\nCall-ID: 1@0.0.0.0\r\nCSeq: 1 INVITE\r\nContact: sip:user_3@test.test.test\r\nVia: SIP/2.0/TCP 0.0.0.0;branch=34uiddhjczqw3mq23\r\nContent-Length: 1\r\n\r\nT", + (_ip, _port), + ) sock.close() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - QSIPServer = QSIPServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + QSIPServer = QSIPServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) QSIPServer.run_server() diff --git a/honeypots/smb_server.py b/honeypots/smb_server.py index 95ef171..e56ab86 100644 --- a/honeypots/smb_server.py +++ b/honeypots/smb_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,68 +8,106 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', category=DeprecationWarning) -filterwarnings(action='ignore', module='.*impacket.*') - -from logging import StreamHandler, getLogger, DEBUG -from impacket import smbserver -from tempfile import mkdtemp +from contextlib import suppress +from logging import DEBUG, getLogger, StreamHandler +from os import getenv, path +from random import randint from shutil import rmtree -from time import sleep -from impacket.ntlm import compute_lmhash, compute_nthash -from logging import DEBUG, getLogger -from os import path, getenv from subprocess import Popen -from six.moves import configparser, socketserver -from threading import enumerate as threading_enumerate -from random import randint +from tempfile import mkdtemp from threading import current_thread -from honeypots.helper import check_if_server_is_running, close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, set_local_vars, setup_logger +from time import sleep from uuid import uuid4 -from contextlib import suppress +from impacket import smbserver +from impacket.ntlm import compute_lmhash, compute_nthash +from six.moves import socketserver + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QSMBServer(): + +class QSMBServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.folders = '' - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.folders = "" + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 445 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - self.disable_logger() - - def disable_logger(self): - getLogger('impacket').propagate = False + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 445 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def smb_server_main(self): _q_s = self - class Logger(object): + class Logger: def write(self, message): with suppress(Exception): temp = current_thread().name - if temp.startswith('thread_'): - ip = temp.split('_')[1] - port = temp.split('_')[2] - if 'Incoming connection' in message.strip() or 'AUTHENTICATE_MESSAGE' in message.strip() or 'authenticated successfully' in message.strip(): - _q_s.logs.info({'server': 'smb_server', 'action': 'connection', 'data': message.strip(), 'src_ip': ip, 'src_port': port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - elif ':4141414141414141:' in message.strip(): - parsed = message.strip().split(':') + if temp.startswith("thread_"): + ip = temp.split("_")[1] + port = temp.split("_")[2] + if ( + "Incoming connection" in message.strip() + or "AUTHENTICATE_MESSAGE" in message.strip() + or "authenticated successfully" in message.strip() + ): + _q_s.logs.info( + { + "server": "smb_server", + "action": "connection", + "data": message.strip(), + "src_ip": ip, + "src_port": port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + elif ":4141414141414141:" in message.strip(): + parsed = message.strip().split(":") if len(parsed) > 2: - _q_s.logs.info({'server': 'smb_server', 'action': 'login', 'workstation': parsed[0], 'test': parsed[1], 'src_ip': ip, 'src_port': port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "smb_server", + "action": "login", + "workstation": parsed[0], + "test": parsed[1], + "src_ip": ip, + "src_port": port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) class SMBSERVERHandler(smbserver.SMBSERVERHandler): def __init__(self, request, client_address, server, select_poll=False): @@ -78,7 +116,7 @@ def __init__(self, request, client_address, server, select_poll=False): self.__request = request self.__select_poll = select_poll self.__ip, self.__port = client_address[:2] - self.__connId = "thread_{}_{}_{}".format(self.__ip, self.__port, randint(1000, 9999)) + self.__connId = f"thread_{self.__ip}_{self.__port}_{randint(1000, 9999)}" current_thread().name = self.__connId socketserver.BaseRequestHandler.__init__(self, request, client_address, server) @@ -91,11 +129,13 @@ def processRequest(self, connId, data): return x class SimpleSMBServer(smbserver.SimpleSMBServer): - def __init__(self, listenAddress='0.0.0.0', listenPort=445, configFile=''): + def __init__(self, listenAddress="0.0.0.0", listenPort=445, configFile=""): super().__init__(listenAddress, listenPort, configFile) self.__server.server_close() sleep(randint(1, 2)) - self.__server = SMBSERVER((listenAddress, listenPort), config_parser=self.__smbConfig) + self.__server = SMBSERVER( + (listenAddress, listenPort), config_parser=self.__smbConfig + ) self.__server.processConfigFile() def start(self): @@ -104,28 +144,30 @@ def start(self): self.__server.serve_forever() handler = StreamHandler(Logger()) - getLogger('impacket').addHandler(handler) - getLogger('impacket').setLevel(DEBUG) + getLogger("impacket").addHandler(handler) + getLogger("impacket").setLevel(DEBUG) dirpath = mkdtemp() server = SimpleSMBServer(listenAddress=self.ip, listenPort=self.port) # server.removeShare('IPC$') - if self.folders == '' or self.folders is None: - server.addShare('C$', dirpath, '', readOnly='yes') + if self.folders == "" or self.folders is None: + server.addShare("C$", dirpath, "", readOnly="yes") else: - for folder in self.folders.split(','): - name, d = folder.split(':') + for folder in self.folders.split(","): + name, d = folder.split(":") if path.isdir(d) and len(name) > 0: - server.addShare(name, d, '', readOnly='yes') + server.addShare(name, d, "", readOnly="yes") server.setSMB2Support(True) - server.addCredential(self.username, 0, compute_lmhash(self.password), compute_nthash(self.password)) - server.setSMBChallenge('') + server.addCredential( + self.username, 0, compute_lmhash(self.password), compute_nthash(self.password) + ) + server.setSMBChallenge("") server.start() rmtree(dirpath) def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -137,13 +179,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' + status = "success" - self.logs.info({'server': 'smb_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) + self.logs.info( + { + "server": "smb_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) - if status == 'success': + if status == "success": return True else: self.kill_server() @@ -152,16 +226,17 @@ def run_server(self, process=False, auto=False): self.smb_server_main() def close_port(self): - ret = close_port_wrapper('smb_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("smb_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('smb_server', self.uuid, self.process) + ret = kill_server_wrapper("smb_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from impacket.smbconnection import SMBConnection + _ip = ip or self.ip _port = port or self.port _username = username or self.username @@ -170,9 +245,16 @@ def test_server(self, ip=None, port=None, username=None, password=None): smb_client.login(_username, _password) -if __name__ == '__main__': - +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qsmbserver = QSMBServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, folders=parsed.folders, options=parsed.options, config=parsed.config) + qsmbserver = QSMBServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + folders=parsed.folders, + options=parsed.options, + config=parsed.config, + ) qsmbserver.run_server() diff --git a/honeypots/smtp_server.py b/honeypots/smtp_server.py index bb680a7..d04b00a 100644 --- a/honeypots/smtp_server.py +++ b/honeypots/smtp_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,114 +8,164 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - -from warnings import filterwarnings -filterwarnings(action='ignore', category=DeprecationWarning) +""" from smtpd import SMTPChannel, SMTPServer from asyncore import loop from base64 import b64decode from os import path, getenv from subprocess import Popen -from honeypots.helper import check_if_server_is_running, close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, set_local_vars, setup_logger +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) from uuid import uuid4 from contextlib import suppress -class QSMTPServer(): +class QSMTPServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 25 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 25 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def smtp_server_main(self): _q_s = self class CustomSMTPChannel(SMTPChannel): - - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def found_terminator(self): with suppress(Exception): if "capture_commands" in _q_s.options: line = self._emptystring.join(self.received_lines).decode() - command = None arg = None data = None - if line.find(' ') < 0: + if line.find(" ") < 0: command = line.upper() else: - command = line.split(' ')[0].upper() - arg = line.split(' ')[1].strip() - if len(line.split(' ')) > 2: - data = line.split(' ', 2)[2] + command = line.split(" ")[0].upper() + arg = line.split(" ")[1].strip() + if len(line.split(" ")) > 2: + data = line.split(" ", 2)[2] if command != "HELO" and command != "EHLO": - _q_s.logs.info({'server': 'smtp_server', 'action': 'connection', 'src_ip': self.addr[0], 'src_port': self.addr[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, "data": {"command": command, "arg": arg, "data": data}}) + _q_s.logs.info( + { + "server": "smtp_server", + "action": "connection", + "src_ip": self.addr[0], + "src_port": self.addr[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": {"command": command, "arg": arg, "data": data}, + } + ) super().found_terminator() def smtp_EHLO(self, arg): - _q_s.logs.info({'server': 'smtp_server', 'action': 'connection', 'src_ip': self.addr[0], 'src_port': self.addr[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "smtp_server", + "action": "connection", + "src_ip": self.addr[0], + "src_port": self.addr[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) if not arg: - self.push('501 Syntax: HELO hostname') + self.push("501 Syntax: HELO hostname") if self._SMTPChannel__greeting: - self.push('503 Duplicate HELO/EHLO') + self.push("503 Duplicate HELO/EHLO") else: self._SMTPChannel__greeting = arg - self.push('250-{0} Hello {1}'.format(self._SMTPChannel__fqdn, arg)) - self.push('250-8BITMIME') - self.push('250-AUTH LOGIN PLAIN') - self.push('250 STARTTLS') + self.push(f"250-{self._SMTPChannel__fqdn} Hello {arg}") + self.push("250-8BITMIME") + self.push("250-AUTH LOGIN PLAIN") + self.push("250 STARTTLS") def smtp_AUTH(self, arg): with suppress(Exception): - if arg.startswith('PLAIN '): - _, username, password = b64decode(arg.split(' ')[1].strip()).decode('utf-8').split('\0') - username = self.check_bytes(username) - password = self.check_bytes(password) - status = 'failed' + if arg.startswith("PLAIN "): + _, username, password = ( + b64decode(arg.split(" ")[1].strip()) + .decode("utf-8", errors="replace") + .split("\0") + ) + status = "failed" if username == _q_s.username and password == _q_s.password: - username = _q_s.username - password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'smtp_server', 'action': 'login', 'status': status, 'src_ip': self.addr[0], 'src_port': self.addr[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + status = "success" + _q_s.logs.info( + { + "server": "smtp_server", + "action": "login", + "status": status, + "src_ip": self.addr[0], + "src_port": self.addr[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) - self.push('235 Authentication successful') + self.push("235 Authentication successful") def __getattr__(self, name): self.smtp_QUIT(0) class CustomSMTPServer(SMTPServer): - def __init__(self, localaddr, remoteaddr): - SMTPServer.__init__(self, localaddr, remoteaddr) - - def process_message(self, peer, mailfrom, rcpttos, data, mail_options=None, rcpt_options=None): + def process_message( + self, peer, mailfrom, rcpttos, data, mail_options=None, rcpt_options=None + ): return def handle_accept(self): conn, addr = self.accept() + _q_s.logs.info( + { + "server": "smtp_server", + "action": "connection", + "src_ip": addr[0], + "src_port": addr[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) CustomSMTPChannel(self, conn, addr) CustomSMTPServer((self.ip, self.port), None) loop(timeout=1.1, use_poll=True) def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -127,13 +177,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' + status = "success" - self.logs.info({'server': 'smtp_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) + self.logs.info( + { + "server": "smtp_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) - if status == 'success': + if status == "success": return True else: self.kill_server() @@ -142,16 +224,17 @@ def run_server(self, process=False, auto=False): self.smtp_server_main() def close_port(self): - ret = close_port_wrapper('smtp_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("smtp_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('smtp_server', self.uuid, self.process) + ret = kill_server_wrapper("smtp_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from smtplib import SMTP + _ip = ip or self.ip _port = port or self.port _username = username or self.username @@ -159,12 +242,19 @@ def test_server(self, ip=None, port=None, username=None, password=None): s = SMTP(_ip, _port) s.ehlo() s.login(_username, _password) - s.sendmail('fromtest', 'totest', 'Nothing') + s.sendmail("fromtest", "totest", "Nothing") s.quit() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qsmtpserver = QSMTPServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qsmtpserver = QSMTPServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qsmtpserver.run_server() diff --git a/honeypots/snmp_server.py b/honeypots/snmp_server.py index c86c6c6..aaee93d 100644 --- a/honeypots/snmp_server.py +++ b/honeypots/snmp_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,68 +8,106 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - +""" from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') -filterwarnings(action='ignore', module='.*scapy.*') +from cryptography.utils import CryptographyDeprecationWarning -from twisted.internet.protocol import DatagramProtocol -from twisted.internet import reactor -from twisted.python import log as tlog +filterwarnings(action="ignore", category=CryptographyDeprecationWarning) + +from contextlib import suppress +from os import getenv, path from subprocess import Popen -from os import path, getenv -from scapy.all import SNMP -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running from uuid import uuid4 -from contextlib import suppress +from scapy.all import SNMP +from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QSNMPServer(): + +class QSNMPServer: def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 161 - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 161 + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def snmp_server_main(self): _q_s = self class CustomDatagramProtocolProtocol(DatagramProtocol): def parse_snmp(self, data): - version = 'UnKnown' - community = 'UnKnown' - oids = 'UnKnown' + version = "UnKnown" + community = "UnKnown" + oids = "UnKnown" with suppress(Exception): parsed_snmp = SNMP(data) community = parsed_snmp.community.val version = parsed_snmp.version.val - oids = ' '.join([item.oid.val for item in parsed_snmp.PDU.varbindlist]) + oids = " ".join([item.oid.val for item in parsed_snmp.PDU.varbindlist]) return version, community, oids def datagramReceived(self, data, addr): - _q_s.logs.info({'server': 'snmp_server', 'action': 'connection', 'status': 'fail', 'src_ip': addr[0], 'src_port': addr[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "snmp_server", + "action": "connection", + "status": "fail", + "src_ip": addr[0], + "src_port": addr[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) version, community, oids = self.parse_snmp(data) if version or community or oids: - _q_s.logs.info({'server': 'snmp_server', 'action': 'query', 'status': 'success', 'src_ip': addr[0], 'src_port': addr[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'data': {'version': version, 'community': community, 'oids': oids}}) - self.transport.write('Error', addr) - - self.transport.loseConnection() - - reactor.listenUDP(port=self.port, protocol=CustomDatagramProtocolProtocol(), interface=self.ip) + _q_s.logs.info( + { + "server": "snmp_server", + "action": "query", + "status": "success", + "src_ip": addr[0], + "src_port": addr[1], + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": {"version": version, "community": community, "oids": oids}, + } + ) + self.transport.write(b"Error", addr) + + reactor.listenUDP( + port=self.port, protocol=CustomDatagramProtocolProtocol(), interface=self.ip + ) reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -81,13 +119,39 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'snmp_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "snmp_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -96,24 +160,41 @@ def run_server(self, process=False, auto=False): self.snmp_server_main() def close_port(self): - ret = close_port_wrapper('snmp_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("snmp_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('snmp_server', self.uuid, self.process) + ret = kill_server_wrapper("snmp_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): - from pysnmp.hlapi import (getCmd, SnmpEngine, CommunityData, UdpTransportTarget, ContextData, ObjectType, ObjectIdentity,) + from pysnmp.hlapi import ( + getCmd, + SnmpEngine, + CommunityData, + UdpTransportTarget, + ContextData, + ObjectType, + ObjectIdentity, + ) + _ip = ip or self.ip _port = port or self.port - g = getCmd(SnmpEngine(), CommunityData('public'), UdpTransportTarget((_ip, _port)), ContextData(), ObjectType(ObjectIdentity('1.3.6.1.4.1.9.9.618.1.4.1.0'))) + g = getCmd( + SnmpEngine(), + CommunityData("public"), + UdpTransportTarget((_ip, _port)), + ContextData(), + ObjectType(ObjectIdentity("1.3.6.1.4.1.9.9.618.1.4.1.0")), + ) errorIndication, errorStatus, errorIndex, varBinds = next(g) -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - QSNMPServer = QSNMPServer(ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config) + QSNMPServer = QSNMPServer( + ip=parsed.ip, port=parsed.port, options=parsed.options, config=parsed.config + ) QSNMPServer.run_server() diff --git a/honeypots/socks5_server.py b/honeypots/socks5_server.py index cd98e0e..fa0a9ff 100644 --- a/honeypots/socks5_server.py +++ b/honeypots/socks5_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,42 +8,64 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - +""" +import struct from socketserver import TCPServer, StreamRequestHandler, ThreadingMixIn from struct import unpack from os import path, getenv from subprocess import Popen -from honeypots.helper import check_if_server_is_running, close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, set_local_vars, setup_logger + +from honeypots import set_up_error_logging +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) from uuid import uuid4 from contextlib import suppress -class QSOCKS5Server(): +class QSOCKS5Server: + NAME = "socks5_server" + def __init__(self, **kwargs): self.auto_disabled = None self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 1080 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 1080 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) + self.logger = set_up_error_logging() def socks5_server_main(self): _q_s = self class CustomStreamRequestHandler(StreamRequestHandler): - def check_bytes(self, string): if isinstance(string, bytes): return string.decode() @@ -51,24 +73,51 @@ def check_bytes(self, string): return str(string) def handle(self): - _q_s.logs.info({'server': 'socks5_server', 'action': 'connection', 'src_ip': self.client_address[0], 'src_port': self.client_address[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - v, m = unpack('!BB', self.connection.recv(2)) - if v == 5: - if 2 in unpack('!' + 'B' * m, self.connection.recv(m)): - self.connection.sendall(b'\x05\x02') - if 1 in unpack('B', self.connection.recv(1)): - _len = ord(self.connection.recv(1)) - username = self.connection.recv(_len) - _len = ord(self.connection.recv(1)) - password = self.connection.recv(_len) - username = self.check_bytes(username) - password = self.check_bytes(password) - status = 'failed' - if username == _q_s.username and password == _q_s.password: - username = _q_s.username - password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'socks5_server', 'action': 'login', 'status': status, 'src_ip': self.client_address[0], 'src_port': self.client_address[1], 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + src_ip, src_port = self.client_address + _q_s.logs.info( + { + "server": _q_s.NAME, + "action": "connection", + "src_ip": src_ip, + "src_port": src_port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + try: + v, m = unpack("!BB", self.connection.recv(2)) + if v == 5: + if 2 in unpack("!" + "B" * m, self.connection.recv(m)): + self.connection.sendall(b"\x05\x02") + if 1 in unpack("B", self.connection.recv(1)): + _len = ord(self.connection.recv(1)) + username = self.connection.recv(_len) + _len = ord(self.connection.recv(1)) + password = self.connection.recv(_len) + username = self.check_bytes(username) + password = self.check_bytes(password) + status = "failed" + if username == _q_s.username and password == _q_s.password: + status = "success" + _q_s.logs.info( + { + "server": _q_s.NAME, + "action": "login", + "status": status, + "src_ip": src_ip, + "src_port": src_port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) + except ConnectionResetError: + _q_s.logger.debug( + f"[{_q_s.NAME}]: Connection reset error when trying to handle connection" + ) + except struct.error: + _q_s.logger.debug(f"[{_q_s.NAME}]: Could not parse data to handle connection") self.server.close_request(self.request) @@ -80,7 +129,7 @@ class ThreadingTCPServer(ThreadingMixIn, TCPServer): server.serve_forever() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -92,13 +141,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'socks5_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": self.NAME, + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -107,25 +188,39 @@ def run_server(self, process=False, auto=False): self.socks5_server_main() def close_port(self): - ret = close_port_wrapper('socks5_server', self.ip, self.port, self.logs) + ret = close_port_wrapper(self.NAME, self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('socks5_server', self.uuid, self.process) + ret = kill_server_wrapper(self.NAME, self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from requests import get + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password - get('https://yahoo.com', proxies=dict(http='socks5://{}:{}@{}:{}'.format(_username, _password, _ip, _port), https='socks5://{}:{}@{}:{}'.format(_username, _password, _ip, _port))) + get( + "https://yahoo.com", + proxies=dict( + http=f"socks5://{_username}:{_password}@{_ip}:{_port}", + https=f"socks5://{_username}:{_password}@{_ip}:{_port}", + ), + ) -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - QSOCKS5Server = QSOCKS5Server(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + QSOCKS5Server = QSOCKS5Server( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) QSOCKS5Server.run_server() diff --git a/honeypots/ssh_server.py b/honeypots/ssh_server.py index 689ef62..6acbd0f 100644 --- a/honeypots/ssh_server.py +++ b/honeypots/ssh_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,20 +8,36 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*paramiko.*') -filterwarnings(action='ignore', module='.*socket.*') - -from paramiko import RSAKey, ServerInterface, Transport, OPEN_SUCCEEDED, AUTH_PARTIALLY_SUCCESSFUL, AUTH_SUCCESSFUL, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_SUCCEEDED, AUTH_FAILED -from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR, getfqdn +from paramiko import ( + RSAKey, + ServerInterface, + Transport, + OPEN_SUCCEEDED, + AUTH_SUCCESSFUL, + OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, + OPEN_SUCCEEDED, + AUTH_FAILED, +) +from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR from _thread import start_new_thread from io import StringIO from random import choice from subprocess import Popen from os import path, getenv -from honeypots.helper import check_if_server_is_running, close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, set_local_vars, setup_logger + +from paramiko.ssh_exception import SSHException + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) from uuid import uuid4 from contextlib import suppress from re import compile as rcompile @@ -30,24 +46,39 @@ from binascii import hexlify -class QSSHServer(): +class QSSHServer: def __init__(self, **kwargs): self.auto_disabled = None - self.mocking_server = choice(['OpenSSH 7.5', 'OpenSSH 7.3', 'Serv-U SSH Server 15.1.1.108', 'OpenSSH 6.4']) + self.mocking_server = choice( + ["OpenSSH 7.5", "OpenSSH 7.3", "Serv-U SSH Server 15.1.1.108", "OpenSSH 6.4"] + ) self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 22 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - self.ansi = rcompile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]') + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 22 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) + self.ansi = rcompile(r"(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]") def generate_pub_pri_keys(self): with suppress(Exception): @@ -61,7 +92,6 @@ def ssh_server_main(self): _q_s = self class SSHHandle(ServerInterface): - def __init__(self, ip, port): self.ip = ip self.port = port @@ -75,27 +105,61 @@ def check_bytes(self, string): return str(string) def check_channel_request(self, kind, chanid): - if kind == 'session': + if kind == "session": return OPEN_SUCCEEDED return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def check_auth_password(self, username, password): username = self.check_bytes(username) password = self.check_bytes(password) - status = 'failed' + status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - if status == 'success': - _q_s.logs.info({'server': 'ssh_server', 'action': 'login', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + status = "success" + if status == "success": + _q_s.logs.info( + { + "server": "ssh_server", + "action": "login", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) return AUTH_SUCCESSFUL - _q_s.logs.info({'server': 'ssh_server', 'action': 'login', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + _q_s.logs.info( + { + "server": "ssh_server", + "action": "login", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) return AUTH_FAILED def check_channel_exec_request(self, channel, command): if "capture_commands" in _q_s.options: - _q_s.logs.info({'server': 'ssh_server', 'action': 'command', 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, "data": {"command": self.check_bytes(command)}}) + _q_s.logs.info( + { + "server": "ssh_server", + "action": "command", + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": {"command": self.check_bytes(command)}, + } + ) self.event.set() return True @@ -103,7 +167,18 @@ def get_allowed_auths(self, username): return "password,publickey" def check_auth_publickey(self, username, key): - _q_s.logs.info({'server': 'ssh_server', 'action': 'login', 'src_ip': self.ip, 'src_port': self.port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, "username": self.check_bytes(username), 'key_fingerprint': self.check_bytes(hexlify(key.get_fingerprint()))}) + _q_s.logs.info( + { + "server": "ssh_server", + "action": "login", + "src_ip": self.ip, + "src_port": self.port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": self.check_bytes(username), + "key_fingerprint": self.check_bytes(hexlify(key.get_fingerprint())), + } + ) return AUTH_SUCCESSFUL def check_channel_shell_request(self, channel): @@ -112,26 +187,46 @@ def check_channel_shell_request(self, channel): def check_channel_direct_tcpip_request(self, chanid, origin, destination): return OPEN_SUCCEEDED - def check_channel_pty_request(self, channel, term, width, height, pixelwidth, pixelheight, modes): + def check_channel_pty_request( + self, channel, term, width, height, pixelwidth, pixelheight, modes + ): return True def ConnectionHandle(client, priv): with suppress(Exception): t = Transport(client) ip, port = client.getpeername() - _q_s.logs.info({'server': 'ssh_server', 'action': 'connection', 'src_ip': ip, 'src_port': port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) - t.local_version = 'SSH-2.0-' + _q_s.mocking_server + _q_s.logs.info( + { + "server": "ssh_server", + "action": "connection", + "src_ip": ip, + "src_port": port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + t.local_version = "SSH-2.0-" + _q_s.mocking_server t.add_server_key(RSAKey(file_obj=StringIO(priv))) sshhandle = SSHHandle(ip, port) - t.start_server(server=sshhandle) + try: + t.start_server(server=sshhandle) + except SSHException: + return conn = t.accept(30) if "interactive" in _q_s.options and conn is not None: - conn.send("Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.10.60.1-microsoft-standard-WSL2 x86_64)\r\n\r\n") + conn.send( + "Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.10.60.1-microsoft-standard-WSL2 x86_64)\r\n\r\n" + ) current_time = time() while True and time() < current_time + 10: conn.send("/$ ") line = "" - while not line.endswith("\x0d") and not line.endswith("\x0a") and time() < current_time + 10: + while ( + not line.endswith("\x0d") + and not line.endswith("\x0a") + and time() < current_time + 10 + ): conn.settimeout(10) recv = conn.recv(1).decode() conn.settimeout(None) @@ -139,9 +234,21 @@ def ConnectionHandle(client, priv): conn.send(recv) line += recv line = line.rstrip() - _q_s.logs.info({'server': 'ssh_server', 'action': 'interactive', 'src_ip': ip, 'src_port': port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, "data": {"command": line}}) + _q_s.logs.info( + { + "server": "ssh_server", + "action": "interactive", + "src_ip": ip, + "src_port": port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "data": {"command": line}, + } + ) if line == "ls": - conn.send("\r\nbin cdrom etc lib lib64 lost+found mnt proc run snap swapfile tmp var boot dev home lib32 libx32 media opt root sbin srv sys usr\r\n") + conn.send( + "\r\nbin cdrom etc lib lib64 lost+found mnt proc run snap swapfile tmp var boot dev home lib32 libx32 media opt root sbin srv sys usr\r\n" + ) elif line == "pwd": conn.send("\r\n/\r\n") elif line == "whoami": @@ -149,7 +256,7 @@ def ConnectionHandle(client, priv): elif line == "exit": break else: - conn.send("\r\n{}: command not found\r\n".format(line)) + conn.send(f"\r\n{line}: command not found\r\n") with suppress(Exception): sshhandle.event.wait(2) with suppress(Exception): @@ -165,10 +272,16 @@ def ConnectionHandle(client, priv): while True: with suppress(Exception): client, addr = sock.accept() - start_new_thread(ConnectionHandle, (client, priv,)) + start_new_thread( + ConnectionHandle, + ( + client, + priv, + ), + ) def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -180,13 +293,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' + status = "success" - self.logs.info({'server': 'ssh_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) + self.logs.info( + { + "server": "ssh_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) - if status == 'success': + if status == "success": return True else: self.kill_server() @@ -195,26 +340,36 @@ def run_server(self, process=False, auto=False): self.ssh_server_main() def close_port(self): - ret = close_port_wrapper('ssh_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("ssh_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('ssh_server', self.uuid, self.process) + ret = kill_server_wrapper("ssh_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): from paramiko import SSHClient, AutoAddPolicy + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password ssh = SSHClient() - ssh.set_missing_host_key_policy(AutoAddPolicy()) # if you have default ones, remove them before using this.. + ssh.set_missing_host_key_policy( + AutoAddPolicy() + ) # if you have default ones, remove them before using this.. ssh.connect(_ip, port=_port, username=_username, password=_password) -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qsshserver = QSSHServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qsshserver = QSSHServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qsshserver.run_server() diff --git a/honeypots/telnet_server.py b/honeypots/telnet_server.py index 1dd5a1a..ae35e12 100644 --- a/honeypots/telnet_server.py +++ b/honeypots/telnet_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,40 +8,60 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' - -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') +""" from twisted.conch.telnet import TelnetProtocol, TelnetTransport from twisted.internet.protocol import Factory from twisted.internet import reactor -from twisted.python import log as tlog from subprocess import Popen from os import path, getenv -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running +from honeypots.helper import ( + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + setup_logger, + set_local_vars, + check_if_server_is_running, +) from uuid import uuid4 from contextlib import suppress -class QTelnetServer(): +class QTelnetServer: def __init__(self, **kwargs): self.auto_disabled = None - self.random_servers = ['Ubuntu 18.04 LTS', 'Ubuntu 16.04.3 LTS', 'Welcome to Microsoft Telnet Server.'] + self.random_servers = [ + "Ubuntu 18.04 LTS", + "Ubuntu 16.04.3 LTS", + "Welcome to Microsoft Telnet Server.", + ] self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 23 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 23 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) def telent_server_main(self): _q_s = self @@ -61,26 +81,47 @@ def connectionMade(self): self._state = None self._user = None self._pass = None - self.transport.write(b'PC login: ') - self._state = b'Username' - _q_s.logs.info({'server': 'telnet_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + self.transport.write(b"PC login: ") + self._state = b"Username" + _q_s.logs.info( + { + "server": "telnet_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def dataReceived(self, data): data = data.strip() - if self._state == b'Username': + if self._state == b"Username": self._user = data - self._state = b'Password' - self.transport.write(b'Password: ') - elif self._state == b'Password': + self._state = b"Password" + self.transport.write(b"Password: ") + elif self._state == b"Password": username = self.check_bytes(self._user) password = self.check_bytes(data) - status = 'failed' + status = "failed" # may need decode if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' - _q_s.logs.info({'server': 'telnet_server', 'action': 'login', 'status': status, 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + status = "success" + _q_s.logs.info( + { + "server": "telnet_server", + "action": "login", + "status": status, + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) self.transport.loseConnection() else: self.transport.loseConnection() @@ -96,7 +137,7 @@ def connectionLost(self, reason): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -108,13 +149,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'telnet_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "telnet_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -123,31 +196,39 @@ def run_server(self, process=False, auto=False): self.telent_server_main() def close_port(self): - ret = close_port_wrapper('telnet_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("telnet_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('telnet_server', self.uuid, self.process) + ret = kill_server_wrapper("telnet_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from telnetlib import Telnet as TTelnet + _ip = ip or self.ip _port = port or self.port _username = username or self.username _password = password or self.password - _username = _username.encode('utf-8') - _password = _password.encode('utf-8') + _username = _username.encode("utf-8") + _password = _password.encode("utf-8") t = TTelnet(_ip, _port) - t.read_until(b'login: ') - t.write(_username + b'\n') - t.read_until(b'Password: ') - t.write(_password + b'\n') + t.read_until(b"login: ") + t.write(_username + b"\n") + t.read_until(b"Password: ") + t.write(_password + b"\n") -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qtelnetserver = QTelnetServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qtelnetserver = QTelnetServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qtelnetserver.run_server() diff --git a/honeypots/testing.py b/honeypots/testing.py index 32add94..ac362ac 100644 --- a/honeypots/testing.py +++ b/honeypots/testing.py @@ -1,10 +1,11 @@ import honeypots from time import sleep from pkg_resources import get_distribution + honeypots.clean_all() -print("Version: ", get_distribution('honeypots').version) +print("Version: ", get_distribution("honeypots").version) for server, cls in honeypots.__dict__.items(): - if server.endswith('Server'): + if server.endswith("Server"): temp_server = cls(options="capture_commands") temp_server.run_server(process=True, auto=True) sleep(2) diff --git a/honeypots/vnc_server.py b/honeypots/vnc_server.py index 0cdf611..ad7d99b 100644 --- a/honeypots/vnc_server.py +++ b/honeypots/vnc_server.py @@ -1,4 +1,4 @@ -''' +""" // ------------------------------------------------------------- // author Giga // project qeeqbox/honeypots @@ -8,57 +8,76 @@ // ------------------------------------------------------------- // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- -''' +""" -from warnings import filterwarnings -filterwarnings(action='ignore', module='.*OpenSSL.*') - -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor -from Crypto.Cipher import DES from binascii import unhexlify -from twisted.python import log as tlog +from contextlib import suppress +from os import getenv, path from subprocess import Popen -from os import path, getenv -#from vncdotool import api as vncapi -from honeypots.helper import close_port_wrapper, get_free_port, kill_server_wrapper, server_arguments, setup_logger, disable_logger, set_local_vars, check_if_server_is_running from uuid import uuid4 -from contextlib import suppress + +from Crypto.Cipher import DES +from twisted.internet import reactor +from twisted.internet.protocol import Factory, Protocol + +from honeypots.helper import ( + check_if_server_is_running, + close_port_wrapper, + get_free_port, + kill_server_wrapper, + server_arguments, + set_local_vars, + setup_logger, +) -class QVNCServer(): +class QVNCServer: def __init__(self, **kwargs): self.auto_disabled = None - self.challenge = unhexlify('00000000901234567890123456789012') - self.words = ['test'] + self.challenge = unhexlify("00000000901234567890123456789012") + self.words = ["test"] self.process = None - self.uuid = 'honeypotslogger' + '_' + __class__.__name__ + '_' + str(uuid4())[:8] - self.config = kwargs.get('config', '') + self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + self.config = kwargs.get("config", "") if self.config: self.logs = setup_logger(__class__.__name__, self.uuid, self.config) set_local_vars(self, self.config) else: self.logs = setup_logger(__class__.__name__, self.uuid, None) - self.ip = kwargs.get('ip', None) or (hasattr(self, 'ip') and self.ip) or '0.0.0.0' - self.port = (kwargs.get('port', None) and int(kwargs.get('port', None))) or (hasattr(self, 'port') and self.port) or 5900 - self.username = kwargs.get('username', None) or (hasattr(self, 'username') and self.username) or 'test' - self.password = kwargs.get('password', None) or (hasattr(self, 'password') and self.password) or 'test' - self.options = kwargs.get('options', '') or (hasattr(self, 'options') and self.options) or getenv('HONEYPOTS_OPTIONS', '') or '' - disable_logger(1, tlog) - - def load_words(self,): - with open(self.file_name, 'r') as file: + self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" + self.port = ( + (kwargs.get("port", None) and int(kwargs.get("port", None))) + or (hasattr(self, "port") and self.port) + or 5900 + ) + self.username = ( + kwargs.get("username", None) or (hasattr(self, "username") and self.username) or "test" + ) + self.password = ( + kwargs.get("password", None) or (hasattr(self, "password") and self.password) or "test" + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) + + def load_words( + self, + ): + with open(self.file_name) as file: self.words = file.read().splitlines() def decode(self, c, r): with suppress(Exception): for word in self.words: temp = word - word = word.strip('\n').ljust(8, '\00')[:8] + word = word.strip("\n").ljust(8, "\00")[:8] rev_word = [] - for i in range(0, 8): - rev_word.append(chr(int('{:08b}'.format(ord(word[i]))[::-1], 2))) - output = DES.new(''.join(rev_word).encode('utf-8'), DES.MODE_ECB).encrypt(c) + for i in range(8): + rev_word.append(chr(int(f"{ord(word[i]):08b}"[::-1], 2))) + output = DES.new("".join(rev_word).encode("utf-8"), DES.MODE_ECB).encrypt(c) if output == r: return temp return None @@ -67,7 +86,6 @@ def vnc_server_main(self): _q_s = self class CustomVNCProtocol(Protocol): - _state = None def check_bytes(self, string): @@ -77,32 +95,53 @@ def check_bytes(self, string): return str(string) def connectionMade(self): - self.transport.write(b'RFB 003.008\n') + self.transport.write(b"RFB 003.008\n") self._state = 1 - _q_s.logs.info({'server': 'vnc_server', 'action': 'connection', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port}) + _q_s.logs.info( + { + "server": "vnc_server", + "action": "connection", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) def dataReceived(self, data): if self._state == 1: - if data == b'RFB 003.008\n': + if data == b"RFB 003.008\n": self._state = 2 - self.transport.write(unhexlify('0102')) + self.transport.write(unhexlify("0102")) elif self._state == 2: - if data == b'\x02': + if data == b"\x02": self._state = 3 self.transport.write(_q_s.challenge) elif self._state == 3: with suppress(Exception): username = self.check_bytes(_q_s.decode(_q_s.challenge, data.hex())) password = self.check_bytes(data) - status = 'failed' + status = "failed" # may need decode if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password - status = 'success' + status = "success" else: password = data.hex() - _q_s.logs.info({'server': 'vnc_server', 'action': 'login', status: 'failed', 'src_ip': self.transport.getPeer().host, 'src_port': self.transport.getPeer().port, 'dest_ip': _q_s.ip, 'dest_port': _q_s.port, 'username': username, 'password': password}) + _q_s.logs.info( + { + "server": "vnc_server", + "action": "login", + status: "failed", + "src_ip": self.transport.getPeer().host, + "src_port": self.transport.getPeer().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + "username": username, + "password": password, + } + ) self.transport.loseConnection() else: self.transport.loseConnection() @@ -116,7 +155,7 @@ def connectionLost(self, reason): reactor.run() def run_server(self, process=False, auto=False): - status = 'error' + status = "error" run = False if process: if auto and not self.auto_disabled: @@ -128,13 +167,45 @@ def run_server(self, process=False, auto=False): run = True if run: - self.process = Popen(['python3', path.realpath(__file__), '--custom', '--ip', str(self.ip), '--port', str(self.port), '--username', str(self.username), '--password', str(self.password), '--options', str(self.options), '--config', str(self.config), '--uuid', str(self.uuid)]) + self.process = Popen( + [ + "python3", + path.realpath(__file__), + "--custom", + "--ip", + str(self.ip), + "--port", + str(self.port), + "--username", + str(self.username), + "--password", + str(self.password), + "--options", + str(self.options), + "--config", + str(self.config), + "--uuid", + str(self.uuid), + ] + ) if self.process.poll() is None and check_if_server_is_running(self.uuid): - status = 'success' - - self.logs.info({'server': 'vnc_server', 'action': 'process', 'status': status, 'src_ip': self.ip, 'src_port': self.port, 'username': self.username, 'password': self.password, 'dest_ip': self.ip, 'dest_port': self.port}) - - if status == 'success': + status = "success" + + self.logs.info( + { + "server": "vnc_server", + "action": "process", + "status": status, + "src_ip": self.ip, + "src_port": self.port, + "username": self.username, + "password": self.password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + if status == "success": return True else: self.kill_server() @@ -143,11 +214,11 @@ def run_server(self, process=False, auto=False): self.vnc_server_main() def close_port(self): - ret = close_port_wrapper('vnc_server', self.ip, self.port, self.logs) + ret = close_port_wrapper("vnc_server", self.ip, self.port, self.logs) return ret def kill_server(self): - ret = kill_server_wrapper('vnc_server', self.uuid, self.process) + ret = kill_server_wrapper("vnc_server", self.uuid, self.process) return ret def test_server(self, ip=None, port=None, username=None, password=None): @@ -156,13 +227,20 @@ def test_server(self, ip=None, port=None, username=None, password=None): port or self.port username or self.username password or self.password - #client = vncapi.connect('{}::{}'.format(self.ip, self.port), password=password) + # client = vncapi.connect('{}::{}'.format(self.ip, self.port), password=password) # client.captureScreen('screenshot.png') # client.disconnect() -if __name__ == '__main__': +if __name__ == "__main__": parsed = server_arguments() if parsed.docker or parsed.aws or parsed.custom: - qvncserver = QVNCServer(ip=parsed.ip, port=parsed.port, username=parsed.username, password=parsed.password, options=parsed.options, config=parsed.config) + qvncserver = QVNCServer( + ip=parsed.ip, + port=parsed.port, + username=parsed.username, + password=parsed.password, + options=parsed.options, + config=parsed.config, + ) qvncserver.run_server() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..19d98bf --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,150 @@ +[build-system] +requires = [ + "setuptools>=61.2", +] +build-backend = "setuptools.build_meta" + +[project] +name = "honeypots" +version = "0.64" +authors = [ + { name = "QeeqBox", email = "gigaqeeq@gmail.com" }, +] +description = "30 different honeypots in one package! (dhcp, dns, elastic, ftp, http proxy, https proxy, http, https, imap, ipp, irc, ldap, memcache, mssql, mysql, ntp, oracle, pjl, pop3, postgres, rdp, redis, sip, smb, smtp, snmp, socks5, ssh, telnet, vnc)" +readme = "README.rst" +requires-python = ">=3.8" +dependencies = [ + "twisted==21.7.0", + "psutil==5.9.0", + "psycopg2-binary==2.9.3", + "pycryptodome==3.19.0", + "requests==2.28.2", + "requests[socks]==2.28.2", + "impacket==0.9.24", + "paramiko==3.1.0", + "scapy==2.4.5", + "service_identity==21.1.0", + "netifaces==0.11.0", +] +license = {text = "AGPL-3.0"} + +[project.urls] +Homepage = "https://github.com/qeeqbox/honeypots" + +[project.optional-dependencies] +dev = [ + "dnspython==2.4.2", + "elasticsearch", + "ldap3", + "mysql-connector", + "pre-commit", + "pymssql", + "pysnmplib", + "pytest", + "redis", + "redis", + "vncdotool", +] + +[project.scripts] +honeypots = "honeypots.__main__:main_logic" + +[tool.setuptools] +packages = [ + "honeypots", + "honeypots.data", +] +include-package-data = true + +[tool.setuptools.package-data] +"honeypots.data" = [ + "*.html", +] + +[tool.ruff] +select = [ + "F", + "E", + "W", + "C90", + "N", + "UP", + "B", + "A", + "C4", + "EXE", + "FA", + "ISC", + "PIE", + "T20", + "PT", + "Q", + "RET", + "SIM", + "TCH", + "ARG", + "PTH", + "ERA", + "PL", + "PLR", + "PLW", + "PERF", + "RUF", +] +ignore = [ + "A003", + "PERF203", + "PERF401", + "RUF001", + "RUF002", + "RUF003", + "RUF015", + # pydantic only supports these from python>=3.9 + "UP006", + "UP007", + # rules may cause conflicts when used with the formatter + "ISC001", + "Q001", +] +fixable = [ + "F", + "E", + "W", + "C90", + "N", + "UP", + "B", + "A", + "C4", + "EXE", + "FA", + "ISC", + "PIE", + "T20", + "PT", + "Q", + "RET", + "SIM", + "TCH", + "ARG", + "PTH", + "ERA", + "PL", + "PLR", + "PLW", + "PERF", + "RUF", +] +exclude = [ + ".git", + ".ruff_cache", + ".venv", + "venv", + "data", +] +line-length = 99 +target-version = "py38" + +[tool.ruff.lint.per-file-ignores] +# don't check for "magic value" in tests +"tests/*" = ["PLR2004"] diff --git a/setup.py b/setup.py deleted file mode 100644 index da9d446..0000000 --- a/setup.py +++ /dev/null @@ -1,48 +0,0 @@ -from setuptools import setup - -with open("README.rst", "r") as f: - long_description = f.read() - -setup( - name="honeypots", - author="QeeqBox", - author_email="gigaqeeq@gmail.com", - description=r"30 different honeypots in one package! (dhcp, dns, elastic, ftp, http proxy, https proxy, http, https, imap, ipp, irc, ldap, memcache, mssql, mysql, ntp, oracle, pjl, pop3, postgres, rdp, redis, sip, smb, smtp, snmp, socks5, ssh, telnet, vnc)", - long_description=long_description, - version="0.64", - license="AGPL-3.0", - license_files=("LICENSE"), - url="https://github.com/qeeqbox/honeypots", - packages=["honeypots", "honeypots.data"], - entry_points={"console_scripts": ["honeypots=honeypots.__main__:main_logic"]}, - include_package_data=True, - package_data={"honeypots.data": ["*.html"]}, - install_requires=[ - "twisted==21.7.0", - "psutil==5.9.0", - "psycopg2-binary==2.9.3", - "pycryptodome==3.19.0", - "requests==2.28.2", - "requests[socks]==2.28.2", - "impacket==0.9.24", - "paramiko==3.1.0", - "scapy==2.4.5", - "service_identity==21.1.0", - "netifaces==0.11.0", - ], - extras_require={ - "test": [ - "redis", - "mysql-connector", - "dnspython==2.4.2", - "elasticsearch", - "pymssql", - "ldap3", - "pysnmplib", - "pytest", - "redis", - "vncdotool", - ] - }, - python_requires=">=3.8", -) diff --git a/tests/conftest.py b/tests/conftest.py index 2358328..582981e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,7 +27,7 @@ def config_for_testing(custom_config: dict) -> Iterator[Path]: yield config -@pytest.fixture +@pytest.fixture() def server_logs(request): custom_config = request.param.get("custom_config", {}) with config_for_testing(custom_config) as config_file: diff --git a/tests/data/test_template.html b/tests/data/test_template.html new file mode 100644 index 0000000..36727f6 --- /dev/null +++ b/tests/data/test_template.html @@ -0,0 +1,10 @@ + + + + + Test Template Title + + +

This is a template for testing!

+ + diff --git a/tests/test_dhcp_server.py b/tests/test_dhcp_server.py index c43511f..743d365 100644 --- a/tests/test_dhcp_server.py +++ b/tests/test_dhcp_server.py @@ -1,7 +1,5 @@ from __future__ import annotations -from time import sleep - import pytest from honeypots import QDHCPServer @@ -10,6 +8,7 @@ EXPECTED_KEYS, IP, load_logs_from_file, + wait_for_server, ) PORT = "50067" @@ -21,13 +20,9 @@ indirect=True, ) def test_dhcp_server(server_logs): - sleep(1) # give the server some time to start - - with connect_to(IP, PORT, udp=True) as connection: + with wait_for_server(PORT), connect_to(IP, PORT, udp=True) as connection: connection.send(b"\x03" * 240) - sleep(1) # give the server process some time to write logs - logs = load_logs_from_file(server_logs) assert len(logs) == 1 diff --git a/tests/test_dns_server.py b/tests/test_dns_server.py index 6ecde93..47ace22 100644 --- a/tests/test_dns_server.py +++ b/tests/test_dns_server.py @@ -1,7 +1,5 @@ from __future__ import annotations -from time import sleep - import pytest from dns.resolver import Resolver @@ -10,6 +8,7 @@ assert_connect_is_logged, IP, load_logs_from_file, + wait_for_server, ) PORT = "50053" @@ -21,24 +20,26 @@ indirect=True, ) def test_dns_server(server_logs): - sleep(1) # give the server some time to start - - resolver = Resolver(configure=False) - resolver.nameservers = [IP] - resolver.port = int(PORT) - domain = "example.org" - response = resolver.resolve(domain, "a") - - sleep(1) # give the server process some time to write logs + with wait_for_server(PORT): + resolver = Resolver(configure=False) + resolver.nameservers = [IP] + resolver.port = int(PORT) + domain = "example.org" + responses = [ + resolver.resolve(domain, "a", tcp=False), + resolver.resolve(domain, "a", tcp=True), + ] logs = load_logs_from_file(server_logs) - assert len(logs) == 2 - connect, query = logs + assert len(logs) == 3 + connect, *queries = logs assert_connect_is_logged(connect, PORT) - assert query["action"] == "query" - assert "data" in query - assert "\x01,\x0cA \x00\xff\xff\x7f\x08\x00" - "\x00\x01\x00\x00\xc8\x00J\x00\x00\x14\x00AA\xa7C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" - f"(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME={SERVICE})(CID=(PROGRAM={PROGRAM})(HOST=xxxxxxxxxxxxxx)" - f"(USER={USERNAME}))(CONNECTION_ID=xxxxxxxxxxxxxxxxxxxxxxxx))(ADDRESS=(PROTOCOL=tcp)(HOST={IP})(PORT={PORT})))" - ) - with connect_to(IP, PORT) as connection: + with wait_for_server(PORT), connect_to(IP, PORT) as connection: + payload = ( + "\x00\x00\x03\x04\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x01F\xb9\xd9@" + "\x00@\x06\x81\xd6\x7f\x00\x00\x01\x7f\x00\x00\x01\xbf\xce\x06\x13\xacW\xde\xc0Z\xb5" + "\x0cI\x80\x18\x02\x00\xff:\x00\x00\x01\x01\x08\n\x1bdZ^\x1bdZ^\x01\x12\x00\x00\x01" + "\x00\x00\x00\x01>\x01,\x0cA \x00\xff\xff\x7f\x08\x00\x00\x01\x00\x00\xc8\x00J\x00" + "\x00\x14\x00AA\xa7C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + f"(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME={SERVICE})(CID=(PROGRAM={PROGRAM})" + f"(HOST=xxxxxxxxxxxxxx)(USER={USERNAME}))(CONNECTION_ID=xxxxxxxxxxxxxxxxxxxxxxxx))" + f"(ADDRESS=(PROTOCOL=tcp)(HOST={IP})(PORT={PORT})))" + ) connection.send(payload.encode()) response, _ = connection.recvfrom(10000) - sleep(1) # give the server process some time to write logs - logs = load_logs_from_file(server_logs) assert len(logs) == 2 diff --git a/tests/test_pjl_server.py b/tests/test_pjl_server.py index 7940060..45fe00b 100644 --- a/tests/test_pjl_server.py +++ b/tests/test_pjl_server.py @@ -1,7 +1,5 @@ from __future__ import annotations -from time import sleep - import pytest from honeypots import QPJLServer @@ -10,6 +8,7 @@ connect_to, IP, load_logs_from_file, + wait_for_server, ) PORT = "59100" @@ -28,13 +27,9 @@ indirect=True, ) def test_pjl_server(server_logs): - sleep(1) # give the server some time to start - - with connect_to(IP, PORT) as connection: + with wait_for_server(PORT), connect_to(IP, PORT) as connection: connection.send(b"\x1b%-12345X@PJL prodinfo") - sleep(1) # give the server process some time to write logs - logs = load_logs_from_file(server_logs) assert len(logs) == 2 diff --git a/tests/test_pop3_server.py b/tests/test_pop3_server.py index 220a02c..ba78fa9 100644 --- a/tests/test_pop3_server.py +++ b/tests/test_pop3_server.py @@ -2,7 +2,6 @@ from contextlib import suppress from poplib import error_proto, POP3 -from time import sleep import pytest @@ -14,6 +13,7 @@ load_logs_from_file, PASSWORD, USERNAME, + wait_for_server, ) PORT = "50110" @@ -32,15 +32,11 @@ indirect=True, ) def test_pop3_server(server_logs): - sleep(1) # give the server some time to start - - with suppress(error_proto): + with wait_for_server(PORT), suppress(error_proto): client = POP3(IP, int(PORT)) client.user(USERNAME) client.pass_(PASSWORD) - sleep(1) # give the server process some time to write logs - logs = load_logs_from_file(server_logs) assert len(logs) == 4 diff --git a/tests/test_postgres_server.py b/tests/test_postgres_server.py index 0e84791..58dfe9b 100644 --- a/tests/test_postgres_server.py +++ b/tests/test_postgres_server.py @@ -1,7 +1,6 @@ from __future__ import annotations from contextlib import suppress -from time import sleep import pytest from psycopg2 import connect, OperationalError @@ -14,6 +13,7 @@ load_logs_from_file, PASSWORD, USERNAME, + wait_for_server, ) PORT = "55432" @@ -25,12 +25,8 @@ indirect=True, ) def test_postgres_server(server_logs): - sleep(1) # give the server some time to start - - with suppress(OperationalError): - db = connect(host=IP, port=PORT, user=USERNAME, password=PASSWORD) - - sleep(1) # give the server process some time to write logs + with wait_for_server(PORT), suppress(OperationalError): + connect(host=IP, port=PORT, user=USERNAME, password=PASSWORD) logs = load_logs_from_file(server_logs) diff --git a/tests/test_rdp_server.py b/tests/test_rdp_server.py index 774c878..60b9cd2 100644 --- a/tests/test_rdp_server.py +++ b/tests/test_rdp_server.py @@ -1,7 +1,5 @@ from __future__ import annotations -from time import sleep - import pytest from honeypots import QRDPServer @@ -10,6 +8,7 @@ connect_to, IP, load_logs_from_file, + wait_for_server, ) PORT = "53389" @@ -21,16 +20,13 @@ indirect=True, ) def test_rdp_server(server_logs): - sleep(1) # give the server some time to start - - with connect_to(IP, PORT) as connection: + with wait_for_server(PORT), connect_to(IP, PORT) as connection: connection.send(b"test") connection.send( - b"\x03\x00\x00*%\xe0\x00\x00\x00\x00\x00Cookie: mstshash=foobar\r\n\x01\x00\x08\x00\x03\x00\x00" + b"\x03\x00\x00*%\xe0\x00\x00\x00\x00\x00Cookie: " + b"mstshash=foobar\r\n\x01\x00\x08\x00\x03\x00\x00" ) - sleep(1) # give the server process some time to write logs - logs = load_logs_from_file(server_logs) assert len(logs) == 2 diff --git a/tests/test_redis_server.py b/tests/test_redis_server.py index 4ab3294..d265baf 100644 --- a/tests/test_redis_server.py +++ b/tests/test_redis_server.py @@ -1,7 +1,6 @@ from __future__ import annotations from contextlib import suppress -from time import sleep import pytest from redis import AuthenticationError, StrictRedis @@ -14,6 +13,7 @@ load_logs_from_file, PASSWORD, USERNAME, + wait_for_server, ) PORT = "56379" @@ -25,13 +25,11 @@ indirect=True, ) def test_redis_server(server_logs): - with suppress(AuthenticationError): + with wait_for_server(PORT), suppress(AuthenticationError): redis = StrictRedis.from_url(f"redis://{USERNAME}:{PASSWORD}@{IP}:{PORT}/1") for _ in redis.scan_iter("user:*"): pass - sleep(1) # give the server process some time to write logs - logs = load_logs_from_file(server_logs) assert len(logs) == 2 diff --git a/tests/test_sip_server.py b/tests/test_sip_server.py index 111cb6d..4aa4d78 100644 --- a/tests/test_sip_server.py +++ b/tests/test_sip_server.py @@ -1,11 +1,9 @@ from __future__ import annotations -from time import sleep - import pytest from honeypots import QSIPServer -from .utils import connect_to, IP, load_logs_from_file +from .utils import connect_to, IP, load_logs_from_file, wait_for_server PORT = "55060" EXPECTED_KEYS = ("action", "server", "src_ip", "src_port", "timestamp") @@ -21,9 +19,7 @@ indirect=True, ) def test_sip_server(server_logs): - sleep(1) # give the server some time to start - - with connect_to(IP, PORT, udp=True) as connection: + with wait_for_server(PORT), connect_to(IP, PORT, udp=True) as connection: payload = ( "INVITE sip:user_1@test.test SIP/2.0\r\n" f"To: {TO}\r\n" @@ -38,8 +34,6 @@ def test_sip_server(server_logs): ) connection.send(payload.encode()) - sleep(1) # give the server process some time to write logs - logs = load_logs_from_file(server_logs) assert len(logs) == 2 diff --git a/tests/test_smb_server.py b/tests/test_smb_server.py index 883871e..17a2711 100644 --- a/tests/test_smb_server.py +++ b/tests/test_smb_server.py @@ -1,7 +1,5 @@ from __future__ import annotations -from time import sleep - import pytest from impacket.smbconnection import SMBConnection @@ -12,6 +10,7 @@ load_logs_from_file, PASSWORD, USERNAME, + wait_for_server, ) PORT = "50445" @@ -23,13 +22,10 @@ indirect=True, ) def test_smb_server(server_logs): - sleep(5) # give the server some time to start - - smb_client = SMBConnection(IP, IP, sess_port=PORT) - smb_client.login(USERNAME, PASSWORD) - smb_client.close() - - sleep(1) # give the server process some time to write logs + with wait_for_server(PORT): + smb_client = SMBConnection(IP, IP, sess_port=PORT) + smb_client.login(USERNAME, PASSWORD) + smb_client.close() logs = load_logs_from_file(server_logs) diff --git a/tests/test_smtp_server.py b/tests/test_smtp_server.py index ed041c0..e1d3ac0 100644 --- a/tests/test_smtp_server.py +++ b/tests/test_smtp_server.py @@ -2,7 +2,6 @@ from base64 import b64decode from smtplib import SMTP -from time import sleep import pytest @@ -14,6 +13,7 @@ load_logs_from_file, PASSWORD, USERNAME, + wait_for_server, ) PORT = "50025" @@ -39,25 +39,23 @@ indirect=True, ) def test_smtp_server(server_logs): - sleep(1) # give server time to start - - client = SMTP(IP, int(PORT)) - client.ehlo() - client.login(USERNAME, PASSWORD) - client.sendmail("fromtest", "totest", "Nothing") - client.quit() - - sleep(1) # give the server process some time to write logs + with wait_for_server(PORT): + client = SMTP(IP, int(PORT)) + client.ehlo() + client.login(USERNAME, PASSWORD) + client.sendmail("fromtest", "totest", "Nothing") + client.quit() logs = load_logs_from_file(server_logs) - assert len(logs) == 8 - connect, auth, login, *additional = logs - assert_connect_is_logged(connect, PORT) + assert len(logs) == 9 + connect1, connect2, auth, login, *additional = logs + assert_connect_is_logged(connect1, PORT) + assert_connect_is_logged(connect2, PORT) assert_login_is_logged(login) assert auth["data"]["command"] == "AUTH" assert b64decode(auth["data"]["data"]).decode() == f"\x00{USERNAME}\x00{PASSWORD}" for entry, expected in zip(additional, EXPECTED_DATA): - assert entry + assert entry["data"] == expected diff --git a/tests/test_snmp_server.py b/tests/test_snmp_server.py index 2ad05c7..528d54f 100644 --- a/tests/test_snmp_server.py +++ b/tests/test_snmp_server.py @@ -1,13 +1,22 @@ from __future__ import annotations import pytest -from pysnmp.hlapi import CommunityData, ContextData, getCmd, ObjectIdentity, ObjectType, SnmpEngine, UdpTransportTarget +from pysnmp.hlapi import ( + CommunityData, + ContextData, + getCmd, + ObjectIdentity, + ObjectType, + SnmpEngine, + UdpTransportTarget, +) from honeypots import QSNMPServer from .utils import ( assert_connect_is_logged, IP, load_logs_from_file, + wait_for_server, ) PORT = "50161" @@ -19,14 +28,15 @@ indirect=True, ) def test_snmp_server(server_logs): - g = getCmd( - SnmpEngine(), - CommunityData("public"), - UdpTransportTarget((IP, int(PORT))), - ContextData(), - ObjectType(ObjectIdentity("1.3.6.1.4.1.9.9.618.1.4.1.0")), - ) - next(g) + with wait_for_server(PORT): + g = getCmd( + SnmpEngine(), + CommunityData("public"), + UdpTransportTarget((IP, int(PORT))), + ContextData(), + ObjectType(ObjectIdentity("1.3.6.1.4.1.9.9.618.1.4.1.0")), + ) + next(g) logs = load_logs_from_file(server_logs) @@ -35,4 +45,8 @@ def test_snmp_server(server_logs): assert_connect_is_logged(connect, PORT) assert query["action"] == "query" - assert query["data"] == {"community": "public", "oids": "1.3.6.1.4.1.9.9.618.1.4.1.0", "version": "1"} + assert query["data"] == { + "community": "public", + "oids": "1.3.6.1.4.1.9.9.618.1.4.1.0", + "version": "1", + } diff --git a/tests/test_socks5_server.py b/tests/test_socks5_server.py index b8de9a9..2dedf9a 100644 --- a/tests/test_socks5_server.py +++ b/tests/test_socks5_server.py @@ -1,7 +1,6 @@ from __future__ import annotations from contextlib import suppress -from time import sleep import pytest import requests @@ -14,6 +13,7 @@ load_logs_from_file, PASSWORD, USERNAME, + wait_for_server, ) PORT = "51080" @@ -25,14 +25,12 @@ indirect=True, ) def test_socks5_server(server_logs): - with suppress(requests.exceptions.ConnectionError): + with wait_for_server(PORT), suppress(requests.exceptions.ConnectionError): requests.get( "http://127.0.0.1/", proxies={"http": f"socks5://{USERNAME}:{PASSWORD}@{IP}:{PORT}"}, ) - sleep(1) # give the server process some time to write logs - logs = load_logs_from_file(server_logs) assert len(logs) == 2 diff --git a/tests/test_ssh_server.py b/tests/test_ssh_server.py index 5f32cfc..98b3162 100644 --- a/tests/test_ssh_server.py +++ b/tests/test_ssh_server.py @@ -1,12 +1,17 @@ from __future__ import annotations -from time import sleep - import pytest from paramiko import AutoAddPolicy, SSHClient from honeypots import QSSHServer -from .utils import assert_connect_is_logged, IP, load_logs_from_file, PASSWORD, USERNAME +from .utils import ( + assert_connect_is_logged, + IP, + load_logs_from_file, + PASSWORD, + USERNAME, + wait_for_server, +) PORT = 50022 SERVER_CONFIG = { @@ -24,14 +29,11 @@ indirect=True, ) def test_ssh_server(server_logs): - sleep(1) # give the server some time to start - - ssh = SSHClient() - ssh.set_missing_host_key_policy(AutoAddPolicy()) - ssh.connect(IP, port=PORT, username=USERNAME, password=PASSWORD) - ssh.close() - - sleep(1) # give the server process some time to write logs + with wait_for_server(PORT): + ssh = SSHClient() + ssh.set_missing_host_key_policy(AutoAddPolicy()) + ssh.connect(IP, port=PORT, username=USERNAME, password=PASSWORD) + ssh.close() logs = load_logs_from_file(server_logs) diff --git a/tests/test_telnet_server.py b/tests/test_telnet_server.py index 98073b6..d353fdf 100644 --- a/tests/test_telnet_server.py +++ b/tests/test_telnet_server.py @@ -1,7 +1,6 @@ from __future__ import annotations from telnetlib import Telnet -from time import sleep import pytest @@ -13,6 +12,7 @@ load_logs_from_file, PASSWORD, USERNAME, + wait_for_server, ) PORT = "50023" @@ -24,15 +24,12 @@ indirect=True, ) def test_telnet_server(server_logs): - sleep(1) # give the server some time to start - - telnet_client = Telnet(IP, int(PORT)) - telnet_client.read_until(b"login: ") - telnet_client.write(USERNAME.encode() + b"\n") - telnet_client.read_until(b"Password: ") - telnet_client.write(PASSWORD.encode() + b"\n") - - sleep(1) # give the server process some time to write logs + with wait_for_server(PORT): + telnet_client = Telnet(IP, int(PORT)) + telnet_client.read_until(b"login: ") + telnet_client.write(USERNAME.encode() + b"\n") + telnet_client.read_until(b"Password: ") + telnet_client.write(PASSWORD.encode() + b"\n") logs = load_logs_from_file(server_logs) diff --git a/tests/test_vnc_server.py b/tests/test_vnc_server.py index 834f40f..632b6dc 100644 --- a/tests/test_vnc_server.py +++ b/tests/test_vnc_server.py @@ -1,13 +1,12 @@ from __future__ import annotations from multiprocessing import Process -from time import sleep import pytest from vncdotool import api from honeypots import QVNCServer -from .utils import assert_connect_is_logged, IP, load_logs_from_file, PASSWORD +from .utils import assert_connect_is_logged, IP, load_logs_from_file, PASSWORD, wait_for_server PORT = "55900" @@ -25,9 +24,9 @@ def _connect_to_vnc(): def test_vnc_server(server_logs): # This VNC API creates a blocking daemon thread that can't be trivially stopped, # so we just run it in a process and terminate that instead - process = Process(target=_connect_to_vnc) - process.start() - sleep(1) # give the server process some time to write logs + with wait_for_server(PORT): + process = Process(target=_connect_to_vnc) + process.start() process.terminate() process.join(timeout=5) diff --git a/tests/utils.py b/tests/utils.py index a273a82..6d453ae 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,8 +2,14 @@ import json from contextlib import contextmanager -from pathlib import Path from socket import AF_INET, IPPROTO_UDP, SOCK_DGRAM, SOCK_STREAM, socket +from time import sleep, time +from typing import TYPE_CHECKING + +import psutil + +if TYPE_CHECKING: + from pathlib import Path IP = "127.0.0.1" USERNAME = "testing" @@ -12,7 +18,7 @@ def load_logs_from_file(log_folder: Path) -> list[dict]: - log_files = [f for f in log_folder.iterdir()] + log_files = list(log_folder.iterdir()) assert len(log_files) == 1 log_file = log_files[0] logs = [] @@ -53,3 +59,24 @@ def assert_login_is_logged(login: dict[str, str]): assert login["username"] == USERNAME assert login["password"] == PASSWORD assert login["status"] == "success" + + +@contextmanager +def wait_for_server(port: str | int): + _wait_for_service(int(port)) + yield + sleep(0.5) # give the server process some time to write logs + + +def _wait_for_service(port: int, interval: float = 0.1, timeout: int = 5.0): + start_time = time() + while True: + if _service_runs(port): + return + sleep(interval) + if time() - start_time > timeout: + raise TimeoutError() + + +def _service_runs(port: int) -> bool: + return any(service.laddr.port == port for service in psutil.net_connections())