From 2afa7081315f688f5973473191305d1cd59ba904 Mon Sep 17 00:00:00 2001 From: Xre0uS Date: Fri, 27 Sep 2024 19:10:11 +0800 Subject: [PATCH] Added handling for SMB session timeout (#102) SMB timeout defaults to 3 seconds instead of 0.1, if timeout, error will be shown. --- smbclientng/__main__.py | 4 +++- smbclientng/core/SMBSession.py | 16 +++++++++++----- smbclientng/core/SessionsManager.py | 7 +++++-- smbclientng/core/utils.py | 23 ++++++++++++----------- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/smbclientng/__main__.py b/smbclientng/__main__.py index f1315a3..34944ee 100644 --- a/smbclientng/__main__.py +++ b/smbclientng/__main__.py @@ -31,6 +31,7 @@ def parseArgs(): parser.add_argument("-S", "--startup-script", metavar="startup_script", required=False, type=str, help="File containing the list of commands to be typed at start of the console.") parser.add_argument("-N", "--not-interactive", dest="not_interactive", required=False, action="store_true", default=False, help="Non interactive mode.") parser.add_argument("-L", "--logfile", dest="logfile", metavar="LOGFILE", required=False, default=None, type=str, help="File to write logs to.") + parser.add_argument( "--timeout", dest="timeout", metavar="TIMEOUT", required=False, type=float, default=3, help="Timeout in seconds for SMB connections (default: 3)") group_target = parser.add_argument_group("Target") group_target.add_argument("--host", action="store", metavar="HOST", required=True, type=str, help="IP address or hostname of the SMB Server to connect to.") @@ -125,7 +126,8 @@ def main(): sessionsManager.create_new_session( credentials=credentials, host=options.host, - port=options.port + port=options.port, + timeout=options.timeout, ) # Start the main interactive command line diff --git a/smbclientng/core/SMBSession.py b/smbclientng/core/SMBSession.py index b32a7c6..7f4bb1b 100644 --- a/smbclientng/core/SMBSession.py +++ b/smbclientng/core/SMBSession.py @@ -53,7 +53,7 @@ class SMBSession(object): test_rights(sharename): Tests read and write access rights on a share. """ - def __init__(self, host, port, credentials, config=None, logger=None): + def __init__(self, host, port, timeout, credentials, config=None, logger=None): super(SMBSession, self).__init__() # Objects self.config = config @@ -63,6 +63,8 @@ def __init__(self, host, port, credentials, config=None, logger=None): self.host = host # Target port (by default on 445) self.port = port + # Timeout (default 3 seconds) + self.timeout = timeout # Credentials self.credentials = credentials @@ -118,14 +120,17 @@ def init_smb_session(self): self.logger.debug("[>] Connecting to remote SMB server '%s' ... " % self.host) try: - if is_port_open(self.host, self.port): + result, error = is_port_open(self.host, self.port, self.timeout) + if result: self.smbClient = impacket.smbconnection.SMBConnection( remoteName=self.host, remoteHost=self.host, - sess_port=int(self.port) + sess_port=int(self.port), + timeout=self.timeout, ) + self.connected = True else: - self.logger.error("Could not connect to '%s:%d'" % (self.host, int(self.port))) + self.logger.error(f"Could not connect to '{self.host}:{self.port}', {error}.") self.connected = False self.smbClient = None except OSError as err: @@ -211,7 +216,8 @@ def ping_smb_session(self): bool: True if the echo command succeeds (indicating the session is active), False otherwise. """ - if not is_port_open(self.host, self.port): + result, error = is_port_open(self.host, self.port, self.timeout) + if result: self.connected = False else: try: diff --git a/smbclientng/core/SessionsManager.py b/smbclientng/core/SessionsManager.py index 7b75fa5..6bf77f4 100644 --- a/smbclientng/core/SessionsManager.py +++ b/smbclientng/core/SessionsManager.py @@ -38,7 +38,7 @@ def __init__(self, config, logger): self.config = config self.logger = logger - def create_new_session(self, credentials, host, port=445): + def create_new_session(self, credentials, host, timeout, port=445): """ Creates a new session with the given session information. @@ -52,6 +52,7 @@ def create_new_session(self, credentials, host, port=445): smbSession = SMBSession( host=host, port=port, + timeout=timeout, credentials=credentials, config=self.config, logger=self.logger @@ -129,6 +130,7 @@ def process_command_line(self, arguments): group_target = mode_create.add_argument_group("Target") group_target.add_argument("--host", action="store", metavar="HOST", required=True, type=str, help="IP address or hostname of the SMB Server to connect to.") group_target.add_argument("--port", action="store", metavar="PORT", type=int, default=445, help="Port of the SMB Server to connect to. (default: 445)") + group_target.add_argument( "--timeout", dest="timeout", metavar="TIMEOUT", required=False, type=float, default=3, help="Timeout in seconds for SMB connections (default: 3)") authconn = mode_create.add_argument_group("Authentication & connection") authconn.add_argument("--kdcHost", dest="kdcHost", action="store", metavar="FQDN KDC", help="FQDN of KDC for Kerberos.") authconn.add_argument("-d", "--domain", dest="auth_domain", metavar="DOMAIN", action="store", default='.', help="(FQDN) domain to authenticate to.") @@ -195,7 +197,8 @@ def process_command_line(self, arguments): self.create_new_session( credentials=credentials, host=options.host, - port=options.port + port=options.port, + timeout=options.timeout ) # diff --git a/smbclientng/core/utils.py b/smbclientng/core/utils.py index 68556af..9b7ac57 100644 --- a/smbclientng/core/utils.py +++ b/smbclientng/core/utils.py @@ -452,26 +452,27 @@ def resolve_remote_files(smbSession, arguments): return resolved_pathFromRoot_files -def is_port_open(target, port) -> bool: +def is_port_open(target, port, timeout): """ Check if a specific port on a target host is open. This function attempts to establish a TCP connection to the specified port on the target host. If the connection is successful, it indicates that the port is open. If the connection fails, - it indicates that the port is closed or the host is unreachable. + it returns the error message. Args: target (str): The hostname or IP address of the target host. port (int): The port number to check. + timeout (float): The timeout in seconds for the connection attempt. Default is 1.0 second. Returns: - bool: True if the port is open, False otherwise. + bool, str: True if the port is open, otherwise False and error message. """ - - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.settimeout(0.1) - # Non-existant domains cause a lot of errors, added error handling - try: - return s.connect_ex((target, port)) == 0 - except Exception as e: - return False \ No newline at end of file + + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.settimeout(timeout) + s.connect((target, port)) + return True, None + except Exception as e: + return False, str(e) \ No newline at end of file