Skip to content

adPEAS v2.5.0

Latest

Choose a tag to compare

@61106960 61106960 released this 08 Sep 10:27

The largest release since 2.0.0: 5 new checks, 16 additions, 138 fixes.

Headlines only below - the full write-up for every entry, with the reasoning
behind it, is in CHANGELOG.md.

Added

  • Who can write to the SCCM System Management container is now reported.
  • A SCOM service account with privileged rights in the domain is called out.
  • The validation now checks that a finding definition can actually appear in a report.
  • LAPS passwords that stopped rotating are reported.
  • AD FS is found and named.
  • A computer whose owner is not its creator is called out separately.
  • Disabled computer accounts are included in the owner check.
  • Constrained delegation now says whether the target is a Domain Controller.
  • Accounts carrying protocol transition without any delegation target are reported.
  • Get-DomainComputer -DomainController.
  • A roastable account that holds privileged rights is called out separately.
  • ESC10 is detected where it is deployed by Group Policy.
  • Get-WeakCertificateMapping - new AD CS check for ESC14, explicit certificate mappings.
  • Get-GPOPointAndPrint - new GPO check for Point and Print printer driver policies.
  • The JSON export survives a scan that does not finish.
  • -ExcludeModule runs everything but the named modules.

Changed

  • Security descriptors are read in one query instead of one per object.
  • An output path is no longer truncated at a dot that was never a file extension.
  • LDAP attribute conversion moved out of Invoke-LDAPSearch into its own function, ConvertFrom-LDAPAttribute.
  • The two attribute-name lists the conversion consults for every value are built once instead of per attribute.
  • The domain policy interval arithmetic is now one function, ConvertFrom-ADInterval.
  • The sidebar derives its category filter through the same function as the sections, and encodes names the way the rest of the report does.
  • A finding card reports its section the way a reader sees it, and an untagged object card names a computer the same way a tagged one does.
  • The HTML report is grouped into cards once instead of three times.
  • Secure now outranks Hint everywhere in the HTML report - the summary counts, the card badge and the scoring metadata.
  • HTML report risk score: the account tier classification now matches the identity gate every check already uses.
  • Console attribute-column alignment is now computed in one place.
  • Point and Print is no longer reported by Get-GPORegistrySettings.
  • Group Policy Preferences Registry.xml parsing moved to a shared helper

Fixed

  • ESC5 findings now carry the ESC5 write-up.
  • StrongCertificateBindingEnforcement=1 deployed by Group Policy is reported.
  • ESC9 says what else has to be true.
  • An EKU OID no longer matches a longer OID that starts with it.
  • The reachability check reads a definition to its end.
  • Both halves of the ESC14 check were deciding on a value that was never right.
  • The Exchange service groups are judged, not just listed.
  • An empty Exchange Windows Permissions group is reported.
  • netbootserver is no longer printed as a host name.
  • Two SCOM management servers without a DNS host name are both reported.
  • MemberCount is a number.
  • Set-DomainUser and Set-DomainComputer could not read the userAccountControl they were about to change.
  • The per-OU list of unprotected computers is capped.
  • Test-AccountActivity understands the userAccountControl it is actually handed.
  • Domain controllers are out of the LAPS coverage figures.
  • Exchange is found in the Configuration partition, not by a group's membership.
  • Get-InfrastructureServers no longer loses one half of the AD FS evidence to the other.
  • The two Computer checks read their findings back in one query instead of one per finding.
  • A trust's SID filtering verdict now reads the direction and the kind of trust, not one bit.
  • LDAP channel binding is read from Registry.pol, not only from GptTmpl.inf.
  • Get-LDAPConfiguration asks the data layer which computers are domain controllers.
  • A GPO linked to the Domain Controllers OU is recognised as reaching them.
  • Get-GPOUserRightsAssignment was rebuilt around what Windows actually ships.
  • No GPO check asked whether the policy it was reading actually applies.
  • Get-GPOScheduledTasks could mistake an ordinary account for SYSTEM.
  • The permission scans stopped at the organizational units.
  • Get-AddComputerRights looked at the wrong container, and only at explicit grants.
  • Get-LAPSPermissions could not tell the two LAPS generations apart in its output.
  • Primary group membership was invisible everywhere.
  • adminCount=1 was the definition of "privileged account", and it misses accounts.
  • Get-AdminReversibleEncryption only looked at privileged accounts.
  • Get-NonDefaultUserOwners skipped disabled accounts.
  • Policy wording in a description hid the password written next to it.
  • Get-UnixPasswordAccounts only looked at user objects.
  • Get-UnixPasswordAccounts printed the credential as decimal bytes.
  • Every server with protocol transition was listed as a Domain Controller.
  • The advice on constrained delegation was wrong about what protects a Domain Controller.
  • The template loaders threw instead of falling back to the current directory.
  • Three gaps in the AD CS checks, found by auditing them against the published ESC preconditions.
  • The certificate templates a CA publishes are listed one per line.
  • A user right assigned to nobody was reported as held by the next line of the file.
  • The BloodHound collector named its archive without saying where it put it.
  • Every GPO was reported as "NOT LINKED", and linking a GPO would have destroyed the existing links on the target.
  • Dates were read and written through the host's calendar, which broke Kerberos outright on some regional formats.
  • Both offline report commands announced a report they had not written.
  • A report that could not be built destroyed the report from the previous run.
  • Invoke-PasswordSpray -Auto's lockout protection never ran, and the console said the domain had no lockout policy while spraying every enabled account.
  • Every early exit in Invoke-PasswordSpray's begin block failed to stop the run.
  • An ACE granting full control via the SDDL generic bit (GA, 0x10000000) - the form Invoke-RBCDOperation itself writes, and the shape any real full-control grant in a DACL actually takes - was invisible to Get-OUPermissions.ps1 (used by Get-LAPSPermissions, Get-PasswordResetRights, Get-DangerousOUPermissions) and to Get-PrivilegedGroupMembers's AdminSDHolder check.
  • Compare-adPEASReport (scan-diff / -Baseline/-Current) silently dropped findings when two of them shared the same identity key, keeping only the last one.
  • Get-ObjectTypeTitle silently corrupted a report card's title whenever the underlying AD object's name contained a $.
  • Get-CurrentUserTokenGroups returned an empty list, every time, for any user whose tokenGroups held exactly one SID
  • New-DiamondTicket's encryption-type mismatch error named the wrong (generic) cipher label.
  • The same Object[]-instead-of-byte[] return defect documented below for the PAC module (Golden/Silver/Diamond Ticket) also runs through the entire rest of the Kerberos stack: the ASN.1 DER encoder every AS-REQ/TGS-REQ/KRB-CRED byte stream is built from (Kerberos-ASN1.ps1), the crypto primitives that produce every NT-Hash/derived key/RC4-HMAC and AES-CTS ciphertext (Kerberos-Crypto.ps1), PKINIT's DH nonce/SHA-1/SHA-256/CMS-signature helpers, and one call site each in AS-REP-Roasting, Kerberoasting, ticket forging, and the main Kerberos auth flow.
  • New-ASN1Integer threw InvalidCastException for any negative value
  • Get-NTHashFromPassword explicitly supports a blank password ([AllowEmptyString()] on -PlainPassword, for PASSWD_NOTREQD accounts) but handed the resulting empty byte array to Get-MD4Hash, whose -Data parameter was Mandatory without [AllowEmptyCollection()] and rejected it outright.
  • Every structure builder in the PAC (Golden/Silver/Diamond Ticket) module returned Object[] where it documented, and its callers assumed, byte[].
  • LDAP_NO_SUCH_OBJECT (error 32) was classified as an error by ConvertFrom-LDAPError/Get-ExceptionErrorInfo, but as the expected, non-error case by ConvertFrom-HResult's equivalent entry (0x80072030) for the identical condition
  • A zero-length AV_PAIR in an NTLM Type2 (Challenge) message was decoded as one bogus character instead of an empty string.
  • New-RandomEPAIdentifier's suffix characters were not drawn uniformly.
  • PKINIT certificate authentication found the wrong identity, or none at all, on German-locale Windows.
  • An ECDSA certificate's key size was reported as blank.
  • A GPP Registry.xml with no byte order mark was decoded wrongly on Windows PowerShell 5.1.
  • REG_MULTI_SZ from a Registry.pol was reported as one string with null characters in it.
  • A ticket or certificate file whose name contains square brackets could not be read.
  • Export-adPEASFile wrote text with a BOM on Windows PowerShell 5.1 and without one on PowerShell 7.
  • Search-Value read square brackets in the search pattern as a wildcard character class.
  • Test-AccountActivity -IncludeDetails dropped every object on a second pass.
  • One LAPS password in 256 lost its update timestamp.
  • New-SafePassword did not draw uniformly from its alphabet.
  • An ACE carrying a generic access right was displayed with an empty rights field.
  • A resolved extended right was glued onto the right beside it.
  • ConvertTo-AccessRules threw on an empty attribute value
  • A userParameters blob with exactly one Terminal Services setting was reported as a single letter.
  • RECONNECT_SAME led the CtxCfgFlags1 flag list instead of closing it.
  • The DES key from Primary:Kerberos was never read.
  • An entry declaring a zero-length key produced two bytes of the blob as if they were key material.
  • A single empty property discarded the credentials next to it.
  • WDigestHashes was not always an array.
  • Invoke-Kerberoast left the caller's credential in session state.
  • gMSA passwords were never extracted at all in the built artifact.
  • The NT hash of a gMSA password was computed from a mangled string.
  • A Shadow Credential's device id was reported as a byte-reversed hex string.
  • A disabled GPO link was reported as an active one, and enforcement was never seen.
  • A recovered GPP password could be a password nobody has.
  • One finding without a timestamp cost the reader the whole imported report.
  • The worst password policy was the one the risk scoring could not read.
  • A report converted from a JSON export could end with an exception.
  • A directory object could inject script into the generated HTML report.
  • A long attribute name ended an object's console output with an exception.
  • A creation date without a trailing Z was read as the year 1601.
  • Access control entries silently disappeared when scanning a domain from outside it.
  • A Domain Admin who was also a Backup Operator was reported as unprivileged.
  • Test-IsPrivileged did nothing useful when used from the pipeline.
  • A user principal name never resolved to a SID.
  • A distinguished name with a comma or parentheses in its RDN resolved to nothing.
  • A delegation inherited from the Certificate Templates container was invisible.
  • An AD CS enrollment principal could be judged against another principal's SID.
  • One failing query in Get-InfrastructureServers ended the whole check.
  • A password that could not be read was reported as readable.
  • The Norwegian and Esperanto spellings of "password" were never matched.
  • A credential mention in the description hid an assignment in the info attribute.
  • An empty Exchange group was reported as having one member.
  • A disabled account lockout was rated one step below a short password.
  • A within-forest trust was told its disabled SID filtering was a security risk.
  • The NetBIOS domain name was thrown away when the domain object could not be read.
  • SCOM service accounts and security groups were introduced by the server help text.
  • The SCCM and SCOM checks never reported a single server.
  • Read-only domain controllers were missing from the infrastructure inventory.
  • A current Windows Server was indistinguishable from a network printer.
  • Get-OutdatedComputers -InactiveDays did nothing.
  • The entire gMSA password-access analysis never ran.
  • Every resource-based constrained delegation could disappear.
  • A domain without AD CS produced a phantom vulnerable certificate template.
  • A template enrollable through an All-Extended-Rights ACE was missed.
  • Windows LAPS read delegations were never detected.
  • Anonymous LDAP binding was reported as restricted when it was allowed.
  • Scheduled tasks running as a named privileged account were never rated privileged.
  • A failed SMB access was reported as a clean domain.
  • A reported credential line was used as a wildcard pattern.
  • The domain scan ignored PowerShell scripts.
  • A quoted script path was mangled.
  • A privileged user with a comma in their name was skipped.
  • Restricted Groups entries keyed by group name were dropped.
  • The severity of a GPO scheduled task and of a GPO registry setting never reached the report.
  • A GPO ACE granting only delete rights was reported without a label
  • Domain Users kept a blanket read finding that Authenticated Users and Everyone were exempt from.
  • The LAPS check over-detected.
  • A machine account quota of 0 depended on an accident.
  • The minified and ultra-compressed builds shipped code the build itself had mangled.
  • S4U delegation attacks reported a real success as a failure.
  • The Set-Domain* write helpers silently no-oped on writes that should have worked.
  • AD CS enrollment over RPC and HTTP lost errors and rejected valid input.
  • Invoke-DCSync -Identity <GUID> never reached the RPC call.
  • Import-KerberosTicket could not import a raw ticket.
  • -ForceKerberos was silently ignored for a cross-domain NetBIOS credential.
  • -IgnoreSSLErrors demanded an explicit argument despite its name.
  • Smaller corrections: the SMB failure message in Get-CredentialExposure was printed twice; pattern hits in custom-path mode were tagged with the wrong object type; two tier-2 credential patterns were unreachable behind a more generic one; a scheduled task without a name attribute was reported as Properties; the membership path in Get-PrivilegedGroupMembers used a stray ? as its separator; broad SIDs were compared with a substring regex instead of equality; and Get-DangerousOUPermissions claimed "no dangerous OU permissions detected" when the domain had returned no OUs at all.