From b3cd20ca8b16251d22d6bdfa67cd57200855ddc1 Mon Sep 17 00:00:00 2001 From: Roman Hergenreder Date: Tue, 19 Sep 2023 14:39:11 +0200 Subject: [PATCH] ssh and smtp server --- __init__.py | 2 +- gobuster.sh | 2 +- smtpserver.py | 40 ++++++++++++++++++++++ sshserver.py | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++ util.py | 23 +++++++++++++ 5 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 smtpserver.py create mode 100644 sshserver.py diff --git a/__init__.py b/__init__.py index 3d17317..3d47c96 100644 --- a/__init__.py +++ b/__init__.py @@ -2,7 +2,7 @@ import os import sys __doc__ = __doc__ or "" -__all__ = ["util", "fileserver", "xss_handler", "rev_shell", "xp_cmdshell", "dnsserver", "sqli"] +__all__ = ["util", "fileserver", "xss_handler", "rev_shell", "xp_cmdshell", "dnsserver", "sqli", "smtpserver"] inc_dir = os.path.dirname(os.path.realpath(__file__)) sys.path.append(inc_dir) diff --git a/gobuster.sh b/gobuster.sh index 96cc758..ebed56c 100755 --- a/gobuster.sh +++ b/gobuster.sh @@ -6,4 +6,4 @@ if [ $# -lt 1 ]; then fi HOST=$1 -gobuster dir --url="${HOST}" --wordlist="/usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt" -b "403,404" "${@:2}" +(set -x; gobuster dir --url="${HOST}" --wordlist="/usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt" -b "403,404" "${@:2}") diff --git a/smtpserver.py b/smtpserver.py new file mode 100644 index 0000000..c545da1 --- /dev/null +++ b/smtpserver.py @@ -0,0 +1,40 @@ +import smtpd +import email +import asyncore +import threading + +class SMTPServer(smtpd.SMTPServer): + + def __init__(self, addr='0.0.0.0', port=25): + super().__init__((addr, port), None) + self.listen_thread = None + self.verbose = False + self.on_message = None + self.is_running = True + + def process_message(self, peer, mailfrom, rcpttos, data, **kwargs): + if self.verbose: + print(f"SMTP IN: {peer=} {mailfrom=} {rcpttos=} {len(data)} bytes, extra:", kwargs) + if self.on_message and callable(self.on_message): + mail = email.message_from_bytes(data) + self.on_message(peer, mailfrom, rcpttos, mail) + + def start(self): + if self.verbose: + print(f"SMTP server running on: {self._localaddr[0]}:{self._localaddr[1]}") + try: + while self.is_running: + asyncore.loop(timeout=1, use_poll=True) + except KeyboardInterrupt: + self.close() + + def start_background(self): + self.listen_thread = threading.Thread(target=self.start) + self.listen_thread.start() + return self.listen_thread + + def stop(self): + self.is_running = False + self.close() + if self.listen_thread: + self.listen_thread.join() \ No newline at end of file diff --git a/sshserver.py b/sshserver.py new file mode 100644 index 0000000..f6434f0 --- /dev/null +++ b/sshserver.py @@ -0,0 +1,92 @@ +import socket +import select +import threading +import paramiko + +class ParamikoConnection(paramiko.ServerInterface): + def __init__(self, server): + self.event = threading.Event() + self.server = server + + def check_channel_request(self, kind, chanid): + print("check_channel_request", kind, chanid) + if kind == 'session': + return paramiko.OPEN_SUCCEEDED + return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED + + def check_auth_password(self, username, password): + if self.server.on_ssh_login: + return self.server.on_ssh_login(username, password) + + print("check_auth_password", username, password) + return paramiko.AUTH_SUCCESSFUL + +class SSHServer: + + def __init__(self, addr='0.0.0.0', port=22): + self.server_address = addr + self.listen_port = port + self.listen_socket = None + self.listen_thread = None + self.client_sockets = [] + self.transports = [] + self.verbose = True + self.on_message = None + self.is_running = True + self.private_key = None + + # hooks + self.on_ssh_login = None + + + def load_private_key_from_file(self, path): + with open(path, "r") as f: + self.private_key = paramiko.RSAKey.from_private_key(f) + + def start(self): + + if self.private_key is None: + self.private_key = paramiko.RSAKey.generate(2048) + + self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.listen_socket.setblocking(False) + self.listen_socket.bind((self.server_address, self.listen_port)) + self.listen_socket.listen() + if self.verbose: + print(f"SSH server running on: {self.server_address}:{self.listen_port}") + try: + while self.is_running: + try: + client_socket, client_address = self.listen_socket.accept() + if self.verbose: + print("Incoming connection:", client_address) + self.client_sockets.append(client_socket) + transport = paramiko.Transport(client_socket) + transport.add_server_key(self.private_key) + paramiko_connection = ParamikoConnection(self) + transport.start_server(server=paramiko_connection) + self.transports.append(transport) + # for client_sock in self.client_sockets: + + except BlockingIOError: + pass + # handle_client(client_socket, client_address) + finally: + self.listen_socket.close() + + def start_background(self): + self.listen_thread = threading.Thread(target=self.start) + self.listen_thread.start() + return self.listen_thread + + def close(self): + if self.listen_socket: + self.listen_socket.close() + for sock in self.client_sockets: + sock.close() + + def stop(self): + self.is_running = False + self.close() + if self.listen_thread: + self.listen_thread.join() diff --git a/util.py b/util.py index c2e1b63..02c784b 100755 --- a/util.py +++ b/util.py @@ -9,6 +9,8 @@ import string import sys import os import io +import json + from PIL import Image def isPortInUse(port): @@ -80,6 +82,27 @@ def assert_header_present(res, header, err=None): err = f"[-] '{res.url}' did not return header: {header}" if err is None else err exit_with_error(res, err) +def assert_not_empty(res, err=None): + if len(res.content) > 0: + return + + err = f"[-] '{res.url}' did not return any data" if err is None else err + exit_with_error(res, err) + +def assert_json_path(res, path, value, err=None): + assert_content_type(res, "application/json") + assert_not_empty(res) + + json_data = json.loads(res.text) + for key in filter(None, path.split(".")): + json_data = json_data[key] + + if json_data == value: + return + + err = f"[-] '{res.url}' value at path '{path}' does not match. got={json_data} expected={value}" if err is None else err + exit_with_error(res, err) + def openServer(address, ports=None): listenPort = None retry = True