From 9ceab247a8fb1f7c04d6357451065b40d8e8c492 Mon Sep 17 00:00:00 2001 From: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:14:07 +0800 Subject: [PATCH 1/7] [winrm] say goodbye to SMB Signed-off-by: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> --- nxc/protocols/winrm.py | 78 +++++++----------- nxc/protocols/winrm/winrm_ntlm_parser.py | 100 +++++++++++++++++++++++ 2 files changed, 129 insertions(+), 49 deletions(-) create mode 100644 nxc/protocols/winrm/winrm_ntlm_parser.py diff --git a/nxc/protocols/winrm.py b/nxc/protocols/winrm.py index 2b37b8029..f7ae1a586 100644 --- a/nxc/protocols/winrm.py +++ b/nxc/protocols/winrm.py @@ -1,7 +1,7 @@ import os +import base64 import requests import urllib3 -import contextlib import logging import xml.etree.ElementTree as ET @@ -10,7 +10,6 @@ from pypsrp.wsman import NAMESPACES from pypsrp.client import Client -from impacket.smbconnection import SMBConnection from impacket.examples.secretsdump import LocalOperations, LSASecrets, SAMHashes from nxc.config import process_secret @@ -18,6 +17,7 @@ from nxc.helpers.bloodhound import add_user_bh from nxc.helpers.misc import gen_random_string from nxc.logger import NXCAdapter +from nxc.protocols.winrm.winrm_ntlm_parser import parse_challenge urllib3.disable_warnings() @@ -33,58 +33,33 @@ def __init__(self, args, db, host): self.lmhash = "" self.nthash = "" self.ssl = False - self.auth_type = None + self.challenge_header = None connection.__init__(self, args, db, host) def proto_logger(self): - # Reason why default is SMB/445, because default is enumerate over SMB. # For more details, please check the function "print_host_info" logging.getLogger("pypsrp").disabled = True logging.getLogger("pypsrp.wsman").disabled = True self.logger = NXCAdapter( extra={ - "protocol": "SMB", + "protocol": "WINRM", "host": self.host, - "port": "445", + "port": "5985", "hostname": self.hostname, } ) def enum_host_info(self): - # smb no open, specify the domain - if self.args.no_smb: - self.domain = self.args.domain - else: - try: - smb_conn = SMBConnection(self.host, self.host, None, timeout=5) - no_ntlm = False - except Exception as e: - self.logger.fail(f"Error retrieving host domain: {e} specify one manually with the '-d' flag") - else: - try: - smb_conn.login("", "") - except BrokenPipeError: - self.logger.fail("Broken Pipe Error while attempting to login") - except Exception as e: - if "STATUS_NOT_SUPPORTED" in str(e): - # no ntlm supported - no_ntlm = True - - self.domain = smb_conn.getServerDNSDomainName() if not no_ntlm else self.args.domain - self.hostname = smb_conn.getServerName() if not no_ntlm else self.host - self.server_os = smb_conn.getServerOS() - if isinstance(self.server_os.lower(), bytes): - self.server_os = self.server_os.decode("utf-8") + ntlm_info = parse_challenge(base64.b64decode(self.challenge_header.split(' ')[1].replace(',', ''))) + self.domain = ntlm_info["target_info"]["MsvAvDnsDomainName"] + self.hostname = ntlm_info["target_info"]["MsvAvNbComputerName"] + self.server_os = f'Windows NT {ntlm_info["version"]}' + self.logger.extra["hostname"] = self.hostname - self.logger.extra["hostname"] = self.hostname + self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}") - self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}") - - with contextlib.suppress(Exception): - smb_conn.logoff() - - self.db.add_host(self.host, self.port, self.hostname, self.domain, self.server_os) + self.db.add_host(self.host, self.port, self.hostname, self.domain, self.server_os) if self.args.domain: self.domain = self.args.domain @@ -98,16 +73,10 @@ def enum_host_info(self): self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-")) def print_host_info(self): - if self.args.no_smb: - self.logger.extra["protocol"] = "WINRM-SSL" if self.ssl else "WINRM" - self.logger.extra["port"] = self.port - self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain})") - else: - self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain})") - self.logger.extra["protocol"] = "WINRM-SSL" if self.ssl else "WINRM" - self.logger.extra["port"] = self.port - - self.logger.info(f"Connection information: {self.endpoint} (auth type:{self.auth_type}) (domain:{self.domain if self.args.domain else ''})") + self.logger.extra["protocol"] = "WINRM-SSL" if self.ssl else "WINRM" + self.logger.extra["port"] = self.port + self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain})") + return True def create_conn_obj(self): @@ -117,6 +86,14 @@ def create_conn_obj(self): endpoints = {} + headers = { + "Content-Length": "0", + "Keep-Alive": "true", + "Content-Type": "application/soap+xml;charset=UTF-8", + "User-Agent": "Microsoft WinRM Client", + "Authorization": "Negotiate TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw==" + } + for protocol in self.args.check_proto: endpoints[protocol] = {} endpoints[protocol]["port"] = self.port[self.args.check_proto.index(protocol)] if len(self.port) == 2 else self.port[0] @@ -131,9 +108,12 @@ def create_conn_obj(self): self.port = endpoints[protocol]["port"] try: self.logger.debug(f"Requesting URL: {endpoints[protocol]['url']}") - res = requests.post(endpoints[protocol]["url"], verify=False, timeout=self.args.http_timeout) + res = requests.post(endpoints[protocol]["url"], headers=headers, verify=False, timeout=self.args.http_timeout) self.logger.debug(f"Received response code: {res.status_code}") - self.auth_type = res.headers["WWW-Authenticate"] if "WWW-Authenticate" in res.headers else "NOAUTH" + self.challenge_header = res.headers["WWW-Authenticate"] + if (not self.challenge_header) or (not 'Negotiate' in self.challenge_header): + self.logger.info('Failed to get NTLM challenge from target "/wsman" endpoint, maybe isn\'t winrm service.') + return False self.endpoint = endpoints[protocol]["url"] self.ssl = endpoints[protocol]["ssl"] return True diff --git a/nxc/protocols/winrm/winrm_ntlm_parser.py b/nxc/protocols/winrm/winrm_ntlm_parser.py new file mode 100644 index 000000000..3d4506dfd --- /dev/null +++ b/nxc/protocols/winrm/winrm_ntlm_parser.py @@ -0,0 +1,100 @@ +# Original from here: https://github.com/nopfor/ntlm_challenger + +import datetime + + +def decoder(byte_string, decode_type): + if decode_type == "byte": + return byte_string.decode("UTF-8").replace("\x00", "") + else: + return int.from_bytes(byte_string, "little") + +def parse_version(version_bytes): + major_version = version_bytes[0] + minor_version = version_bytes[1] + product_build = decoder(version_bytes[2:4], "int") + return f"{major_version}.{minor_version}.{product_build}" + +def parse_target_info(target_info_bytes): + MsvAvEOL = 0x0000 + MsvAvNbComputerName = 0x0001 + MsvAvNbDomainName = 0x0002 + MsvAvDnsComputerName = 0x0003 + MsvAvDnsDomainName = 0x0004 + MsvAvDnsTreeName = 0x0005 + MsvAvFlags = 0x0006 + MsvAvTimestamp = 0x0007 + MsvAvSingleHost = 0x0008 + MsvAvTargetName = 0x0009 + MsvAvChannelBindings = 0x000A + + target_info = { + "MsvAvNbComputerName": None, + "MsvAvDnsDomainName": None, + } + info_offset = 0 + + while info_offset < len(target_info_bytes): + av_id = decoder(target_info_bytes[info_offset:info_offset + 2], "int") + av_len = decoder(target_info_bytes[info_offset + 2:info_offset + 4], "int") + av_value = target_info_bytes[info_offset + 4:info_offset + 4 + av_len] + + info_offset = info_offset + 4 + av_len + + if av_id == MsvAvEOL: + pass + elif av_id == MsvAvNbComputerName: + target_info["MsvAvNbComputerName"] = decoder(av_value, "byte") + elif av_id == MsvAvNbDomainName: + target_info["MsvAvNbDomainName"] = decoder(av_value, "byte") + elif av_id == MsvAvDnsComputerName: + target_info["MsvAvDnsComputerName"] = decoder(av_value, "byte") + elif av_id == MsvAvDnsDomainName: + target_info["MsvAvDnsDomainName"] = decoder(av_value, "byte") + elif av_id == MsvAvDnsTreeName: + target_info["MsvAvDnsTreeName"] = decoder(av_value, "byte") + elif av_id == MsvAvFlags: + pass + elif av_id == MsvAvTimestamp: + filetime = decoder(av_value, "int") + microseconds = (filetime - 116444736000000000) / 10 + time = datetime.datetime(1970, 1, 1) + datetime.timedelta(microseconds=microseconds) + target_info["MsvAvTimestamp"] = time.strftime("%b %d, %Y %H:%M:%S.%f") + elif av_id == MsvAvSingleHost: + target_info["MsvAvSingleHost"] = decoder(av_value, "byte") + elif av_id == MsvAvTargetName: + target_info["MsvAvTargetName"] = decoder(av_value, "byte") + elif av_id == MsvAvChannelBindings: + target_info["MsvAvChannelBindings"] = av_value + return target_info + +def parse_challenge(challenge_message): + # TargetNameFields + target_name_fields = challenge_message[12:20] + target_name_len = decoder(target_name_fields[0:2], "int") + target_name_offset = decoder(target_name_fields[4:8], "int") + + # TargetInfoFields + target_info_fields = challenge_message[40:48] + target_info_len = decoder(target_info_fields[0:2], "int") + target_info_offset = decoder(target_info_fields[4:8], "int") + + # Version + version = None + version_bytes = challenge_message[48:56] + version = parse_version(version_bytes) + + # TargetName + target_name = challenge_message[target_name_offset:target_name_offset + target_name_len] + target_name = decoder(target_name, "byte") + + # TargetInfo + target_info_bytes = challenge_message[target_info_offset:target_info_offset + target_info_len] + + target_info = parse_target_info(target_info_bytes) + + return { + "target_name": target_name, + "version": version, + "target_info": target_info + } \ No newline at end of file From 217cf21cff1ccc41a6995f8abae2a1bec38e766e Mon Sep 17 00:00:00 2001 From: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:19:13 +0800 Subject: [PATCH 2/7] [winrm] Clean args.py Signed-off-by: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> --- nxc/protocols/winrm/proto_args.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nxc/protocols/winrm/proto_args.py b/nxc/protocols/winrm/proto_args.py index 0c896aeea..66b1959b3 100644 --- a/nxc/protocols/winrm/proto_args.py +++ b/nxc/protocols/winrm/proto_args.py @@ -9,12 +9,10 @@ def proto_args(parser, std_parser, module_parser): winrm_parser.add_argument("--check-proto", nargs="+", default=["http", "https"], help="Choose what prorocol you want to check, default is %(default)s, format: 'http https'(with space separated) or 'single-protocol'") winrm_parser.add_argument("--laps", dest="laps", metavar="LAPS", type=str, help="LAPS authentification", nargs="?", const="administrator") winrm_parser.add_argument("--http-timeout", dest="http_timeout", type=int, default=10, help="HTTP timeout for WinRM connections") - no_smb_arg = winrm_parser.add_argument("--no-smb", action=get_conditional_action(_StoreTrueAction), make_required=[], help="No smb connection") dgroup = winrm_parser.add_mutually_exclusive_group() domain_arg = dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, default=None, help="domain to authenticate to") dgroup.add_argument("--local-auth", action="store_true", help="authenticate locally to each target") - no_smb_arg.make_required = [domain_arg] cgroup = winrm_parser.add_argument_group("Credential Gathering", "Options for gathering credentials") cgroup.add_argument("--dump-method", action="store", default="cmd", choices={"cmd", "powershell"}, help="Select shell type in hashes dump") From 19ee0665422da89b299db321bbd902b115355d03 Mon Sep 17 00:00:00 2001 From: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> Date: Thu, 8 Feb 2024 00:22:44 +0800 Subject: [PATCH 3/7] [winrm] Ruff Signed-off-by: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> --- nxc/protocols/winrm.py | 4 ++-- nxc/protocols/winrm/proto_args.py | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/nxc/protocols/winrm.py b/nxc/protocols/winrm.py index f7ae1a586..c0960762b 100644 --- a/nxc/protocols/winrm.py +++ b/nxc/protocols/winrm.py @@ -51,7 +51,7 @@ def proto_logger(self): ) def enum_host_info(self): - ntlm_info = parse_challenge(base64.b64decode(self.challenge_header.split(' ')[1].replace(',', ''))) + ntlm_info = parse_challenge(base64.b64decode(self.challenge_header.split(" ")[1].replace(",", ""))) self.domain = ntlm_info["target_info"]["MsvAvDnsDomainName"] self.hostname = ntlm_info["target_info"]["MsvAvNbComputerName"] self.server_os = f'Windows NT {ntlm_info["version"]}' @@ -111,7 +111,7 @@ def create_conn_obj(self): res = requests.post(endpoints[protocol]["url"], headers=headers, verify=False, timeout=self.args.http_timeout) self.logger.debug(f"Received response code: {res.status_code}") self.challenge_header = res.headers["WWW-Authenticate"] - if (not self.challenge_header) or (not 'Negotiate' in self.challenge_header): + if (not self.challenge_header) or ("Negotiate" not in self.challenge_header): self.logger.info('Failed to get NTLM challenge from target "/wsman" endpoint, maybe isn\'t winrm service.') return False self.endpoint = endpoints[protocol]["url"] diff --git a/nxc/protocols/winrm/proto_args.py b/nxc/protocols/winrm/proto_args.py index 66b1959b3..d00a1372e 100644 --- a/nxc/protocols/winrm/proto_args.py +++ b/nxc/protocols/winrm/proto_args.py @@ -1,6 +1,3 @@ -from argparse import _StoreTrueAction - - def proto_args(parser, std_parser, module_parser): winrm_parser = parser.add_parser("winrm", help="own stuff using WINRM", parents=[std_parser, module_parser]) winrm_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes") @@ -11,7 +8,7 @@ def proto_args(parser, std_parser, module_parser): winrm_parser.add_argument("--http-timeout", dest="http_timeout", type=int, default=10, help="HTTP timeout for WinRM connections") dgroup = winrm_parser.add_mutually_exclusive_group() - domain_arg = dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, default=None, help="domain to authenticate to") + dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, default=None, help="domain to authenticate to") dgroup.add_argument("--local-auth", action="store_true", help="authenticate locally to each target") cgroup = winrm_parser.add_argument_group("Credential Gathering", "Options for gathering credentials") From 1e12cadb3353f53bab7ef1b8c1329d4b45db4f50 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 13 Feb 2024 01:23:42 +0100 Subject: [PATCH 4/7] Removed unused conditional function, removed "NT" from windows version so it matches SMB enumeration --- nxc/protocols/winrm.py | 2 +- nxc/protocols/winrm/proto_args.py | 15 --------------- nxc/protocols/winrm/winrm_ntlm_parser.py | 2 +- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/nxc/protocols/winrm.py b/nxc/protocols/winrm.py index c0960762b..fb9cf642c 100644 --- a/nxc/protocols/winrm.py +++ b/nxc/protocols/winrm.py @@ -54,7 +54,7 @@ def enum_host_info(self): ntlm_info = parse_challenge(base64.b64decode(self.challenge_header.split(" ")[1].replace(",", ""))) self.domain = ntlm_info["target_info"]["MsvAvDnsDomainName"] self.hostname = ntlm_info["target_info"]["MsvAvNbComputerName"] - self.server_os = f'Windows NT {ntlm_info["version"]}' + self.server_os = f'Windows {ntlm_info["version"]}' self.logger.extra["hostname"] = self.hostname self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}") diff --git a/nxc/protocols/winrm/proto_args.py b/nxc/protocols/winrm/proto_args.py index d00a1372e..ca4e81a51 100644 --- a/nxc/protocols/winrm/proto_args.py +++ b/nxc/protocols/winrm/proto_args.py @@ -24,18 +24,3 @@ def proto_args(parser, std_parser, module_parser): cgroup.add_argument("-X", metavar="PS_COMMAND", dest="ps_execute", help="execute the specified PowerShell command") return parser - - -def get_conditional_action(baseAction): - class ConditionalAction(baseAction): - def __init__(self, option_strings, dest, **kwargs): - x = kwargs.pop("make_required", []) - super().__init__(option_strings, dest, **kwargs) - self.make_required = x - - def __call__(self, parser, namespace, values, option_string=None): - for x in self.make_required: - x.required = True - super().__call__(parser, namespace, values, option_string) - - return ConditionalAction \ No newline at end of file diff --git a/nxc/protocols/winrm/winrm_ntlm_parser.py b/nxc/protocols/winrm/winrm_ntlm_parser.py index 3d4506dfd..838a5d080 100644 --- a/nxc/protocols/winrm/winrm_ntlm_parser.py +++ b/nxc/protocols/winrm/winrm_ntlm_parser.py @@ -13,7 +13,7 @@ def parse_version(version_bytes): major_version = version_bytes[0] minor_version = version_bytes[1] product_build = decoder(version_bytes[2:4], "int") - return f"{major_version}.{minor_version}.{product_build}" + return f"{major_version}.{minor_version} Build {product_build}" def parse_target_info(target_info_bytes): MsvAvEOL = 0x0000 From 7e0c0e0c07ad610c8131c0fb8a51b2f291240022 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 13 Feb 2024 01:24:42 +0100 Subject: [PATCH 5/7] Formating --- nxc/protocols/winrm.py | 2 +- nxc/protocols/winrm/winrm_ntlm_parser.py | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/nxc/protocols/winrm.py b/nxc/protocols/winrm.py index fb9cf642c..f152e453d 100644 --- a/nxc/protocols/winrm.py +++ b/nxc/protocols/winrm.py @@ -38,7 +38,7 @@ def __init__(self, args, db, host): connection.__init__(self, args, db, host) def proto_logger(self): - # For more details, please check the function "print_host_info" + # For more details, please check the function "print_host_info" logging.getLogger("pypsrp").disabled = True logging.getLogger("pypsrp.wsman").disabled = True self.logger = NXCAdapter( diff --git a/nxc/protocols/winrm/winrm_ntlm_parser.py b/nxc/protocols/winrm/winrm_ntlm_parser.py index 838a5d080..75feb580c 100644 --- a/nxc/protocols/winrm/winrm_ntlm_parser.py +++ b/nxc/protocols/winrm/winrm_ntlm_parser.py @@ -9,12 +9,14 @@ def decoder(byte_string, decode_type): else: return int.from_bytes(byte_string, "little") -def parse_version(version_bytes): + +def parse_version(version_bytes): major_version = version_bytes[0] minor_version = version_bytes[1] product_build = decoder(version_bytes[2:4], "int") return f"{major_version}.{minor_version} Build {product_build}" + def parse_target_info(target_info_bytes): MsvAvEOL = 0x0000 MsvAvNbComputerName = 0x0001 @@ -38,9 +40,9 @@ def parse_target_info(target_info_bytes): av_id = decoder(target_info_bytes[info_offset:info_offset + 2], "int") av_len = decoder(target_info_bytes[info_offset + 2:info_offset + 4], "int") av_value = target_info_bytes[info_offset + 4:info_offset + 4 + av_len] - + info_offset = info_offset + 4 + av_len - + if av_id == MsvAvEOL: pass elif av_id == MsvAvNbComputerName: @@ -68,6 +70,7 @@ def parse_target_info(target_info_bytes): target_info["MsvAvChannelBindings"] = av_value return target_info + def parse_challenge(challenge_message): # TargetNameFields target_name_fields = challenge_message[12:20] @@ -97,4 +100,4 @@ def parse_challenge(challenge_message): "target_name": target_name, "version": version, "target_info": target_info - } \ No newline at end of file + } From 1d1c03be7a4f75f7e9ee110faaead171a167039b Mon Sep 17 00:00:00 2001 From: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> Date: Tue, 27 Feb 2024 19:15:42 +0800 Subject: [PATCH 6/7] [winrm] better os info Signed-off-by: XiaoliChan <30458572+XiaoliChan@users.noreply.github.com> --- .../winrm/winrm_ntlm_parser.py => helpers/ntlm_parser.py} | 6 +++--- nxc/protocols/winrm.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename nxc/{protocols/winrm/winrm_ntlm_parser.py => helpers/ntlm_parser.py} (96%) diff --git a/nxc/protocols/winrm/winrm_ntlm_parser.py b/nxc/helpers/ntlm_parser.py similarity index 96% rename from nxc/protocols/winrm/winrm_ntlm_parser.py rename to nxc/helpers/ntlm_parser.py index 75feb580c..bda36b099 100644 --- a/nxc/protocols/winrm/winrm_ntlm_parser.py +++ b/nxc/helpers/ntlm_parser.py @@ -2,6 +2,8 @@ import datetime +from impacket.smb3 import WIN_VERSIONS + def decoder(byte_string, decode_type): if decode_type == "byte": @@ -11,10 +13,8 @@ def decoder(byte_string, decode_type): def parse_version(version_bytes): - major_version = version_bytes[0] - minor_version = version_bytes[1] product_build = decoder(version_bytes[2:4], "int") - return f"{major_version}.{minor_version} Build {product_build}" + return f"{WIN_VERSIONS[product_build]} Build {product_build}" def parse_target_info(target_info_bytes): diff --git a/nxc/protocols/winrm.py b/nxc/protocols/winrm.py index f152e453d..9a0945579 100644 --- a/nxc/protocols/winrm.py +++ b/nxc/protocols/winrm.py @@ -16,8 +16,8 @@ from nxc.connection import connection from nxc.helpers.bloodhound import add_user_bh from nxc.helpers.misc import gen_random_string +from nxc.helpers.ntlm_parser import parse_challenge from nxc.logger import NXCAdapter -from nxc.protocols.winrm.winrm_ntlm_parser import parse_challenge urllib3.disable_warnings() @@ -54,7 +54,7 @@ def enum_host_info(self): ntlm_info = parse_challenge(base64.b64decode(self.challenge_header.split(" ")[1].replace(",", ""))) self.domain = ntlm_info["target_info"]["MsvAvDnsDomainName"] self.hostname = ntlm_info["target_info"]["MsvAvNbComputerName"] - self.server_os = f'Windows {ntlm_info["version"]}' + self.server_os = ntlm_info["version"] self.logger.extra["hostname"] = self.hostname self.output_filename = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}") From 9f0ac2cf90d4ab4075f27dabe1171d38697c58b2 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 28 Feb 2024 00:55:29 +0100 Subject: [PATCH 7/7] Added fallback if build version is not available (yet) --- nxc/helpers/ntlm_parser.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nxc/helpers/ntlm_parser.py b/nxc/helpers/ntlm_parser.py index bda36b099..671c3b199 100644 --- a/nxc/helpers/ntlm_parser.py +++ b/nxc/helpers/ntlm_parser.py @@ -13,8 +13,13 @@ def decoder(byte_string, decode_type): def parse_version(version_bytes): + major_version = version_bytes[0] + minor_version = version_bytes[1] product_build = decoder(version_bytes[2:4], "int") - return f"{WIN_VERSIONS[product_build]} Build {product_build}" + if product_build in WIN_VERSIONS: + return f"{WIN_VERSIONS[product_build]} Build {product_build}" + else: + return f"Windows {major_version}.{minor_version} Build {product_build}" def parse_target_info(target_info_bytes): @@ -93,7 +98,6 @@ def parse_challenge(challenge_message): # TargetInfo target_info_bytes = challenge_message[target_info_offset:target_info_offset + target_info_len] - target_info = parse_target_info(target_info_bytes) return {