diff --git a/honeypots/__init__.py b/honeypots/__init__.py index d6bffe3..df9d946 100644 --- a/honeypots/__init__.py +++ b/honeypots/__init__.py @@ -6,7 +6,6 @@ is_privileged, clean_all, close_port_wrapper, - disable_logger, get_free_port, get_running_servers, kill_server_wrapper, @@ -78,7 +77,6 @@ "is_privileged", "clean_all", "close_port_wrapper", - "disable_logger", "get_free_port", "get_running_servers", "kill_server_wrapper", diff --git a/honeypots/base_http_server.py b/honeypots/base_http_server.py new file mode 100644 index 0000000..9207663 --- /dev/null +++ b/honeypots/base_http_server.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +from abc import ABC +from cgi import FieldStorage +from contextlib import suppress +from random import choice + +from twisted.web.resource import Resource + +from honeypots.base_server import BaseServer +from honeypots.helper import load_template, get_headers_and_ip_from_request, check_bytes + + +class BaseHttpServer(BaseServer, ABC): + def __init__(self, **kwargs): + super().__init__(**kwargs) + 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", + ] + ) + + class MainResource(Resource): + isLeaf = True + home_file = load_template("home.html") + login_file = load_template("login.html") + + def __init__(self, *args, hp_server=None, **kwargs): + super().__init__(*args, **kwargs) + self.hp_server = hp_server + self.headers = {} + + def render(self, request): + client_ip, headers = get_headers_and_ip_from_request(request, self.hp_server.options) + + with suppress(Exception): + log_data = { + "server": self.hp_server.NAME, + "action": "connection", + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": self.hp_server.ip, + "dest_port": self.hp_server.port, + } + if "capture_commands" in self.hp_server.options: + log_data["data"] = headers + self.hp_server.logs.info(log_data) + + if self.hp_server.mocking_server != "": + request.responseHeaders.removeHeader("Server") + request.responseHeaders.addRawHeader("Server", self.hp_server.mocking_server) + + if request.method in (b"GET", b"POST"): + self.hp_server.logs.info( + { + "server": self.hp_server.NAME, + "action": request.method.decode(), + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": self.hp_server.ip, + "dest_port": self.hp_server.port, + } + ) + + if request.method == b"GET": + if ( + request.uri == b"/login.html" + and self.hp_server.username != "" + and self.hp_server.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") + return self.login_file + + if request.method == b"POST": + self.headers = request.getAllHeaders() + if ( + request.uri in (b"/login.html", b"/") + and self.hp_server.username != "" + and self.hp_server.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 = check_bytes(form["username"].value) + password = check_bytes(form["password"].value) + self.hp_server.log_login( + username, password, client_ip, request.getClientAddress().port + ) + + request.responseHeaders.addRawHeader("Content-Type", "text/html; charset=utf-8") + return self.home_file diff --git a/honeypots/base_server.py b/honeypots/base_server.py new file mode 100644 index 0000000..074fcbd --- /dev/null +++ b/honeypots/base_server.py @@ -0,0 +1,133 @@ +from __future__ import annotations + +import inspect +from abc import ABC, abstractmethod +from os import getenv +from shlex import split +from subprocess import Popen +from uuid import uuid4 + +from honeypots.helper import ( + setup_logger, + set_local_vars, + set_up_error_logging, + close_port_wrapper, + kill_server_wrapper, + get_free_port, + check_if_server_is_running, +) + + +class BaseServer(ABC): + NAME = "base" + DEFAULT_PORT = 0 + DEFAULT_USERNAME = "test" + DEFAULT_PASSWORD = "test" + + def __init__(self, **kwargs): + self.auto_disabled = None + self.process = None + 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 self.DEFAULT_PORT + ) + self.username = ( + kwargs.get("username") + or (hasattr(self, "username") and self.username) + or self.DEFAULT_USERNAME + ) + self.password = ( + kwargs.get("password") + or (hasattr(self, "password") and self.password) + or self.DEFAULT_PASSWORD + ) + self.options = ( + kwargs.get("options", "") + or (hasattr(self, "options") and self.options) + or getenv("HONEYPOTS_OPTIONS", "") + or "" + ) + self.logger = set_up_error_logging() + + def close_port(self): + return close_port_wrapper(self.NAME, self.ip, self.port, self.logs) + + def kill_server(self): + return kill_server_wrapper(self.NAME, self.uuid, self.process) + + @abstractmethod + def server_main(self): + pass + + def run_server(self, process: bool = False, auto: bool = False) -> bool | None: + status = "error" + run = False + if not process: + self.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: + file = inspect.getfile(self.__class__) + command = ( + f"python3 {file} --custom --ip {self.ip} " f"--port {self.port} --uuid {self.uuid}" + ) + if self.options: + command += f" --options '{self.options}'" + if self.config: + command += f" --config '{self.config}'" + self.process = Popen(split(command)) + 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 log_login(self, username: str, password: str, ip: str, port: int): + status = "success" if self._login_is_correct(username, password) else "failed" + self.logs.info( + { + "server": self.NAME, + "action": "login", + "status": status, + "src_ip": ip, + "src_port": port, + "username": username, + "password": password, + "dest_ip": self.ip, + "dest_port": self.port, + } + ) + + def _login_is_correct(self, username: str, password: str) -> bool: + return username == self.username and password == self.password diff --git a/honeypots/data/home.html b/honeypots/data/home.html new file mode 100644 index 0000000..c184ba4 --- /dev/null +++ b/honeypots/data/home.html @@ -0,0 +1,21 @@ + + + + + + + Login + + + +
+
+
+ We'll back soon.. +
+
+
+ + diff --git a/honeypots/data/login.html b/honeypots/data/login.html new file mode 100644 index 0000000..443dc34 --- /dev/null +++ b/honeypots/data/login.html @@ -0,0 +1,29 @@ + + + + + + + Login + + + +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + diff --git a/honeypots/dhcp_server.py b/honeypots/dhcp_server.py index 3a01a3d..8aabb9f 100644 --- a/honeypots/dhcp_server.py +++ b/honeypots/dhcp_server.py @@ -10,58 +10,27 @@ // ------------------------------------------------------------- """ -from twisted.internet.protocol import DatagramProtocol -from twisted.internet import reactor -from struct import unpack, error as StructError from socket import inet_aton -from subprocess import Popen -from os import path, getenv +from struct import error as StructError, unpack + +from twisted.internet import reactor +from twisted.internet.protocol import DatagramProtocol + +from honeypots.base_server import BaseServer 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, + check_bytes, ) -from uuid import uuid4 -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", "") - 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 "" - ) +class QDHCPServer(BaseServer): + NAME = "dhcp_server" + DEFAULT_PORT = 67 - def dhcp_server_main(self): + def server_main(self): _q_s = self class CustomDatagramProtocolProtocol(DatagramProtocol): - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def payload(self, value, message): ( op, @@ -114,7 +83,7 @@ def parse_options(self, raw): tag_size -= 1 tag += chr(b) if tag_size == 0: - options.update({self.check_bytes(tag_name): self.check_bytes(tag)}) + options.update({check_bytes(tag_name): check_bytes(tag)}) tag_name = None tag_size = None tag = "" @@ -129,7 +98,7 @@ def datagramReceived(self, data, addr): data.update({"mac_address": mac_address}) _q_s.logs.info( { - "server": "dhcp_server", + "server": _q_s.NAME, "action": "query", "status": "success", "src_ip": addr[0], @@ -145,67 +114,6 @@ def datagramReceived(self, data, addr): ) reactor.run() - def run_server(self, process=False, auto=False): - 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": "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() - return False - else: - self.dhcp_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None): pass diff --git a/honeypots/dns_server.py b/honeypots/dns_server.py index a05fcd4..48aaeb0 100644 --- a/honeypots/dns_server.py +++ b/honeypots/dns_server.py @@ -12,50 +12,27 @@ from __future__ import annotations +from contextlib import suppress + +from twisted.internet import defer, reactor from twisted.names import dns, error, client from twisted.names.server import DNSServerFactory -from twisted.internet import defer, reactor -from subprocess import Popen -from os import path, getenv + +from honeypots.base_server import BaseServer 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(BaseServer): + NAME = "dns_server" + DEFAULT_PORT = 53 + def __init__(self, **kwargs): - self.auto_disabled = None + super().__init__(**kwargs) self.resolver_addresses = [("8.8.8.8", 53)] - self.process = None - 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 "" - ) - - def dns_server_main(self): + + def server_main(self): _q_s = self class CustomClientResolver(client.Resolver): @@ -78,7 +55,7 @@ def gotResolverResponse(self, response, protocol, message, address): for item in items: _q_s.logs.info( { - "server": "dns_server", + "server": _q_s.NAME, "action": "query", "src_ip": src_ip, "src_port": src_port, @@ -93,7 +70,7 @@ class CustomDnsUdpProtocol(dns.DNSDatagramProtocol): def datagramReceived(self, data: bytes, addr: tuple[str, int]): _q_s.logs.info( { - "server": "dns_server", + "server": _q_s.NAME, "action": "connection", "src_ip": addr[0], "src_port": addr[1], @@ -111,68 +88,6 @@ def datagramReceived(self, data: bytes, addr: tuple[str, int]): reactor.listenTCP(self.port, self.factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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": "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() - return False - else: - self.dns_server_main() - return None - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, domain=None): with suppress(Exception): from dns.resolver import Resolver diff --git a/honeypots/elastic_server.py b/honeypots/elastic_server.py index 4b6b105..d126a58 100644 --- a/honeypots/elastic_server.py +++ b/honeypots/elastic_server.py @@ -11,83 +11,27 @@ """ from base64 import b64encode, b64decode -from json import dumps +from contextlib import suppress from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer +from json import dumps +from ssl import wrap_socket from urllib.parse import urlparse from zlib import compressobj, DEFLATED -from subprocess import Popen -from ssl import wrap_socket -from uuid import uuid4 -from os import path, getenv -from OpenSSL import crypto -from tempfile import gettempdir, _get_candidate_names + +from honeypots.base_server import BaseServer 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, + create_certificate, + check_bytes, ) -from contextlib import suppress - -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", "") - 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 "" - ) - 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().CN = next(_get_candidate_names()) - c.set_serial_number(0) - before, after = (0, 60 * 60 * 24 * 365 * 2) - c.gmtime_adj_notBefore(before) - 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)) +class QElasticServer(BaseServer): + NAME = "elastic_server" + DEFAULT_PORT = 9200 + DEFAULT_USERNAME = "elastic" - def elastic_server_main(self): + def server_main(self): _q_s = self class CustomElasticServerHandler(SimpleHTTPRequestHandler): @@ -97,19 +41,12 @@ class CustomElasticServerHandler(SimpleHTTPRequestHandler): def _dump_headers(self): headers = {} with suppress(Exception): - - def check_bytes(string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - for item, value in dict(self.headers).items(): headers.update({check_bytes(item): check_bytes(value)}) _q_s.logs.info( { - "server": "elastic_server", + "server": _q_s.NAME, "action": "dump", "data": check_bytes(self.raw_requestline), "src_ip": self.client_address[0], @@ -174,7 +111,7 @@ def do_GET(self): if self.headers.get("Authorization") is None: _q_s.logs.info( { - "server": "elastic_server", + "server": _q_s.NAME, "action": "login", "status": "failed", "src_ip": self.client_address[0], @@ -218,7 +155,7 @@ def do_GET(self): extracted = "" _q_s.logs.info( { - "server": "elastic_server", + "server": _q_s.NAME, "action": "login", "status": "success", "src_ip": self.client_address[0], @@ -401,7 +338,7 @@ def do_GET(self): username, password = basic.split(":") _q_s.logs.info( { - "server": "elastic_server", + "server": _q_s.NAME, "action": "login", "status": "failed", "src_ip": self.client_address[0], @@ -452,7 +389,7 @@ def log_message(self, format, *args): def handle_one_request(self): _q_s.logs.info( { - "server": "elastic_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.client_address[0], "src_port": self.client_address[1], @@ -474,84 +411,16 @@ def set_auth_key(self, username, password): 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, - ) - server.serve_forever() - - def run_server(self, process=False, auto=False): - 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), - "--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, - } + with create_certificate() as (cert, key): + server = CustomElasticServer((self.ip, self.port)) + server.set_auth_key(self.username, self.password) + server.socket = wrap_socket( + server.socket, + keyfile=key, + certfile=cert, + server_side=True, ) - - if status == "success": - return True - else: - self.kill_server() - return False - else: - self.elastic_server_main() - return None - - def close_port(self): - 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) - return ret + server.serve_forever() def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): diff --git a/honeypots/ftp_server.py b/honeypots/ftp_server.py index f810312..826e5d0 100644 --- a/honeypots/ftp_server.py +++ b/honeypots/ftp_server.py @@ -10,6 +10,15 @@ // ------------------------------------------------------------- """ +from contextlib import suppress +from random import choice +from tempfile import TemporaryDirectory + +from twisted.cred import portal, credentials +from twisted.cred.checkers import ICredentialsChecker +from twisted.cred.error import UnauthorizedLogin, UnhandledCredentials +from twisted.cred.portal import Portal +from twisted.internet import reactor, defer from twisted.protocols.ftp import ( FTPAnonymousShell, FTPFactory, @@ -19,33 +28,22 @@ 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, UnhandledCredentials -from twisted.cred.checkers import ICredentialsChecker -from zope.interface import implementer from twisted.python import filepath -from random import choice -from subprocess import Popen -from os import path, getenv +from zope.interface import implementer + +from honeypots.base_server import BaseServer 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, + check_bytes, ) -from uuid import uuid4 -from contextlib import suppress -from tempfile import TemporaryDirectory -class QFTPServer: +class QFTPServer(BaseServer): + NAME = "ftp_server" + DEFAULT_PORT = 21 + def __init__(self, **kwargs): - self.auto_disabled = None + super().__init__(**kwargs) self.mocking_server = choice( [ "ProFTPD 1.2.10", @@ -56,35 +54,9 @@ def __init__(self, **kwargs): "ProFTPD 1.2.8", ] ) - self.process = None - 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.temp_folder = TemporaryDirectory() - def ftp_server_main(self): + def server_main(self): _q_s = self @implementer(portal.IRealm) @@ -103,16 +75,10 @@ def requestAvatar(self, avatarId, mind, *interfaces): class CustomAccess: credentialInterfaces = (credentials.IAnonymous, credentials.IUsernamePassword) - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def requestAvatarId(self, credentials): with suppress(Exception): - username = self.check_bytes(credentials.username) - password = self.check_bytes(credentials.password) + username = check_bytes(credentials.username) + password = check_bytes(credentials.password) if username == _q_s.username and password == _q_s.password: username = _q_s.username password = _q_s.password @@ -120,16 +86,10 @@ def requestAvatarId(self, credentials): return defer.fail(UnauthorizedLogin()) class CustomFTPProtocol(FTP): - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def connectionMade(self): _q_s.logs.info( { - "server": "ftp_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -146,11 +106,11 @@ def processCommand(self, cmd, *params): if "capture_commands" in _q_s.options: _q_s.logs.info( { - "server": "ftp_server", + "server": _q_s.NAME, "action": "command", "data": { - "cmd": self.check_bytes(cmd.upper()), - "args": self.check_bytes(params), + "cmd": check_bytes(cmd.upper()), + "args": check_bytes(params), }, "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -161,16 +121,14 @@ def processCommand(self, cmd, *params): return super().processCommand(cmd, *params) def ftp_PASS(self, password): - username = self.check_bytes(self._user) - password = self.check_bytes(password) + username = check_bytes(self._user) + password = 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": "ftp_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.transport.getPeer().host, @@ -214,74 +172,6 @@ def _ebLogin(failure): reactor.listenTCP(port=self.port, factory=factory) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": "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": - return True - else: - self.kill_server() - return False - else: - self.ftp_server_main() - return None - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from ftplib import FTP as FFTP diff --git a/honeypots/helper.py b/honeypots/helper.py index ef48867..4c22c52 100644 --- a/honeypots/helper.py +++ b/honeypots/helper.py @@ -9,11 +9,13 @@ // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- """ +from __future__ import annotations + import logging import sys from argparse import ArgumentParser from collections.abc import Mapping -from contextlib import suppress +from contextlib import suppress, contextmanager from datetime import datetime from json import dumps, JSONEncoder, load from logging import DEBUG, Formatter, getLogger, Handler @@ -24,10 +26,12 @@ 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 tempfile import _get_candidate_names, gettempdir, NamedTemporaryFile from time import sleep +from typing import Any, Iterator from urllib.parse import urlparse +from OpenSSL import crypto from psutil import process_iter from psycopg2 import connect as psycopg2_connect, sql @@ -155,12 +159,6 @@ def get_running_servers(): 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) - - def setup_logger(name, temp_name, config, drop=False): logs = "terminal" logs_location = "" @@ -687,3 +685,60 @@ def server_arguments(): ) _server_parsergroupdef.add_argument("--uuid", type=str, help="unique id", required=False) return _server_parser.parse_args() + + +@contextmanager +def create_certificate() -> Iterator[tuple[str, str]]: + pk = crypto.PKey() + pk.generate_key(crypto.TYPE_RSA, 2048) + certificate = crypto.X509() + certificate.get_subject().C = "US" + certificate.get_subject().ST = "OR" + certificate.get_subject().L = "None" + certificate.get_subject().O = "None" + certificate.get_subject().OU = "None" + certificate.get_subject().CN = next(_get_candidate_names()) + certificate.set_serial_number(0) + before, after = (0, 60 * 60 * 24 * 365 * 2) + certificate.gmtime_adj_notBefore(before) + certificate.gmtime_adj_notAfter(after) + certificate.set_issuer(certificate.get_subject()) + certificate.set_pubkey(pk) + certificate.sign(pk, "sha256") + with NamedTemporaryFile() as cert, NamedTemporaryFile() as key: + cert_path = Path(cert.name) + key_path = Path(key.name) + cert_path.write_bytes(crypto.dump_certificate(crypto.FILETYPE_PEM, certificate)) + key_path.write_bytes(crypto.dump_privatekey(crypto.FILETYPE_PEM, pk)) + yield cert.name, key.name + + +def check_bytes(string: Any) -> str: + if isinstance(string, bytes): + return string.decode("utf-8", errors="replace") + return str(string) + + +def load_template(filename: str) -> str: + file_path = Path(__file__).parent / "data" / filename + return file_path.read_text() + + +def get_headers_and_ip_from_request(request, options): + headers = {} + client_ip = "" + with suppress(Exception): + 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)}) + if "fix_get_client_ip" in 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 client_ip == "": + client_ip = request.getClientAddress().host + return client_ip, headers diff --git a/honeypots/http_proxy_server.py b/honeypots/http_proxy_server.py index 66c63f2..fceca05 100644 --- a/honeypots/http_proxy_server.py +++ b/honeypots/http_proxy_server.py @@ -11,59 +11,30 @@ """ from __future__ import annotations +from contextlib import suppress +from email.parser import BytesParser from pathlib import Path -from shlex import split from dns.resolver import resolve as dsnquery from twisted.internet import reactor from twisted.internet.protocol import Protocol, Factory -from subprocess import Popen -from email.parser import BytesParser -from os import getenv + +from honeypots.base_server import BaseServer 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, + load_template, ) -from uuid import uuid4 -from contextlib import suppress - -DUMMY_TEMPLATE = (Path(__file__).parent / "data" / "dummy_page.html").read_text() +DUMMY_TEMPLATE = load_template("dummy_page.html") -class QHTTPProxyServer: +class QHTTPProxyServer(BaseServer): NAME = "http_proxy_server" + DEFAULT_PORT = 8080 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.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 "" - ) - self.logger = set_up_error_logging() + super().__init__(**kwargs) self.template_contents: str | None = self._load_template() def _load_template(self) -> str | None: @@ -78,7 +49,7 @@ def _load_template(self) -> str | None: self.logger.error(f"[{self.NAME}]: Template file {self.template} not found") return None - def http_proxy_server_main(self): + def server_main(self): _q_s = self class CustomProtocolParent(Protocol): @@ -135,54 +106,6 @@ def write(self, data): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False) -> bool | None: - status = "error" - run = False - 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): - return close_port_wrapper(self.NAME, self.ip, self.port, self.logs) - - def kill_server(self): - 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 diff --git a/honeypots/http_server.py b/honeypots/http_server.py index 494b1c3..e58881b 100644 --- a/honeypots/http_server.py +++ b/honeypots/http_server.py @@ -10,349 +10,26 @@ // ------------------------------------------------------------- """ -from cgi import FieldStorage from contextlib import suppress -from os import getenv, path -from random import choice -from subprocess import Popen -from tempfile import _get_candidate_names, gettempdir -from uuid import uuid4 from twisted.internet import reactor -from twisted.web.resource import Resource from twisted.web.server import Site +from honeypots.base_http_server import BaseHttpServer 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: - 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.process = None - 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 "" - ) - - def http_server_main(self): - _q_s = self - - class MainResource(Resource): - isLeaf = True - - home_file = b""" - - - - - - - Login - - - -
-
-
- We'll back soon.. -
-
-
- -""" - - login_file = b""" - - - - - - Login - - - -
-
-
-
-
- -
-
- -
-
- -
-
-
-
-
- - -""" - - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - 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)}) - - 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]) +class QHTTPServer(BaseHttpServer): + NAME = "http_server" + DEFAULT_PORT = 80 - 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, - } - ) - 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" - ) - return self.login_file - - request.responseHeaders.addRawHeader( - "Content-Type", "text/html; charset=utf-8" - ) - return self.login_file - - 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.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" - ) - return self.home_file - else: - request.responseHeaders.addRawHeader( - "Content-Type", "text/html; charset=utf-8" - ) - return self.home_file - - reactor.listenTCP(self.port, Site(MainResource())) + def server_main(self): + resource = self.MainResource(hp_server=self) + reactor.listenTCP(self.port, Site(resource)) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": - return True - else: - self.kill_server() - return False - else: - self.http_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from requests import get, post diff --git a/honeypots/https_server.py b/honeypots/https_server.py index ec5e803..fd030f9 100644 --- a/honeypots/https_server.py +++ b/honeypots/https_server.py @@ -10,370 +10,28 @@ // ------------------------------------------------------------- """ -from cgi import FieldStorage from contextlib import suppress -from os import getenv, path -from random import choice -from subprocess import Popen -from tempfile import _get_candidate_names, gettempdir -from uuid import uuid4 -from OpenSSL import crypto from twisted.internet import reactor, ssl -from twisted.web.resource import Resource from twisted.web.server import Site +from honeypots.base_http_server import BaseHttpServer 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, + create_certificate, ) -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.process = None - 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 "" - ) - - 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().CN = next(_get_candidate_names()) - c.set_serial_number(0) - before, after = (0, 60 * 60 * 24 * 365 * 2) - c.gmtime_adj_notBefore(before) - 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)) - - def https_server_main(self): - _q_s = self - - class MainResource(Resource): - isLeaf = True - home_file = b""" - - - - - - - Login - - - -
-
-
- We'll back soon.. -
-
-
- -""" - - login_file = b""" - - - - - - Login - - - -
-
-
-
-
- -
-
- -
-
- -
-
-
-
-
- - -""" - - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - 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)}) - - 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 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, - } - ) - 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" - ) - return self.login_file - - request.responseHeaders.addRawHeader( - "Content-Type", "text/html; charset=utf-8" - ) - return self.login_file - - 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.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" - ) - return self.home_file - else: - request.responseHeaders.addRawHeader( - "Content-Type", "text/html; charset=utf-8" - ) - return self.home_file - - 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" - 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), - "--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": - return True - else: - self.kill_server() - return False - else: - self.https_server_main() - - def close_port(self): - ret = close_port_wrapper("https_server", self.ip, self.port, self.logs) - return ret +class QHTTPSServer(BaseHttpServer): + NAME = "https_server" + DEFAULT_PORT = 443 - def kill_server(self): - ret = kill_server_wrapper("https_server", self.uuid, self.process) - return ret + def server_main(self): + resource = self.MainResource(hp_server=self) + with create_certificate() as (cert, key): + ssl_context = ssl.DefaultOpenSSLContextFactory(key, cert) + reactor.listenSSL(self.port, Site(resource), ssl_context) + reactor.run() def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): diff --git a/honeypots/imap_server.py b/honeypots/imap_server.py index 2e122d7..623b605 100644 --- a/honeypots/imap_server.py +++ b/honeypots/imap_server.py @@ -10,69 +10,40 @@ // ------------------------------------------------------------- """ -from twisted.mail.imap4 import IMAP4Server -from twisted.internet.protocol import Factory -from twisted.internet import reactor +from contextlib import suppress from random import choice + from twisted import cred -from subprocess import Popen -from os import path, getenv +from twisted.internet import reactor +from twisted.internet.protocol import Factory +from twisted.mail.imap4 import ( + IMAP4Server, + IllegalClientResponse, + IllegalOperation, + IllegalMailboxEncoding, +) + +from honeypots.base_server import BaseServer 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, + check_bytes, ) -from uuid import uuid4 -from contextlib import suppress -class QIMAPServer: +class QIMAPServer(BaseServer): + NAME = "imap_server" + DEFAULT_PORT = 143 + def __init__(self, **kwargs): - self.auto_disabled = None + super().__init__(**kwargs) 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", "") - 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 "" - ) - def imap_server_main(self): + def server_main(self): _q_s = self class CustomIMAP4Server(IMAP4Server): - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def parse_command(self, line): args = line.split(None, 2) rest = None @@ -95,12 +66,12 @@ def parse_command(self, line): if "capture_commands" in _q_s.options: _q_s.logs.info( { - "server": "imap_server", + "server": _q_s.NAME, "action": "command", "data": { - "cmd": self.check_bytes(cmd), - "tag": self.check_bytes(tag), - "data": self.check_bytes(rest), + "cmd": check_bytes(cmd), + "tag": check_bytes(tag), + "data": check_bytes(rest), }, "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -121,7 +92,7 @@ def parse_command(self, line): def connectionMade(self): _q_s.logs.info( { - "server": "imap_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -132,8 +103,8 @@ def connectionMade(self): self.sendPositiveResponse(message=_q_s.mocking_server) def authenticateLogin(self, user, passwd): - username = self.check_bytes(user) - password = self.check_bytes(passwd) + username = check_bytes(user) + password = check_bytes(passwd) status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username @@ -141,7 +112,7 @@ def authenticateLogin(self, user, passwd): status = "success" _q_s.logs.info( { - "server": "imap_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.transport.getPeer().host, @@ -179,73 +150,6 @@ def buildProtocol(self, address): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": "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": - return True - else: - self.kill_server() - return False - else: - self.imap_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from imaplib import IMAP4 diff --git a/honeypots/ipp_server.py b/honeypots/ipp_server.py index 4a2c9a5..6161c9e 100644 --- a/honeypots/ipp_server.py +++ b/honeypots/ipp_server.py @@ -9,57 +9,31 @@ // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- """ +from __future__ import annotations from contextlib import suppress -from os import getenv, path from struct import unpack -from subprocess import Popen -from typing import Dict -from uuid import uuid4 from twisted.internet import reactor from twisted.web.resource import Resource from twisted.web.server import Site +from honeypots.base_server import BaseServer 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, + check_bytes, + get_headers_and_ip_from_request, ) STATUS_CODE_OK = b"\x00\x00" STATUS_CODE_BAD_REQUEST = b"\x04\x00" -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", "") - 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 "" - ) +class QIPPServer(BaseServer): + NAME = "ipp_server" + DEFAULT_PORT = 631 - def ipp_server_main(self): + def server_main(self): _q_s = self class MainResource(Resource): @@ -210,64 +184,6 @@ class MainResource(Resource): 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] @@ -280,74 +196,28 @@ def get_uint32_t(self, index, data): def get_string(self, index, length, data): return index + length, data[index : index + length] - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - 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() - 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)}) - - 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 client_ip == "": - client_ip = request.getClientAddress().host + client_ip, headers = get_headers_and_ip_from_request(request, _q_s.options) with suppress(Exception): + log_data = { + "server": _q_s.NAME, + "action": "connection", + "src_ip": client_ip, + "src_port": request.getClientAddress().port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } 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, - } - ) - 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, - } - ) + log_data["data"] = headers + _q_s.logs.info(log_data) data = request.content.read() response = "" version = [0, 0] - request_id = 0 - group = "" groups = [] groups_parsed = "" - operation = "" status = "success" with suppress(Exception): @@ -363,7 +233,6 @@ 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": index, attribute = self.get_uint32_t(index, data) @@ -375,11 +244,9 @@ def check_bytes(string): index, uint16_t = self.get_uint16_t(index, data) index, value = self.get_string(index, uint16_t, data) if attribute == b"": - groups[-1][1].append(self.check_bytes(value)) + groups[-1][1].append(check_bytes(value)) else: - groups.append( - [self.check_bytes(attribute), [self.check_bytes(value)]] - ) + groups.append([check_bytes(attribute), [check_bytes(value)]]) index, uint8_t = self.get_uint8_t(index, data) if uint8_t in self.attribute_group_tags: @@ -405,7 +272,7 @@ def check_bytes(string): if len(response) > 0: _q_s.logs.info( { - "server": "ipp_server", + "server": _q_s.NAME, "action": "query", "status": status, "src_ip": client_ip, @@ -426,93 +293,36 @@ def send_response(request: bytes, successful: bool) -> bytes: attributes = attributes_dict_to_bytes( {"attributes-charset": "utf-8", "attributes-natural-language": "en-us"} ) - response = version + status_code + request_id + attributes - return response + return version + status_code + request_id + attributes reactor.listenTCP(self.port, Site(MainResource())) reactor.run() - def run_server(self, process=False, auto=False): - 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": "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() - return False - else: - self.ipp_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None): - with suppress(): - from socket import socket, AF_INET, SOCK_STREAM + from socket import socket, AF_INET, SOCK_STREAM - _ip = ip or self.ip - _port = port or self.port + _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\x1b" + b"attributes-natural-language\x00\x02enE\x00\x0bprinter-uri\x00\x15" + b"ipp://127.0.0.1:631/D\x00\x14requested-attributes\x00\x03allD\x00\x00\x00\x12" + b"media-col-database\x03" + ) - headers = f"""\ - POST / HTTP/1.1\r - Content-Type: application/x-www-form-urlencoded\r - Content-Length: {len(body)}\r - Host: {_ip}:{_port}\r - Connection: close\r - \r\n""".encode() + headers = ( + "POST / HTTP/1.1\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: {len(body)}\r\n" + f"Host: {_ip}:{_port}\r\n" + "Connection: close\r\n" + "\r\n" + ) - s = socket(AF_INET, SOCK_STREAM) - s.connect((_ip, _port)) - s.sendall(headers + body) + s = socket(AF_INET, SOCK_STREAM) + s.connect((_ip, _port)) + s.sendall(headers.encode() + body) ATTRIBUTE_NAME_TO_VALUE_TAG = { @@ -521,7 +331,7 @@ def test_server(self, ip=None, port=None): } -def attributes_dict_to_bytes(attributes: Dict[str, str]) -> bytes: +def attributes_dict_to_bytes(attributes: dict[str, str]) -> bytes: attributes_str = b"\x01" # start operation attributes for key, value in attributes.items(): value_tag = ATTRIBUTE_NAME_TO_VALUE_TAG[key] diff --git a/honeypots/irc_server.py b/honeypots/irc_server.py index 9c86188..a8b9f42 100644 --- a/honeypots/irc_server.py +++ b/honeypots/irc_server.py @@ -10,59 +10,24 @@ // ------------------------------------------------------------- """ -from twisted.internet.protocol import Factory +from contextlib import suppress + from twisted.internet import reactor +from twisted.internet.protocol import Factory from twisted.words import service -from subprocess import Popen -from os import path, getenv + +from honeypots.base_server import BaseServer 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, + check_bytes, ) -from uuid import uuid4 -from contextlib import suppress -class QIRCServer: +class QIRCServer(BaseServer): NAME = "irc_server" + DEFAULT_PORT = 6667 - def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None - 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 "" - ) - self.logger = set_up_error_logging() - - def irc_server_main(self): + def server_main(self): _q_s = self class CustomIRCProtocol(service.IRCUser): @@ -134,67 +99,6 @@ def irc_NICK(self, prefix, params): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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": 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() - return False - else: - self.irc_server_main() - - def close_port(self): - ret = close_port_wrapper(self.NAME, self.ip, self.port, self.logs) - return ret - - def kill_server(self): - 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 @@ -213,13 +117,6 @@ def test_server(self, ip=None, port=None, username=None, password=None): c.close() -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: diff --git a/honeypots/ldap_server.py b/honeypots/ldap_server.py index e625750..4a07c36 100644 --- a/honeypots/ldap_server.py +++ b/honeypots/ldap_server.py @@ -9,73 +9,37 @@ // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- """ +from __future__ import annotations -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor -from subprocess import Popen -from os import path, getenv -from struct import unpack from binascii import unhexlify +from contextlib import suppress +from struct import unpack + +from twisted.internet import reactor +from twisted.internet.protocol import Protocol, Factory + +from honeypots.base_server import BaseServer 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, + check_bytes, ) -from uuid import uuid4 -from contextlib import suppress -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", "") - 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 "" - ) +class QLDAPServer(BaseServer): + NAME = "ldap_server" + DEFAULT_PORT = 389 - def ldap_server_main(self): + def server_main(self): _q_s = self class CustomLDAProtocol(Protocol): _state = None - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def connectionMade(self): self._state = 1 _q_s.logs.info( { - "server": "ldap_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -84,16 +48,13 @@ def connectionMade(self): } ) - def parse_ldap_packet(self, data): + @staticmethod + def parse_ldap_packet(data: bytes) -> tuple[str, str]: # V # 30[20] 0201[02] 60[1b] 0201[03] 04[0a] 7379736261636b757031 [80][0a] 7379736261636b757032 username = "" password = "" - username_start = 0 - username_end = 0 - password_start = 0 - password_end = 0 with suppress(Exception): version = data.find(b"\x02\x01\x03") if version > 0: @@ -120,82 +81,41 @@ def parse_ldap_packet(self, data): ) password = data[password_start:password_end] - return username, password + return check_bytes(username), check_bytes(password) def dataReceived(self, data): if self._state == 1: self._state = 2 - username, password = self.parse_ldap_packet(data) - username = self.check_bytes(username) - password = self.check_bytes(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, - } - ) - 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._check_login(data) 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 == _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, - } - ) - 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._check_login(data) self.transport.write(unhexlify(b"300c02010265070a013204000400")) else: self.transport.loseConnection() + def _check_login(self, data): + username, password = self.parse_ldap_packet(data) + if username != "" or password != "": + if username == _q_s.username and password == _q_s.password: + status = "success" + else: + status = "failed" + _q_s.logs.info( + { + "server": _q_s.NAME, + "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 connectionLost(self, reason): self._state = None @@ -204,73 +124,6 @@ def connectionLost(self, reason): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": "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": - return True - else: - self.kill_server() - return False - else: - self.ldap_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from ldap3 import Server, Connection, ALL diff --git a/honeypots/memcache_server.py b/honeypots/memcache_server.py index 8aac179..c2f774c 100644 --- a/honeypots/memcache_server.py +++ b/honeypots/memcache_server.py @@ -11,51 +11,23 @@ """ from contextlib import suppress -from os import getenv, path from random import randint, uniform -from subprocess import Popen from time import time -from uuid import uuid4 from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol +from honeypots.base_server import BaseServer 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: - def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None - 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 "" - ) +class QMemcacheServer(BaseServer): + NAME = "memcache_server" + DEFAULT_PORT = 11211 - def memcache_server_main(self): + def server_main(self): _q_s = self class CustomRedisProtocol(Protocol): @@ -166,7 +138,7 @@ def get_key(self, key): def connectionMade(self): _q_s.logs.info( { - "server": "memcache_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -192,7 +164,7 @@ def dataReceived(self, data): if _data[0] != b"": _q_s.logs.info( { - "server": "memcache_server", + "server": _q_s.NAME, "action": _data[0].decode(), "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -207,67 +179,6 @@ def dataReceived(self, data): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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": "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": - return True - else: - self.kill_server() - return False - else: - self.memcache_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from warnings import filterwarnings diff --git a/honeypots/mssql_server.py b/honeypots/mssql_server.py index 8b0e0e9..8564a4b 100644 --- a/honeypots/mssql_server.py +++ b/honeypots/mssql_server.py @@ -12,68 +12,32 @@ from binascii import hexlify, unhexlify from contextlib import suppress -from os import getenv, path from struct import pack, unpack -from subprocess import Popen -from uuid import uuid4 from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol +from honeypots.base_server import BaseServer 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, + check_bytes, ) -class QMSSQLServer: +class QMSSQLServer(BaseServer): + NAME = "mssql_server" + DEFAULT_PORT = 1433 + def __init__(self, **kwargs): - self.auto_disabled = None + super().__init__(**kwargs) self.file_name = None - self.process = None - 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 "" - ) - def mssql_server_main(self): + def server_main(self): _q_s = self class CustomMSSQLProtocol(Protocol): _state = None - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def create_payload(self, server_name=b"", token_error_msg=b"", error_code=2): ret = "040100c000350100aaa80002000000010e440041006e0020006500720072006f007200200068006100730020006f00630063007500720072006500640020007700680069006c0065002000650073007400610062006c0069007300680069006e00670020006100200063006f006e006e0065006300740069006f006e00200074006f00200074006800650020007300650072007600650072002e00095200260044006200610063006b00750070000001000000fd020000000000000000000000" with suppress(Exception): @@ -117,7 +81,7 @@ def connectionMade(self): self._state = 1 _q_s.logs.info( { - "server": "mssql_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -152,8 +116,8 @@ def dataReceived(self, data): password_decrypted += chr( ((x ^ 0xA5) & 0x0F) << 4 | ((x ^ 0xA5) & 0xF0) >> 4 ) - username = self.check_bytes(username) - password = self.check_bytes(password_decrypted) + username = check_bytes(username) + password = check_bytes(password_decrypted) status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username @@ -161,7 +125,7 @@ def dataReceived(self, data): status = "success" _q_s.logs.info( { - "server": "mssql_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.transport.getPeer().host, @@ -191,73 +155,6 @@ def connectionLost(self, reason): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": "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": - return True - else: - self.kill_server() - return False - else: - self.mssql_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from pymssql import connect as pconnect diff --git a/honeypots/mysql_server.py b/honeypots/mysql_server.py index 8742d71..006b081 100644 --- a/honeypots/mysql_server.py +++ b/honeypots/mysql_server.py @@ -12,54 +12,24 @@ from contextlib import suppress from hashlib import sha1 -from os import getenv, path from struct import pack -from subprocess import Popen -from uuid import uuid4 from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol +from honeypots.base_server import BaseServer 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, + check_bytes, ) -class QMysqlServer: +class QMysqlServer(BaseServer): + NAME = "mysql_server" + DEFAULT_PORT = 3306 + def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None - 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 "" - ) + super().__init__(**kwargs) self.words = [self.password.encode()] def load_words( @@ -150,26 +120,18 @@ def decode(self, hash): return temp return None - def mysql_server_main(self): + def server_main(self): _q_s = self class CustomMysqlProtocol(Protocol): _state = None - def check_bytes(self, string): - with suppress(Exception): - if isinstance(string, bytes): - return string.decode("utf-8", "ignore") - else: - return str(string) - return string - def connectionMade(self): self._state = 1 self.transport.write(_q_s.greeting()) _q_s.logs.info( { - "server": "mysql_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -183,13 +145,13 @@ def dataReceived(self, data): if self._state == 1: ret_access_denied = False username, password, good = _q_s.parse_data(data) - username = self.check_bytes(username) + username = check_bytes(username) status = "failed" if good: if password: password_decoded = _q_s.decode(password) if password_decoded is not None and username == _q_s.username: - password = self.check_bytes(password_decoded) + password = check_bytes(password_decoded) status = "success" else: password = password.hex() @@ -199,7 +161,7 @@ def dataReceived(self, data): password = ":".join(hex(c)[2:] for c in data) _q_s.logs.info( { - "server": "mysql_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.transport.getPeer().host, @@ -229,73 +191,6 @@ def connectionLost(self, reason): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": "mysql_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() - return False - else: - self.mysql_server_main() - - def close_port(self): - ret = close_port_wrapper("mysql_server", self.ip, self.port, self.logs) - return ret - - def kill_server(self): - ret = kill_server_wrapper("mysql_server", self.uuid, self.process) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from mysql.connector import connect as mysqlconnect diff --git a/honeypots/ntp_server.py b/honeypots/ntp_server.py index d6894d1..0c84352 100644 --- a/honeypots/ntp_server.py +++ b/honeypots/ntp_server.py @@ -11,57 +11,23 @@ """ import struct from contextlib import suppress -from os import getenv, path from struct import pack, unpack -from subprocess import Popen from time import time -from uuid import uuid4 from twisted.internet import reactor from twisted.internet.protocol import DatagramProtocol +from honeypots.base_server import BaseServer 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 QNTPServer: - def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None - 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 123 - ) - 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 "" - ) +class QNTPServer(BaseServer): + NAME = "ntp_server" + DEFAULT_PORT = 123 - def ntp_server_main(self): + def server_main(self): _q_s = self class CustomDatagramProtocolProtocol(DatagramProtocol): @@ -80,7 +46,7 @@ def datagramReceived(self, data, addr): mode = "UnKnown" _q_s.logs.info( { - "server": "ntp_server", + "server": _q_s.NAME, "action": "connection", "src_ip": addr[0], "src_port": addr[1], @@ -111,7 +77,7 @@ def datagramReceived(self, data, addr): _q_s.logs.info( { - "server": "ntp_server", + "server": _q_s.NAME, "action": "query", "status": status, "src_ip": addr[0], @@ -127,67 +93,6 @@ def datagramReceived(self, data, addr): ) reactor.run() - def run_server(self, process=False, auto=False): - 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": "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() - return False - else: - self.ntp_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from warnings import filterwarnings diff --git a/honeypots/oracle_server.py b/honeypots/oracle_server.py index 803cbfa..ed8fff5 100644 --- a/honeypots/oracle_server.py +++ b/honeypots/oracle_server.py @@ -11,57 +11,23 @@ """ from contextlib import suppress -from os import getenv, path from re import findall from struct import unpack -from subprocess import Popen -from uuid import uuid4 from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol +from honeypots.base_server import BaseServer 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: - def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None - 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 "" - ) +class QOracleServer(BaseServer): + NAME = "oracle_server" + DEFAULT_PORT = 1521 - def oracle_server_main(self): + def server_main(self): _q_s = self class CustomRedisProtocol(Protocol): @@ -106,7 +72,7 @@ def parse_payload(self, data): def connectionMade(self): _q_s.logs.info( { - "server": "oracle_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -120,7 +86,7 @@ def dataReceived(self, data): if service_name or program or local_user: _q_s.logs.info( { - "server": "oracle_server", + "server": _q_s.NAME, "action": "login", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -141,67 +107,6 @@ def dataReceived(self, data): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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": "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() - return False - else: - self.oracle_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from warnings import filterwarnings diff --git a/honeypots/pjl_server.py b/honeypots/pjl_server.py index c7dd6bd..ab9b27c 100644 --- a/honeypots/pjl_server.py +++ b/honeypots/pjl_server.py @@ -11,48 +11,24 @@ """ from contextlib import suppress -from os import getenv, path -from subprocess import Popen -from uuid import uuid4 from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol +from honeypots.base_server import BaseServer 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, + check_bytes, ) -class QPJLServer: +class QPJLServer(BaseServer): + NAME = "pjl_server" + DEFAULT_PORT = 9100 + def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None + super().__init__(**kwargs) 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", @@ -72,23 +48,17 @@ def __init__(self, **kwargs): "HWAddress": "0025B395EA01", } - def pjl_server_main(self): + def server_main(self): _q_s = self class Custompjlrotocol(Protocol): _state = None - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode(errors="replace") - else: - return str(string) - def connectionMade(self): self._state = 1 _q_s.logs.info( { - "server": "pjl_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -109,14 +79,14 @@ def dataReceived(self, data): self.transport.write(prodinfo.encode("utf-8") + b"\x1b") _q_s.logs.info( { - "server": "pjl_server", + "server": _q_s.NAME, "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": {"command": check_bytes(data)}, } ) self.transport.loseConnection() @@ -129,67 +99,6 @@ def connectionLost(self, reason): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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": "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() - return False - else: - self.pjl_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from warnings import filterwarnings diff --git a/honeypots/pop3_server.py b/honeypots/pop3_server.py index 09ffaee..380a42b 100644 --- a/honeypots/pop3_server.py +++ b/honeypots/pop3_server.py @@ -10,74 +10,38 @@ // ------------------------------------------------------------- """ from contextlib import suppress -from os import getenv, path from random import choice -from subprocess import Popen from typing import Tuple -from uuid import uuid4 from twisted.internet import reactor from twisted.internet.protocol import Factory from twisted.mail.pop3 import POP3, POP3Error +from honeypots.base_server import BaseServer 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, + check_bytes, ) -class QPOP3Server: +class QPOP3Server(BaseServer): + NAME = "pop3_server" + DEFAULT_PORT = 110 + def __init__(self, **kwargs): - self.auto_disabled = None + super().__init__(**kwargs) 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", "") - 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 "" - ) - def pop3_server_main(self): + def server_main(self): _q_s = self class CustomPOP3Protocol(POP3): self._user = None - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def connectionMade(self): _q_s.logs.info( { - "server": "pop3_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -93,11 +57,11 @@ def processCommand(self, command: bytes, *args): if "capture_commands" in _q_s.options: _q_s.logs.info( { - "server": "pop3_server", + "server": _q_s.NAME, "action": "command", "data": { - "cmd": self.check_bytes(command), - "args": self.check_bytes(b" ".join(args)), + "cmd": check_bytes(command), + "args": check_bytes(b" ".join(args)), }, "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -120,7 +84,7 @@ def processCommand(self, command: bytes, *args): 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, f"do_{self.check_bytes(command)}", None) + f = getattr(self, f"do_{check_bytes(command)}", None) if f: return f(*args) raise POP3Error(b"Unknown protocol command: " + command) @@ -131,8 +95,8 @@ def do_USER(self, user): 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)) + username = check_bytes(self._user) + password = check_bytes(b" ".join((password,) + words)) status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username @@ -140,7 +104,7 @@ def do_PASS(self, password: bytes, *words: Tuple[bytes]): status = "success" _q_s.logs.info( { - "server": "pop3_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.transport.getPeer().host, @@ -171,73 +135,6 @@ def buildProtocol(self, address): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": "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": - return True - else: - self.kill_server() - return False - else: - self.pop3_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from poplib import POP3 as poplibPOP3 diff --git a/honeypots/postgres_server.py b/honeypots/postgres_server.py index d0dd44c..2b84d25 100644 --- a/honeypots/postgres_server.py +++ b/honeypots/postgres_server.py @@ -11,68 +11,29 @@ """ from contextlib import suppress -from os import getenv, path from struct import unpack -from subprocess import Popen -from uuid import uuid4 from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol +from honeypots.base_server import BaseServer 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, + check_bytes, ) -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", "") - 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 "" - ) +class QPostgresServer(BaseServer): + NAME = "postgres_server" + DEFAULT_PORT = 5432 - def postgres_server_main(self): + def server_main(self): _q_s = self class CustomPostgresProtocol(Protocol): _state = None _variables = {} - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def read_data_custom(self, data): _data = data.decode("utf-8") length = unpack("!I", data[0:4]) @@ -88,7 +49,7 @@ def connectionMade(self): self._variables = {} _q_s.logs.info( { - "server": "postgres_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -108,8 +69,8 @@ def dataReceived(self, data): elif self._state == 3: 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"]) + username = check_bytes(self._variables["user"]) + password = check_bytes(self._variables["password"]) status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username @@ -117,7 +78,7 @@ def dataReceived(self, data): status = "success" _q_s.logs.info( { - "server": "postgres_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.transport.getPeer().host, @@ -142,73 +103,6 @@ def connectionLost(self, reason): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": - return True - else: - self.kill_server() - return False - else: - self.postgres_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from psycopg2 import connect diff --git a/honeypots/rdp_server.py b/honeypots/rdp_server.py index 31a5bb2..95920ad 100644 --- a/honeypots/rdp_server.py +++ b/honeypots/rdp_server.py @@ -10,82 +10,30 @@ // ------------------------------------------------------------- """ +from contextlib import suppress 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 struct import unpack +from threading import Thread + +from honeypots.base_server import BaseServer 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, + create_certificate, + check_bytes, ) -from uuid import uuid4 -from contextlib import suppress -from threading import Thread -from struct import unpack -from OpenSSL import crypto -from tempfile import gettempdir, _get_candidate_names -class QRDPServer: +class QRDPServer(BaseServer): + NAME = "rdp_server" + DEFAULT_PORT = 3389 + 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", "") - 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 "" - ) + super().__init__(**kwargs) - def rdp_server_main(self): + def server_main(self): _q_s = self - 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().CN = next(_get_candidate_names()) - c.set_serial_number(0) - before, after = (0, 60 * 60 * 24 * 365 * 2) - c.gmtime_adj_notBefore(before) - 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)) - class ConnectionHandle(Thread): def __init__(self, sock, key, cert): super(ConnectionHandle, self).__init__() @@ -93,12 +41,6 @@ def __init__(self, sock, key, cert): self.key = key self.cert = cert - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def get_value(self, length, data): with suppress(Exception): var = b"" @@ -107,25 +49,15 @@ def get_value(self, length, data): break if _ == 0: continue - else: - var += bytes([_]) + var += bytes([_]) if length / 2 == len(var): return var return b"" - def extract_cookie(self, data): - cookie = b"" - with suppress(Exception): - for idx, _ in enumerate(data): - if _ == 13 and data[idx + 1] == 10: - break - else: - cookie += bytes([_]) - return cookie + def extract_cookie(self, data: bytes) -> bytes: + return data[: data.find(b"\r\n")] - def extract_creds(self, data): - user = "" - password = "" + def extract_creds(self, data: bytes): with suppress(Exception): ( flag, @@ -160,7 +92,7 @@ def run(self): with suppress(Exception): _q_s.logs.info( { - "server": "rdp_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.sock.getpeername()[0], "src_port": self.sock.getpeername()[1], @@ -172,11 +104,10 @@ def run(self): data = self.sock.recv(1024) if b"Cookie" in data: - cookie = self.extract_cookie(data[11:]) - cookie = self.check_bytes(cookie) + cookie = self.extract_cookie(data[11:]).decode(errors="replace") _q_s.logs.info( { - "server": "rdp_server", + "server": _q_s.NAME, "action": "stshash", "mstshash": "success", "src_ip": self.sock.getpeername()[0], @@ -257,19 +188,15 @@ def run(self): data = self.sock.recv(1024) if len(data) > 14: if data[15] == 64: - username = "" - password = "" status = "failed" username, password = self.extract_creds(data) - username = self.check_bytes(username) - password = self.check_bytes(password) + username = check_bytes(username) + password = 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", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.sock.getpeername()[0], @@ -299,84 +226,17 @@ def run(self): with suppress(Exception): self.sock.close() - 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)) rpdserver.listen() - while True: - with suppress(Exception): - client, addr = rpdserver.accept() - client.settimeout(10.0) - ConnectionHandle(client, self.key, self.cert).start() - - def run_server(self, process=False, auto=False): - 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), - "--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": - return True - else: - self.kill_server() - return False - else: - self.rdp_server_main() - - def close_port(self): - 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) - return ret + with create_certificate() as (cert, key): + while True: + with suppress(Exception): + client, addr = rpdserver.accept() + client.settimeout(10.0) + ConnectionHandle(client, key, cert).start() def test_server(self, ip=None, port=None): with suppress(Exception): diff --git a/honeypots/redis_server.py b/honeypots/redis_server.py index c691ea3..ea39566 100644 --- a/honeypots/redis_server.py +++ b/honeypots/redis_server.py @@ -11,64 +11,25 @@ """ from contextlib import suppress -from os import getenv, path -from subprocess import Popen -from uuid import uuid4 from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol +from honeypots.base_server import BaseServer 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, + check_bytes, ) -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", "") - 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 "" - ) +class QRedisServer(BaseServer): + NAME = "redis_server" + DEFAULT_PORT = 6379 - def redis_server_main(self): + def server_main(self): _q_s = self class CustomRedisProtocol(Protocol): - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def get_command(self, data): with suppress(Exception): _data = data.decode("utf-8").split("\x0d\x0a") @@ -97,8 +58,8 @@ def parse_data(self, c, data): 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) + username = check_bytes(username) + password = check_bytes(password) status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username @@ -106,7 +67,7 @@ def parse_data(self, c, data): status = "success" _q_s.logs.info( { - "server": "redis_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.transport.getPeer().host, @@ -123,7 +84,7 @@ def connectionMade(self): self._variables = {} _q_s.logs.info( { - "server": "redis_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -146,73 +107,6 @@ def dataReceived(self, data): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": - return True - else: - self.kill_server() - return False - else: - self.redis_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from redis import StrictRedis diff --git a/honeypots/sip_server.py b/honeypots/sip_server.py index b0a345a..cbdf9de 100644 --- a/honeypots/sip_server.py +++ b/honeypots/sip_server.py @@ -11,55 +11,22 @@ """ from contextlib import suppress -from os import getenv, path -from subprocess import Popen -from uuid import uuid4 from twisted.internet import reactor from twisted.protocols.sip import Base +from honeypots.base_server import BaseServer 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, + check_bytes, ) -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", "") - 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 "" - ) +class QSIPServer(BaseServer): + NAME = "sip_server" + DEFAULT_PORT = 5060 - def sip_server_main(self): + def server_main(self): _q_s = self class CustomSIPServer(Base): @@ -68,25 +35,19 @@ def handle_request(self, message, addr): _q_s.logs.info( { - "server": "sip_server", + "server": _q_s.NAME, "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", + "server": _q_s.NAME, "action": "request", "src_ip": addr[0], "src_port": addr[1], @@ -100,67 +61,6 @@ def check_bytes(string): reactor.listenUDP(port=self.port, protocol=CustomSIPServer(), interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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": "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() - return False - else: - self.sip_server_main() - - def close_port(self): - 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) - 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 diff --git a/honeypots/smb_server.py b/honeypots/smb_server.py index e56ab86..92177ec 100644 --- a/honeypots/smb_server.py +++ b/honeypots/smb_server.py @@ -12,102 +12,72 @@ from contextlib import suppress from logging import DEBUG, getLogger, StreamHandler -from os import getenv, path +from os import path from random import randint from shutil import rmtree -from subprocess import Popen from tempfile import mkdtemp from threading import current_thread from time import sleep -from uuid import uuid4 from impacket import smbserver from impacket.ntlm import compute_lmhash, compute_nthash from six.moves import socketserver +from honeypots.base_server import BaseServer 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(BaseServer): + NAME = "smb_server" + DEFAULT_PORT = 445 + def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None - self.uuid = "honeypotslogger" + "_" + __class__.__name__ + "_" + str(uuid4())[:8] + super().__init__(**kwargs) 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 "" - ) - def smb_server_main(self): + def server_main(self): _q_s = self 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() - ): + 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": _q_s.NAME, + "action": "connection", + "data": message.strip(), + "src_ip": ip, + "src_port": port, + "dest_ip": _q_s.ip, + "dest_port": _q_s.port, + } + ) + elif ":aaaaaaaaaaaaaaaa:" in message.strip(): + parsed = message.strip().split(":") + if len(parsed) == 6: + username, _, _, _, nt_res_1, nt_res_2 = parsed _q_s.logs.info( { - "server": "smb_server", - "action": "connection", - "data": message.strip(), + "server": _q_s.NAME, + "action": "login", + "username": username, "src_ip": ip, "src_port": port, "dest_ip": _q_s.ip, "dest_port": _q_s.port, + "data": {"nt_data_1": nt_res_1, "nt_data_2": nt_res_2}, } ) - 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, - } - ) class SMBSERVERHandler(smbserver.SMBSERVERHandler): def __init__(self, request, client_address, server, select_poll=False): @@ -166,73 +136,6 @@ def start(self): server.start() rmtree(dirpath) - def run_server(self, process=False, auto=False): - 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), - "--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": "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": - return True - else: - self.kill_server() - return False - else: - self.smb_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from impacket.smbconnection import SMBConnection diff --git a/honeypots/smtp_server.py b/honeypots/smtp_server.py index d04b00a..c92da1b 100644 --- a/honeypots/smtp_server.py +++ b/honeypots/smtp_server.py @@ -10,55 +10,22 @@ // ------------------------------------------------------------- """ -from smtpd import SMTPChannel, SMTPServer from asyncore import loop from base64 import b64decode -from os import path, getenv -from subprocess import Popen +from contextlib import suppress +from smtpd import SMTPChannel, SMTPServer + +from honeypots.base_server import BaseServer 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: - def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None - 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 "" - ) +class QSMTPServer(BaseServer): + NAME = "smtp_server" + DEFAULT_PORT = 25 - def smtp_server_main(self): + def server_main(self): _q_s = self class CustomSMTPChannel(SMTPChannel): @@ -78,7 +45,7 @@ def found_terminator(self): if command != "HELO" and command != "EHLO": _q_s.logs.info( { - "server": "smtp_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.addr[0], "src_port": self.addr[1], @@ -92,7 +59,7 @@ def found_terminator(self): def smtp_EHLO(self, arg): _q_s.logs.info( { - "server": "smtp_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.addr[0], "src_port": self.addr[1], @@ -124,7 +91,7 @@ def smtp_AUTH(self, arg): status = "success" _q_s.logs.info( { - "server": "smtp_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.addr[0], @@ -151,7 +118,7 @@ def handle_accept(self): conn, addr = self.accept() _q_s.logs.info( { - "server": "smtp_server", + "server": _q_s.NAME, "action": "connection", "src_ip": addr[0], "src_port": addr[1], @@ -164,73 +131,6 @@ def handle_accept(self): CustomSMTPServer((self.ip, self.port), None) loop(timeout=1.1, use_poll=True) - def run_server(self, process=False, auto=False): - 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), - "--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": "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": - return True - else: - self.kill_server() - return False - else: - self.smtp_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from smtplib import SMTP diff --git a/honeypots/snmp_server.py b/honeypots/snmp_server.py index aaee93d..5d72b3b 100644 --- a/honeypots/snmp_server.py +++ b/honeypots/snmp_server.py @@ -9,56 +9,27 @@ // contributors list qeeqbox/honeypots/graphs/contributors // ------------------------------------------------------------- """ +from contextlib import suppress from warnings import filterwarnings + from cryptography.utils import CryptographyDeprecationWarning filterwarnings(action="ignore", category=CryptographyDeprecationWarning) - -from contextlib import suppress -from os import getenv, path -from subprocess import Popen -from uuid import uuid4 - from scapy.all import SNMP from twisted.internet import reactor from twisted.internet.protocol import DatagramProtocol +from honeypots.base_server import BaseServer 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: - def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None - 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 "" - ) +class QSNMPServer(BaseServer): + NAME = "snmp_server" + DEFAULT_PORT = 161 - def snmp_server_main(self): + def server_main(self): _q_s = self class CustomDatagramProtocolProtocol(DatagramProtocol): @@ -76,7 +47,7 @@ def parse_snmp(self, data): def datagramReceived(self, data, addr): _q_s.logs.info( { - "server": "snmp_server", + "server": _q_s.NAME, "action": "connection", "status": "fail", "src_ip": addr[0], @@ -89,7 +60,7 @@ def datagramReceived(self, data, addr): if version or community or oids: _q_s.logs.info( { - "server": "snmp_server", + "server": _q_s.NAME, "action": "query", "status": "success", "src_ip": addr[0], @@ -106,67 +77,6 @@ def datagramReceived(self, data, addr): ) reactor.run() - def run_server(self, process=False, auto=False): - 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": "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() - return False - else: - self.snmp_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from pysnmp.hlapi import ( diff --git a/honeypots/socks5_server.py b/honeypots/socks5_server.py index fa0a9ff..ca026e1 100644 --- a/honeypots/socks5_server.py +++ b/honeypots/socks5_server.py @@ -10,68 +10,25 @@ // ------------------------------------------------------------- """ import struct +from contextlib import suppress from socketserver import TCPServer, StreamRequestHandler, ThreadingMixIn from struct import unpack -from os import path, getenv -from subprocess import Popen -from honeypots import set_up_error_logging +from honeypots.base_server import BaseServer 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, + check_bytes, ) -from uuid import uuid4 -from contextlib import suppress -class QSOCKS5Server: +class QSOCKS5Server(BaseServer): NAME = "socks5_server" + DEFAULT_PORT = 1080 - def __init__(self, **kwargs): - self.auto_disabled = None - self.process = None - 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.logger = set_up_error_logging() - - def socks5_server_main(self): + def server_main(self): _q_s = self class CustomStreamRequestHandler(StreamRequestHandler): - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def handle(self): src_ip, src_port = self.client_address _q_s.logs.info( @@ -91,11 +48,9 @@ def handle(self): 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) + username = check_bytes(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) + password = check_bytes(self.connection.recv(_len)) status = "failed" if username == _q_s.username and password == _q_s.password: status = "success" @@ -128,73 +83,6 @@ class ThreadingTCPServer(ThreadingMixIn, TCPServer): server = ThreadingTCPServer((self.ip, self.port), CustomStreamRequestHandler) server.serve_forever() - def run_server(self, process=False, auto=False): - 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), - "--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": 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() - return False - else: - self.socks5_server_main() - - def close_port(self): - ret = close_port_wrapper(self.NAME, self.ip, self.port, self.logs) - return ret - - def kill_server(self): - 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 diff --git a/honeypots/ssh_server.py b/honeypots/ssh_server.py index 6acbd0f..198aa2a 100644 --- a/honeypots/ssh_server.py +++ b/honeypots/ssh_server.py @@ -10,74 +10,43 @@ // ------------------------------------------------------------- """ +from _thread import start_new_thread +from binascii import hexlify +from contextlib import suppress +from io import StringIO +from random import choice +from re import compile as rcompile +from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR +from threading import Event +from time import time + 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 paramiko.ssh_exception import SSHException +from honeypots.base_server import BaseServer 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, + check_bytes, ) -from uuid import uuid4 -from contextlib import suppress -from re import compile as rcompile -from time import time -from threading import Event -from binascii import hexlify -class QSSHServer: +class QSSHServer(BaseServer): + NAME = "ssh_server" + DEFAULT_PORT = 22 + def __init__(self, **kwargs): - self.auto_disabled = None + super().__init__(**kwargs) 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", "") - 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-?]*[ -/]*[@-~]") def generate_pub_pri_keys(self): @@ -88,7 +57,7 @@ def generate_pub_pri_keys(self): return key.get_base64(), string_io.getvalue() return None, None - def ssh_server_main(self): + def server_main(self): _q_s = self class SSHHandle(ServerInterface): @@ -96,13 +65,6 @@ def __init__(self, ip, port): self.ip = ip self.port = port self.event = Event() - # ServerInterface.__init__(self) - - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) def check_channel_request(self, kind, chanid): if kind == "session": @@ -110,8 +72,8 @@ def check_channel_request(self, kind, chanid): return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def check_auth_password(self, username, password): - username = self.check_bytes(username) - password = self.check_bytes(password) + username = check_bytes(username) + password = check_bytes(password) status = "failed" if username == _q_s.username and password == _q_s.password: username = _q_s.username @@ -120,7 +82,7 @@ def check_auth_password(self, username, password): if status == "success": _q_s.logs.info( { - "server": "ssh_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.ip, @@ -134,7 +96,7 @@ def check_auth_password(self, username, password): return AUTH_SUCCESSFUL _q_s.logs.info( { - "server": "ssh_server", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.ip, @@ -151,13 +113,13 @@ def check_channel_exec_request(self, channel, command): if "capture_commands" in _q_s.options: _q_s.logs.info( { - "server": "ssh_server", + "server": _q_s.NAME, "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)}, + "data": {"command": check_bytes(command)}, } ) self.event.set() @@ -169,14 +131,14 @@ def get_allowed_auths(self, username): def check_auth_publickey(self, username, key): _q_s.logs.info( { - "server": "ssh_server", + "server": _q_s.NAME, "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())), + "username": check_bytes(username), + "key_fingerprint": check_bytes(hexlify(key.get_fingerprint())), } ) return AUTH_SUCCESSFUL @@ -198,7 +160,7 @@ def ConnectionHandle(client, priv): ip, port = client.getpeername() _q_s.logs.info( { - "server": "ssh_server", + "server": _q_s.NAME, "action": "connection", "src_ip": ip, "src_port": port, @@ -216,11 +178,11 @@ def ConnectionHandle(client, priv): 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" + b"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("/$ ") + conn.send(b"/$ ") line = "" while ( not line.endswith("\x0d") @@ -231,12 +193,12 @@ def ConnectionHandle(client, priv): recv = conn.recv(1).decode() conn.settimeout(None) if _q_s.ansi.match(recv) is None and recv != "\x7f": - conn.send(recv) + conn.send(recv.encode()) line += recv line = line.rstrip() _q_s.logs.info( { - "server": "ssh_server", + "server": _q_s.NAME, "action": "interactive", "src_ip": ip, "src_port": port, @@ -247,16 +209,18 @@ def ConnectionHandle(client, priv): ) 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" + b"\r\nbin cdrom etc lib lib64 lost+found mnt proc run snap " + b"swapfile tmp var boot dev home lib32 libx32 media opt root " + b"sbin srv sys usr\r\n" ) elif line == "pwd": - conn.send("\r\n/\r\n") + conn.send(b"\r\n/\r\n") elif line == "whoami": - conn.send("\r\nroot\r\n") + conn.send(b"\r\nroot\r\n") elif line == "exit": break else: - conn.send(f"\r\n{line}: command not found\r\n") + conn.send(f"\r\n{line}: command not found\r\n".encode()) with suppress(Exception): sshhandle.event.wait(2) with suppress(Exception): @@ -280,73 +244,6 @@ def ConnectionHandle(client, priv): ), ) - def run_server(self, process=False, auto=False): - 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), - "--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": "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": - return True - else: - self.kill_server() - return False - else: - self.ssh_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): from paramiko import SSHClient, AutoAddPolicy diff --git a/honeypots/telnet_server.py b/honeypots/telnet_server.py index ae35e12..ac46d1b 100644 --- a/honeypots/telnet_server.py +++ b/honeypots/telnet_server.py @@ -10,60 +10,32 @@ // ------------------------------------------------------------- """ +from contextlib import suppress + from twisted.conch.telnet import TelnetProtocol, TelnetTransport -from twisted.internet.protocol import Factory from twisted.internet import reactor -from subprocess import Popen -from os import path, getenv +from twisted.internet.protocol import Factory + +from honeypots.base_server import BaseServer 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, + check_bytes, ) -from uuid import uuid4 -from contextlib import suppress -class QTelnetServer: +class QTelnetServer(BaseServer): + NAME = "telnet_server" + DEFAULT_PORT = 23 + def __init__(self, **kwargs): - self.auto_disabled = None + super().__init__(**kwargs) 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", "") - 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 "" - ) - def telent_server_main(self): + def server_main(self): _q_s = self class CustomTelnetProtocol(TelnetProtocol): @@ -71,12 +43,6 @@ class CustomTelnetProtocol(TelnetProtocol): _user = None _pass = None - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def connectionMade(self): self._state = None self._user = None @@ -85,7 +51,7 @@ def connectionMade(self): self._state = b"Username" _q_s.logs.info( { - "server": "telnet_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -101,17 +67,15 @@ def dataReceived(self, data): 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) + username = check_bytes(self._user) + password = check_bytes(data) 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", + "server": _q_s.NAME, "action": "login", "status": status, "src_ip": self.transport.getPeer().host, @@ -136,73 +100,6 @@ def connectionLost(self, reason): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": - return True - else: - self.kill_server() - return False - else: - self.telent_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): from telnetlib import Telnet as TTelnet diff --git a/honeypots/vnc_server.py b/honeypots/vnc_server.py index ad7d99b..7a80b99 100644 --- a/honeypots/vnc_server.py +++ b/honeypots/vnc_server.py @@ -12,56 +12,26 @@ from binascii import unhexlify from contextlib import suppress -from os import getenv, path -from subprocess import Popen -from uuid import uuid4 from Crypto.Cipher import DES from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol +from honeypots.base_server import BaseServer 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, + check_bytes, ) -class QVNCServer: +class QVNCServer(BaseServer): + NAME = "vnc_server" + DEFAULT_PORT = 5900 + def __init__(self, **kwargs): - self.auto_disabled = None + super().__init__(**kwargs) self.challenge = unhexlify("00000000901234567890123456789012") self.words = ["test"] - self.process = None - 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 "" - ) def load_words( self, @@ -82,24 +52,18 @@ def decode(self, c, r): return temp return None - def vnc_server_main(self): + def server_main(self): _q_s = self class CustomVNCProtocol(Protocol): _state = None - def check_bytes(self, string): - if isinstance(string, bytes): - return string.decode() - else: - return str(string) - def connectionMade(self): self.transport.write(b"RFB 003.008\n") self._state = 1 _q_s.logs.info( { - "server": "vnc_server", + "server": _q_s.NAME, "action": "connection", "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, @@ -119,21 +83,19 @@ def dataReceived(self, data): 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) + username = check_bytes(_q_s.decode(_q_s.challenge, data.hex())) + password = check_bytes(data) 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" else: password = data.hex() _q_s.logs.info( { - "server": "vnc_server", + "server": _q_s.NAME, "action": "login", - status: "failed", + "status": status, "src_ip": self.transport.getPeer().host, "src_port": self.transport.getPeer().port, "dest_ip": _q_s.ip, @@ -154,73 +116,6 @@ def connectionLost(self, reason): reactor.listenTCP(port=self.port, factory=factory, interface=self.ip) reactor.run() - def run_server(self, process=False, auto=False): - 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), - "--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": - return True - else: - self.kill_server() - return False - else: - self.vnc_server_main() - - def close_port(self): - 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) - return ret - def test_server(self, ip=None, port=None, username=None, password=None): with suppress(Exception): ip or self.ip diff --git a/tests/test_smb_server.py b/tests/test_smb_server.py index 17a2711..542a4cc 100644 --- a/tests/test_smb_server.py +++ b/tests/test_smb_server.py @@ -27,12 +27,15 @@ def test_smb_server(server_logs): smb_client.login(USERNAME, PASSWORD) smb_client.close() - logs = load_logs_from_file(server_logs) + *connects, login = load_logs_from_file(server_logs) - assert len(logs) == 3 - for entry in logs: + assert len(connects) == 3 + for entry in connects: assert_connect_is_logged(entry, PORT) - assert "Incoming connection" in logs[0]["data"] - assert "AUTHENTICATE_MESSAGE" in logs[1]["data"] - assert "authenticated successfully" in logs[2]["data"] + assert "Incoming connection" in connects[0]["data"] + assert "AUTHENTICATE_MESSAGE" in connects[1]["data"] + assert "authenticated successfully" in connects[2]["data"] + + assert login["action"] == "login" + assert login["username"] == USERNAME diff --git a/tests/test_ssh_server.py b/tests/test_ssh_server.py index 98b3162..df66238 100644 --- a/tests/test_ssh_server.py +++ b/tests/test_ssh_server.py @@ -33,13 +33,16 @@ def test_ssh_server(server_logs): ssh = SSHClient() ssh.set_missing_host_key_policy(AutoAddPolicy()) ssh.connect(IP, port=PORT, username=USERNAME, password=PASSWORD) + ssh.exec_command("ls") ssh.close() logs = load_logs_from_file(server_logs) - assert len(logs) == 2 - connect, login = logs + assert len(logs) == 3 + connect, login, command = logs assert_connect_is_logged(connect, str(PORT)) + assert command["action"] == "command" + assert command["data"] == {"command": "ls"} assert login["action"] == "login" assert login["username"] == USERNAME diff --git a/tests/test_vnc_server.py b/tests/test_vnc_server.py index 632b6dc..b118a05 100644 --- a/tests/test_vnc_server.py +++ b/tests/test_vnc_server.py @@ -32,5 +32,9 @@ def test_vnc_server(server_logs): logs = load_logs_from_file(server_logs) - assert len(logs) == 1 - assert_connect_is_logged(logs[0], PORT) + assert len(logs) == 2 + connection, login = logs + assert_connect_is_logged(connection, PORT) + assert login["action"] == "login" + assert login["username"] == "None" # FixMe: seems to be "None" even with a user + assert login["password"] # we can't check the PW because it is encrypted