134 lines
5.0 KiB
Python
134 lines
5.0 KiB
Python
|
import sys
|
||
|
import codecs
|
||
|
import logging
|
||
|
|
||
|
from impacket.nt_errors import STATUS_MORE_ENTRIES
|
||
|
from impacket.dcerpc.v5 import transport, samr
|
||
|
from impacket.dcerpc.v5.rpcrt import DCERPCException
|
||
|
from time import strftime, gmtime
|
||
|
|
||
|
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
|
||
|
self.protocol = connection.args.smb_port
|
||
|
self.username = connection.username
|
||
|
self.password = connection.password
|
||
|
self.domain = connection.domain
|
||
|
self.hash = connection.hash
|
||
|
self.lmhash = ''
|
||
|
self.nthash = ''
|
||
|
self.aesKey = None
|
||
|
self.doKerberos = False
|
||
|
|
||
|
if self.hash is not None:
|
||
|
self.lmhash, self.nthash = self.hash.split(':')
|
||
|
|
||
|
if self.password is None:
|
||
|
self.password = ''
|
||
|
|
||
|
def enum(self):
|
||
|
|
||
|
#logging.info('Retrieving endpoint list from %s' % addr)
|
||
|
|
||
|
entries = []
|
||
|
|
||
|
protodef = PassPolDump.KNOWN_PROTOCOLS['{}/SMB'.format(self.protocol)]
|
||
|
port = protodef[1]
|
||
|
|
||
|
logging.info("Trying protocol %s..." % self.protocol)
|
||
|
rpctransport = transport.SMBTransport(self.addr, port, r'\samr', self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, doKerberos = self.doKerberos)
|
||
|
|
||
|
dce = rpctransport.get_dce_rpc()
|
||
|
dce.connect()
|
||
|
|
||
|
dce.bind(samr.MSRPC_UUID_SAMR)
|
||
|
|
||
|
resp = samr.hSamrConnect(dce)
|
||
|
serverHandle = resp['ServerHandle']
|
||
|
|
||
|
resp = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle)
|
||
|
domains = resp['Buffer']['Buffer']
|
||
|
|
||
|
resp = samr.hSamrLookupDomainInSamServer(dce, serverHandle, domains[0]['Name'])
|
||
|
|
||
|
resp = samr.hSamrOpenDomain(dce, serverHandle = serverHandle, domainId = resp['DomainId'])
|
||
|
domainHandle = resp['DomainHandle']
|
||
|
|
||
|
self.logger.success('Dumping password policy')
|
||
|
self.get_pass_pol(self.addr, rpctransport, dce, domainHandle)
|
||
|
|
||
|
def convert(self, low, high, no_zero):
|
||
|
|
||
|
if low == 0 and hex(high) == "-0x80000000":
|
||
|
return "Not Set"
|
||
|
if low == 0 and high == 0:
|
||
|
return "None"
|
||
|
if no_zero: # make sure we have a +ve vale for the unsined int
|
||
|
if (low != 0):
|
||
|
high = 0 - (high+1)
|
||
|
else:
|
||
|
high = 0 - (high)
|
||
|
low = 0 - low
|
||
|
tmp = low + (high)*16**8 # convert to 64bit int
|
||
|
tmp *= (1e-7) # convert to seconds
|
||
|
try:
|
||
|
minutes = int(strftime("%M", gmtime(tmp))) # do the conversion to human readable format
|
||
|
except ValueError, e:
|
||
|
return "BAD TIME:"
|
||
|
hours = int(strftime("%H", gmtime(tmp)))
|
||
|
days = int(strftime("%j", gmtime(tmp)))-1
|
||
|
time = ""
|
||
|
if days > 1:
|
||
|
time = str(days) + " days "
|
||
|
elif days == 1:
|
||
|
time = str(days) + " day "
|
||
|
if hours > 1:
|
||
|
time += str(hours) + " hours "
|
||
|
elif hours == 1:
|
||
|
time = str(days) + " hour "
|
||
|
if minutes > 1:
|
||
|
time += str(minutes) + " minutes"
|
||
|
elif minutes == 1:
|
||
|
time = str(days) + " minute "
|
||
|
return time
|
||
|
|
||
|
def get_pass_pol(self, host, rpctransport, dce, domainHandle):
|
||
|
try:
|
||
|
resp = samr.hSamrQueryInformationDomain(dce, domainHandle, samr.DOMAIN_INFORMATION_CLASS.DomainPasswordInformation)
|
||
|
|
||
|
min_pass_len = resp['Buffer']['Password']['MinPasswordLength']
|
||
|
|
||
|
pass_hst_len = resp['Buffer']['Password']['PasswordHistoryLength']
|
||
|
|
||
|
self.logger.highlight('Minimum password length: {}'.format(min_pass_len))
|
||
|
self.logger.highlight('Password history length: {}'.format(pass_hst_len))
|
||
|
|
||
|
max_pass_age = self.convert(resp['Buffer']['Password']['MaxPasswordAge']['LowPart'],
|
||
|
resp['Buffer']['Password']['MaxPasswordAge']['HighPart'],
|
||
|
1)
|
||
|
|
||
|
min_pass_age = self.convert(resp['Buffer']['Password']['MinPasswordAge']['LowPart'],
|
||
|
resp['Buffer']['Password']['MinPasswordAge']['HighPart'],
|
||
|
1)
|
||
|
|
||
|
self.logger.highlight('Maximum password age: {}'.format(max_pass_age))
|
||
|
self.logger.highlight('Minimum password age: {}'.format(min_pass_age))
|
||
|
|
||
|
resp = samr.hSamrQueryInformationDomain2(dce, domainHandle,samr.DOMAIN_INFORMATION_CLASS.DomainLockoutInformation)
|
||
|
|
||
|
lock_threshold = int(resp['Buffer']['Lockout']['LockoutThreshold'])
|
||
|
|
||
|
self.logger.highlight("Account lockout threshold: {}".format(lock_threshold))
|
||
|
|
||
|
lock_duration = None
|
||
|
if lock_threshold != 0: lock_duration = int(resp['Buffer']['Lockout']['LockoutDuration']) / -600000000
|
||
|
|
||
|
self.logger.highlight("Account lockout duration: {}".format(lock_duration))
|
||
|
except Exception as e:
|
||
|
self.logger.error("{}".format(e))
|