#!/usr/bin/env python3 # Stolen from https://github.com/Wh1t3Fox/polenum from impacket.dcerpc.v5.rpcrt import DCERPC_v5 from impacket.dcerpc.v5 import transport, samr from time import strftime, gmtime from nxc.logger import nxc_logger def d2b(a): tbin = [] while a: tbin.append(a % 2) a //= 2 t2bin = tbin[::-1] if len(t2bin) != 8: for _x in range(6 - len(t2bin)): t2bin.insert(0, 0) return "".join([str(g) for g in t2bin]) def convert(low, high, lockout=False): time = "" tmp = 0 if low == 0 and hex(high) == "-0x80000000": return "Not Set" if low == 0 and high == 0: return "None" if not lockout: if low != 0: high = abs(high + 1) else: high = abs(high) low = abs(low) tmp = low + (high) * 16**8 # convert to 64bit int tmp *= 1e-7 # convert to seconds else: tmp = abs(high) * (1e-7) try: minutes = int(strftime("%M", gmtime(tmp))) hours = int(strftime("%H", gmtime(tmp))) days = int(strftime("%j", gmtime(tmp))) - 1 except ValueError: return "[-] Invalid TIME" if days > 1: time += f"{days} days " elif days == 1: time += f"{days} day " if hours > 1: time += f"{hours} hours " elif hours == 1: time += f"{hours} hour " if minutes > 1: time += f"{minutes} minutes " elif minutes == 1: time += f"{minutes} minute " return time class PassPolDump: KNOWN_PROTOCOLS = { "139/SMB": (r"ncacn_np:%s[\pipe\samr]", 139), "445/SMB": (r"ncacn_np:%s[\pipe\samr]", 445), } def __init__(self, connection): self.logger = connection.logger self.addr = connection.host if not connection.kerberos else connection.hostname + "." + connection.domain self.protocol = connection.args.port self.username = connection.username self.password = connection.password self.domain = connection.domain self.hash = connection.hash self.lmhash = "" self.nthash = "" self.aesKey = connection.aesKey self.doKerberos = connection.kerberos self.protocols = PassPolDump.KNOWN_PROTOCOLS.keys() self.pass_pol = {} if self.hash is not None: if self.hash.find(":") != -1: self.lmhash, self.nthash = self.hash.split(":") else: self.nthash = self.hash if self.password is None: self.password = "" def dump(self): # Try all requested protocols until one works. for protocol in self.protocols: try: protodef = PassPolDump.KNOWN_PROTOCOLS[protocol] port = protodef[1] except KeyError: nxc_logger.debug(f"Invalid Protocol '{protocol}'") nxc_logger.debug(f"Trying protocol {protocol}") rpctransport = transport.SMBTransport( self.addr, port, r"\samr", self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, doKerberos=self.doKerberos, ) try: self.fetchList(rpctransport) except Exception as e: nxc_logger.debug(f"Protocol failed: {e}") else: # Got a response. No need for further iterations. self.pretty_print() break return self.pass_pol def fetchList(self, rpctransport): dce = DCERPC_v5(rpctransport) dce.connect() dce.bind(samr.MSRPC_UUID_SAMR) # Setup Connection resp = samr.hSamrConnect2(dce) if resp["ErrorCode"] != 0: raise Exception("Connect error") resp2 = samr.hSamrEnumerateDomainsInSamServer( dce, serverHandle=resp["ServerHandle"], enumerationContext=0, preferedMaximumLength=500, ) if resp2["ErrorCode"] != 0: raise Exception("Connect error") resp3 = samr.hSamrLookupDomainInSamServer( dce, serverHandle=resp["ServerHandle"], name=resp2["Buffer"]["Buffer"][0]["Name"], ) if resp3["ErrorCode"] != 0: raise Exception("Connect error") resp4 = samr.hSamrOpenDomain( dce, serverHandle=resp["ServerHandle"], desiredAccess=samr.MAXIMUM_ALLOWED, domainId=resp3["DomainId"], ) if resp4["ErrorCode"] != 0: raise Exception("Connect error") self.__domains = resp2["Buffer"]["Buffer"] domainHandle = resp4["DomainHandle"] # End Setup re = samr.hSamrQueryInformationDomain2( dce, domainHandle=domainHandle, domainInformationClass=samr.DOMAIN_INFORMATION_CLASS.DomainPasswordInformation, ) self.__min_pass_len = re["Buffer"]["Password"]["MinPasswordLength"] or "None" self.__pass_hist_len = re["Buffer"]["Password"]["PasswordHistoryLength"] or "None" self.__max_pass_age = convert( int(re["Buffer"]["Password"]["MaxPasswordAge"]["LowPart"]), int(re["Buffer"]["Password"]["MaxPasswordAge"]["HighPart"]), ) self.__min_pass_age = convert( int(re["Buffer"]["Password"]["MinPasswordAge"]["LowPart"]), int(re["Buffer"]["Password"]["MinPasswordAge"]["HighPart"]), ) self.__pass_prop = d2b(re["Buffer"]["Password"]["PasswordProperties"]) re = samr.hSamrQueryInformationDomain2( dce, domainHandle=domainHandle, domainInformationClass=samr.DOMAIN_INFORMATION_CLASS.DomainLockoutInformation, ) self.__rst_accnt_lock_counter = convert(0, re["Buffer"]["Lockout"]["LockoutObservationWindow"], lockout=True) self.__lock_accnt_dur = convert(0, re["Buffer"]["Lockout"]["LockoutDuration"], lockout=True) self.__accnt_lock_thres = re["Buffer"]["Lockout"]["LockoutThreshold"] or "None" re = samr.hSamrQueryInformationDomain2( dce, domainHandle=domainHandle, domainInformationClass=samr.DOMAIN_INFORMATION_CLASS.DomainLogoffInformation, ) self.__force_logoff_time = convert( re["Buffer"]["Logoff"]["ForceLogoff"]["LowPart"], re["Buffer"]["Logoff"]["ForceLogoff"]["HighPart"], ) self.pass_pol = { "min_pass_len": self.__min_pass_len, "pass_hist_len": self.__pass_hist_len, "max_pass_age": self.__max_pass_age, "min_pass_age": self.__min_pass_age, "pass_prop": self.__pass_prop, "rst_accnt_lock_counter": self.__rst_accnt_lock_counter, "lock_accnt_dur": self.__lock_accnt_dur, "accnt_lock_thres": self.__accnt_lock_thres, "force_logoff_time": self.__force_logoff_time, } dce.disconnect() def pretty_print(self): PASSCOMPLEX = { 5: "Domain Password Complex:", 4: "Domain Password No Anon Change:", 3: "Domain Password No Clear Change:", 2: "Domain Password Lockout Admins:", 1: "Domain Password Store Cleartext:", 0: "Domain Refuse Password Change:", } nxc_logger.debug("Found domain(s):") for domain in self.__domains: nxc_logger.debug(f"{domain['Name']}") self.logger.success(f"Dumping password info for domain: {self.__domains[0]['Name']}") self.logger.highlight(f"Minimum password length: {self.__min_pass_len}") self.logger.highlight(f"Password history length: {self.__pass_hist_len}") self.logger.highlight(f"Maximum password age: {self.__max_pass_age}") self.logger.highlight("") self.logger.highlight(f"Password Complexity Flags: {self.__pass_prop or 'None'}") for i, a in enumerate(self.__pass_prop): self.logger.highlight(f"\t{PASSCOMPLEX[i]} {a!s}") self.logger.highlight("") self.logger.highlight(f"Minimum password age: {self.__min_pass_age}") self.logger.highlight(f"Reset Account Lockout Counter: {self.__rst_accnt_lock_counter}") self.logger.highlight(f"Locked Account Duration: {self.__lock_accnt_dur}") self.logger.highlight(f"Account Lockout Threshold: {self.__accnt_lock_thres}") self.logger.highlight(f"Forced Log off Time: {self.__force_logoff_time}")