Skip to content

Commit

Permalink
Merge pull request #62 from fkie-cad/multiprocessing2
Browse files Browse the repository at this point in the history
Multiprocessing
  • Loading branch information
giga-a authored Mar 7, 2024
2 parents 5b3bfbe + 9fc65df commit a990b2c
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 71 deletions.
2 changes: 1 addition & 1 deletion honeypots/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def _set_up_honeypots(self): # noqa: C901
running_honeypots = {"good": [], "bad": []}
if len(self.honeypots) > 0:
for _, server_name, status in self.honeypots:
if status is False or status is None:
if not status:
running_honeypots["bad"].append(server_name)
else:
running_honeypots["good"].append(server_name)
Expand Down
47 changes: 23 additions & 24 deletions honeypots/base_server.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
from __future__ import annotations

import inspect
from abc import ABC, abstractmethod
from multiprocessing import Process
from os import getenv
from shlex import split
from subprocess import Popen
from typing import Any
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,
service_has_started,
set_local_vars,
set_up_error_logging,
setup_logger,
)


Expand All @@ -26,7 +23,7 @@ class BaseServer(ABC):
DEFAULT_PASSWORD = "test"

def __init__(self, **kwargs):
self.auto_disabled = None
self.auto_disabled = False
self.process = None
self.uuid = f"honeypotslogger_{self.__class__.__name__}_{str(uuid4())[:8]}"
self.config = kwargs.get("config", "")
Expand Down Expand Up @@ -58,19 +55,24 @@ def __init__(self, **kwargs):
or ""
)
self.logger = set_up_error_logging()
self._server_process: Process | None = None

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)
if self._server_process:
try:
self._server_process.terminate()
self._server_process.join(timeout=5)
except TimeoutError:
self._server_process.kill()

@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()
Expand All @@ -81,21 +83,10 @@ def run_server(self, process: bool = False, auto: bool = False) -> bool | None:
if port > 0:
self.port = port
run = True
elif self.close_port() and self.kill_server():
elif self.close_port():
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"
status = self._start_server() if run else "error"

self.log(
{
Expand All @@ -111,6 +102,14 @@ def run_server(self, process: bool = False, auto: bool = False) -> bool | None:
self.kill_server()
return False

def _start_server(self) -> str:
self._server_process = Process(target=self.server_main)
self._server_process.start()
if service_has_started(int(self.port)):
return "success"
self.logger.error(f"Server {self.NAME} did not start")
return "error"

def check_login(self, username: str, password: str, ip: str, port: int) -> bool:
status = "success" if self._login_is_correct(username, password) else "failed"
self.log(
Expand Down
39 changes: 24 additions & 15 deletions honeypots/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@
from sqlite3 import connect as sqlite3_connect
from sys import stdout
from tempfile import _get_candidate_names, gettempdir, NamedTemporaryFile
from time import sleep
from time import sleep, time
from typing import Any, Iterator
from urllib.parse import urlparse

import psutil
from OpenSSL import crypto
from psutil import process_iter
from psycopg2 import connect as psycopg2_connect, sql
Expand Down Expand Up @@ -241,15 +242,6 @@ def kill_servers(name):
process.kill()


def check_if_server_is_running(uuid):
with suppress(Exception):
for process in process_iter():
cmdline = " ".join(process.cmdline())
if "--custom" in cmdline and uuid in cmdline:
return True
return False


def kill_server_wrapper(server_name, name, process):
with suppress(Exception):
if process is not None:
Expand Down Expand Up @@ -300,11 +292,6 @@ def default(self, obj):
return repr(obj).replace("\x00", " ")


class ComplexEncoder_db(JSONEncoder):
def default(self, obj):
return "Something wrong, deleted.."


def serialize_object(_dict):
if isinstance(_dict, Mapping):
return dict((k, serialize_object(v)) for k, v in _dict.items())
Expand Down Expand Up @@ -742,3 +729,25 @@ def get_headers_and_ip_from_request(request, options):
if client_ip == "":
client_ip = request.getClientAddress().host
return client_ip, headers


def service_has_started(port: int):
try:
wait_for_service(port)
return True
except TimeoutError:
return False


def wait_for_service(port: int, interval: float = 0.1, timeout: int = 5.0):
start_time = time()
while True:
if _service_runs(port):
return
sleep(interval)
if time() - start_time > timeout:
raise TimeoutError()


def _service_runs(port: int) -> bool:
return any(service.laddr.port == port for service in psutil.net_connections())
20 changes: 8 additions & 12 deletions honeypots/qbsniffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,17 @@
// -------------------------------------------------------------
"""

from warnings import filterwarnings
from cryptography.utils import CryptographyDeprecationWarning

filterwarnings(action="ignore", category=CryptographyDeprecationWarning)

from binascii import hexlify
from multiprocessing import Process
from re import compile as rcompile, search as rsearch
from sys import stdout
from uuid import uuid4

from netifaces import AF_INET, AF_LINK, ifaddresses
from scapy.all import *
from scapy.layers.inet import IP, TCP
from scapy.sendrecv import send, sniff

from honeypots.helper import setup_logger
from honeypots.helper import server_arguments, setup_logger


class QBSniffer:
Expand All @@ -38,7 +34,7 @@ def __init__(self, filter=None, interface=None, config=""):
(0, 0, "Echo/Ping reply"),
(3, 0, "Destination network unreachable"),
(3, 1, "Destination host unreachable"),
(3, 2, "Desination protocol unreachable"),
(3, 2, "Destination protocol unreachable"),
(3, 3, "Destination port unreachable"),
(3, 4, "Fragmentation required"),
(3, 5, "Source route failed"),
Expand All @@ -51,7 +47,7 @@ def __init__(self, filter=None, interface=None, config=""):
(3, 12, "Host unreachable for TOS"),
(3, 13, "Communication administratively prohibited"),
(3, 14, "Host Precedence Violation"),
(3, 15, "Precendence cutoff in effect"),
(3, 15, "Precedence cutoff in effect"),
(4, 0, "Source quench"),
(5, 0, "Redirect Datagram for the Network"),
(5, 1, "Redirect Datagram for the Host"),
Expand Down Expand Up @@ -290,9 +286,9 @@ def kill_sniffer(self):


if __name__ == "__main__":
from server_options import server_arguments

parsed = server_arguments()
if parsed.docker or parsed.aws or parsed.custom:
qsniffer = QSniffer(filter=parsed.filter, interface=parsed.interface, config=parsed.config)
qsniffer = QBSniffer(
filter=parsed.filter, interface=parsed.interface, config=parsed.config
)
qsniffer.run_sniffer()
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ authors = [
]
description = "30 different honeypots in one package! (dhcp, dns, elastic, ftp, http proxy, https proxy, http, https, imap, ipp, irc, ldap, memcache, mssql, mysql, ntp, oracle, pjl, pop3, postgres, rdp, redis, sip, smb, smtp, snmp, socks5, ssh, telnet, vnc)"
readme = "README.rst"
requires-python = ">=3.8"
# ToDo: fix smtp incompatibility with 3.12
requires-python = ">=3.8,<3.12"
dependencies = [
"twisted==21.7.0",
"psutil==5.9.0",
Expand All @@ -23,7 +24,7 @@ dependencies = [
"impacket==0.9.24",
"paramiko==3.1.0",
"scapy==2.4.5",
"service_identity==21.1.0",
"service-identity==21.1.0",
"netifaces==0.11.0",
]
license = {text = "AGPL-3.0"}
Expand Down
20 changes: 3 additions & 17 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import json
from contextlib import contextmanager
from socket import AF_INET, IPPROTO_UDP, SOCK_DGRAM, SOCK_STREAM, socket
from time import sleep, time
from time import sleep
from typing import TYPE_CHECKING

import psutil
from honeypots.helper import wait_for_service

if TYPE_CHECKING:
from pathlib import Path
Expand Down Expand Up @@ -63,20 +63,6 @@ def assert_login_is_logged(login: dict[str, str]):

@contextmanager
def wait_for_server(port: str | int):
_wait_for_service(int(port))
wait_for_service(int(port))
yield
sleep(0.5) # give the server process some time to write logs


def _wait_for_service(port: int, interval: float = 0.1, timeout: int = 5.0):
start_time = time()
while True:
if _service_runs(port):
return
sleep(interval)
if time() - start_time > timeout:
raise TimeoutError()


def _service_runs(port: int) -> bool:
return any(service.laddr.port == port for service in psutil.net_connections())

0 comments on commit a990b2c

Please sign in to comment.