@@ -1477,6 +1477,97 @@ def pso_mins(ldap_time):
14771477 self .logger .highlight (f"\t { policyApplies } " )
14781478 self .logger .highlight ("" )
14791479
1480+ def pass_pol (self ):
1481+ search_filter = "(objectClass=domainDNS)"
1482+ attributes = [
1483+ "minPwdLength" ,
1484+ "pwdHistoryLength" ,
1485+ "maxPwdAge" ,
1486+ "minPwdAge" ,
1487+ "lockoutThreshold" ,
1488+ "lockoutDuration" ,
1489+ "lockOutObservationWindow" ,
1490+ "forceLogoff" ,
1491+ "pwdProperties"
1492+ ]
1493+
1494+ resp = self .search (search_filter , attributes , sizeLimit = 0 , baseDN = self .baseDN )
1495+ resp_parsed = parse_result_attributes (resp )
1496+
1497+ if not resp_parsed :
1498+ self .logger .fail ("No domain password policy found!" )
1499+ return
1500+
1501+ self .logger .highlight ("Domain Password Policy:" )
1502+ self .logger .highlight ("" )
1503+
1504+ for policy in resp_parsed :
1505+ # Helper function to convert LDAP time to human readable format
1506+ def ldap_time_to_days (ldap_time ):
1507+ if not ldap_time or ldap_time == "0" :
1508+ return "Never"
1509+ # LDAP time is in 100-nanosecond intervals, negative for time intervals
1510+ seconds = abs (int (ldap_time )) / 10000000
1511+ days = int (seconds / 86400 ) # 86400 seconds in a day
1512+ return f"{ days } days"
1513+
1514+ def ldap_time_to_minutes (ldap_time ):
1515+ if not ldap_time or ldap_time == "0" :
1516+ return "Never"
1517+ # LDAP time is in 100-nanosecond intervals
1518+ seconds = abs (int (ldap_time )) / 10000000
1519+ minutes = int (seconds / 60 )
1520+ return f"{ minutes } minutes"
1521+
1522+ # Display password policy information
1523+ min_pwd_length = policy .get ("minPwdLength" , "Not set" )
1524+ pwd_history_length = policy .get ("pwdHistoryLength" , "Not set" )
1525+ max_pwd_age = ldap_time_to_days (policy .get ("maxPwdAge" , "0" ))
1526+ min_pwd_age = ldap_time_to_days (policy .get ("minPwdAge" , "0" ))
1527+ lockout_threshold = policy .get ("lockoutThreshold" , "Not set" )
1528+ lockout_duration = ldap_time_to_minutes (policy .get ("lockoutDuration" , "0" ))
1529+ lockout_observation_window = ldap_time_to_minutes (policy .get ("lockOutObservationWindow" , "0" ))
1530+ force_logoff = ldap_time_to_minutes (policy .get ("forceLogoff" , "0" ))
1531+ pwd_properties = policy .get ("pwdProperties" , "0" )
1532+
1533+ self .logger .highlight (f"Minimum Password Length: { min_pwd_length } " )
1534+ self .logger .highlight (f"Password History Length: { pwd_history_length } " )
1535+ self .logger .highlight (f"Maximum Password Age: { max_pwd_age } " )
1536+ self .logger .highlight (f"Minimum Password Age: { min_pwd_age } " )
1537+ self .logger .highlight (f"Account Lockout Threshold: { lockout_threshold } " )
1538+ self .logger .highlight (f"Account Lockout Duration: { lockout_duration } " )
1539+ self .logger .highlight (f"Account Lockout Observation Window: { lockout_observation_window } " )
1540+ self .logger .highlight (f"Force Logoff: { force_logoff } " )
1541+
1542+ # Decode password properties flags
1543+ if pwd_properties and pwd_properties != "0" :
1544+ pwd_props_int = int (pwd_properties )
1545+ properties = []
1546+
1547+ if pwd_props_int & 0x1 :
1548+ properties .append ("Password complexity enabled" )
1549+ if pwd_props_int & 0x2 :
1550+ properties .append ("Store passwords using reversible encryption" )
1551+ if pwd_props_int & 0x4 :
1552+ properties .append ("No anonymous password changes" )
1553+ if pwd_props_int & 0x8 :
1554+ properties .append ("No clear change password" )
1555+ if pwd_props_int & 0x10 :
1556+ properties .append ("Lockout admins" )
1557+ if pwd_props_int & 0x20 :
1558+ properties .append ("Store password with weaker obfuscation" )
1559+ if pwd_props_int & 0x40 :
1560+ properties .append ("Refuse password change" )
1561+
1562+ if properties :
1563+ self .logger .highlight ("Password Properties:" )
1564+ for prop in properties :
1565+ self .logger .highlight (f" - { prop } " )
1566+ else :
1567+ self .logger .highlight (f"Password Properties: { pwd_properties } (Unknown flags)" )
1568+ else :
1569+ self .logger .highlight ("Password Properties: None" )
1570+
14801571 def bloodhound (self ):
14811572 # Check which version is desired
14821573 use_bhce = self .config .getboolean ("BloodHound-CE" , "bhce_enabled" , fallback = False )
0 commit comments