1010from zipfile import ZipFile
1111from termcolor import colored
1212from dns import resolver
13+ from dateutil .relativedelta import relativedelta as rd
1314
1415from Cryptodome .Hash import MD4
1516from OpenSSL .SSL import SysCallError
@@ -688,11 +689,6 @@ def check_if_admin(self):
688689 if item :
689690 self .admin_privs = True
690691
691- def getUnixTime (self , t ):
692- t -= 116444736000000000
693- t /= 10000000
694- return t
695-
696692 def search (self , searchFilter , attributes , sizeLimit = 0 , baseDN = None ) -> list :
697693 if baseDN is None and self .args .base_dn is not None :
698694 baseDN = self .args .base_dn
@@ -1396,6 +1392,95 @@ def gmsa_decrypt_lsa(self):
13961392 else :
13971393 self .logger .fail ("No string provided :'(" )
13981394
1395+ def pso (self ):
1396+ """
1397+ Get the Fine Grained Password Policy/PSOs
1398+ Initial FGPP/PSO script written by @n00py: https://github.com/n00py/GetFGPP
1399+ """
1400+ # Convert LDAP time to human readable format
1401+ pso_days = lambda ldap_time : f"{ rd (seconds = int (abs (int (ldap_time )) / 10000000 )).days } days"
1402+ pso_mins = lambda ldap_time : f"{ rd (seconds = int (abs (int (ldap_time )) / 10000000 )).minutes } minutes"
1403+
1404+ # Are there even any FGPPs?
1405+ self .logger .success ("Attempting to enumerate policies..." )
1406+ resp = self .ldap_connection .search (searchBase = f"CN=Password Settings Container,CN=System,{ '' .join ([f'DC={ dc } ,' for dc in self .domain .split ('.' )]).rstrip (',' )} " , searchFilter = "(objectclass=*)" )
1407+ if len (resp ) > 1 :
1408+ self .logger .highlight (f"{ len (resp ) - 1 } PSO Objects found!" )
1409+ self .logger .highlight ("" )
1410+ self .logger .success ("Attempting to enumerate objects with an applied policy..." )
1411+
1412+ # Who do they apply to?
1413+ resp = self .search (searchFilter = "(objectclass=*)" , attributes = ["DistinguishedName" , "msDS-PSOApplied" ])
1414+ for attrs in resp :
1415+ if isinstance (attrs , ldapasn1_impacket .SearchResultEntry ) is not True :
1416+ continue
1417+ for attr in attrs ["attributes" ]:
1418+ if str (attr ["type" ]) in "msDS-PSOApplied" :
1419+ self .logger .highlight (f"Object: { attrs ['objectName' ]} " )
1420+ self .logger .highlight ("Applied Policy: " )
1421+ for value in attr ["vals" ]:
1422+ self .logger .highlight (f"\t { value } " )
1423+ self .logger .highlight ("" )
1424+
1425+ # Let's find out even more details!
1426+ self .logger .success ("Attempting to enumerate details...\n " )
1427+ resp = self .search (searchFilter = "(objectclass=msDS-PasswordSettings)" ,
1428+ attributes = ["name" , "msds-lockoutthreshold" , "msds-psoappliesto" , "msds-minimumpasswordlength" ,
1429+ "msds-passwordhistorylength" , "msds-lockoutobservationwindow" , "msds-lockoutduration" ,
1430+ "msds-passwordsettingsprecedence" , "msds-passwordcomplexityenabled" , "Description" ,
1431+ "msds-passwordreversibleencryptionenabled" , "msds-minimumpasswordage" , "msds-maximumpasswordage" ])
1432+ for attrs in resp :
1433+ if not isinstance (attrs , ldapasn1_impacket .SearchResultEntry ):
1434+ continue
1435+ policyName , description , passwordLength , passwordhistorylength , lockoutThreshold , observationWindow , lockoutDuration , complexity , minPassAge , maxPassAge , reverseibleEncryption , precedence , policyApplies = ("" ,) * 13
1436+ for attr in attrs ["attributes" ]:
1437+ if str (attr ["type" ]) == "name" :
1438+ policyName = attr ["vals" ][0 ]
1439+ elif str (attr ["type" ]) == "msDS-LockoutThreshold" :
1440+ lockoutThreshold = attr ["vals" ][0 ]
1441+ elif str (attr ["type" ]) == "msDS-MinimumPasswordLength" :
1442+ passwordLength = attr ["vals" ][0 ]
1443+ elif str (attr ["type" ]) == "msDS-PasswordHistoryLength" :
1444+ passwordhistorylength = attr ["vals" ][0 ]
1445+ elif str (attr ["type" ]) == "msDS-LockoutObservationWindow" :
1446+ observationWindow = attr ["vals" ][0 ]
1447+ elif str (attr ["type" ]) == "msDS-LockoutDuration" :
1448+ lockoutDuration = attr ["vals" ][0 ]
1449+ elif str (attr ["type" ]) == "msDS-PasswordSettingsPrecedence" :
1450+ precedence = attr ["vals" ][0 ]
1451+ elif str (attr ["type" ]) == "msDS-PasswordComplexityEnabled" :
1452+ complexity = attr ["vals" ][0 ]
1453+ elif str (attr ["type" ]) == "msDS-PasswordReversibleEncryptionEnabled" :
1454+ reverseibleEncryption = attr ["vals" ][0 ]
1455+ elif str (attr ["type" ]) == "msDS-MinimumPasswordAge" :
1456+ minPassAge = attr ["vals" ][0 ]
1457+ elif str (attr ["type" ]) == "msDS-MaximumPasswordAge" :
1458+ maxPassAge = attr ["vals" ][0 ]
1459+ elif str (attr ["type" ]) == "description" :
1460+ description = attr ["vals" ][0 ]
1461+ elif str (attr ["type" ]) == "msDS-PSOAppliesTo" :
1462+ policyApplies = ""
1463+ for value in attr ["vals" ]:
1464+ policyApplies += f"{ value } ;"
1465+ self .logger .highlight (f"Policy Name: { policyName } " )
1466+ if description :
1467+ self .logger .highlight (f"Description: { description } " )
1468+ self .logger .highlight (f"Minimum Password Length: { passwordLength } " )
1469+ self .logger .highlight (f"Minimum Password History Length: { passwordhistorylength } " )
1470+ self .logger .highlight (f"Lockout Threshold: { lockoutThreshold } " )
1471+ self .logger .highlight (f"Observation Window: { pso_mins (observationWindow )} " )
1472+ self .logger .highlight (f"Lockout Duration: { pso_mins (lockoutDuration )} " )
1473+ self .logger .highlight (f"Complexity Enabled: { complexity } " )
1474+ self .logger .highlight (f"Minimum Password Age: { pso_days (minPassAge )} " )
1475+ self .logger .highlight (f"Maximum Password Age: { pso_days (maxPassAge )} " )
1476+ self .logger .highlight (f"Reversible Encryption: { reverseibleEncryption } " )
1477+ self .logger .highlight (f"Precedence: { precedence } (Lower is Higher Priority)" )
1478+ self .logger .highlight ("Policy Applies to:" )
1479+ for value in str (policyApplies )[:- 1 ].split (";" ):
1480+ if value :
1481+ self .logger .highlight (f"\t { value } " )
1482+ self .logger .highlight ("" )
1483+
13991484 def bloodhound (self ):
14001485 # Check which version is desired
14011486 use_bhce = self .config .getboolean ("BloodHound-CE" , "bhce_enabled" , fallback = False )
@@ -1412,6 +1497,7 @@ def bloodhound(self):
14121497 # If using pipx
14131498 self .logger .fail ("Or if you installed with pipx:" )
14141499 self .logger .fail ("pipx runpip netexec uninstall -y bloodhound" )
1500+
14151501 self .logger .fail ("pipx inject netexec bloodhound-ce --force" )
14161502 return False
14171503
0 commit comments