adPEAS v2 Episode 9: Tips & Tricks - The Hidden Tools in adPEAS
Hidden productivity boosters in adPEAS v2: Show-Object, KnownSPN discovery, dangerous ACL analysis, EPA testing, account activity filters, certificate analysis, and more.
Introduction
In previous episodes we covered the automated scan, security checks, reports, and offensive operations. But adPEAS has another side that’s easy to overlook: the many small helpers that save enormous amounts of time during day-to-day pentesting.
This post highlights functions and parameters whose usefulness isn’t immediately obvious, but which are invaluable for manual analysis. None of them require external tools — everything is built into adPEAS.
Prerequisite: An active adPEAS session:
Import-Module .\adPEAS.ps1
Connect-adPEAS -Domain "contoso.com" -UseWindowsAuth
Reconnaissance & Enumeration
Understanding AD Objects at a Glance with Show-Object
When you query a user or computer with Get-DomainUser, you get buried in attributes - dozens of properties, most of them irrelevant. Show-Object filters out the noise and displays only the security-relevant attributes, color-coded:
Get-DomainUser -Identity f.sinatra | Show-Object
displayName: Frank Sinatra
sAMAccountName: f.sinatra
userPrincipalName: f.sinatra@contoso.com
distinguishedName: CN=Frank Sinatra,OU=Users,OU=Domain User,DC=contoso,DC=com
objectSid: S-1-5-21-1234567890-1234567890-1234567890-1157
[+] memberOf: TerminalServerUsers
LAPSReader
ClientAdmins
ServerAdmins
Server-Operatoren
description: User Help Desk
[+] msDS-KeyCredentialLink: DeviceID: 617b9d6fa7b5204c8fa049bebf9da5eb | Created: 2026-02-19 11:20
[+] userAccountControl: NORMAL_ACCOUNT
DONT_EXPIRE_PASSWORD
[+] pwdLastSet: 04/12/2022 11:39:08
lastLogonTimestamp: 02/19/2026 10:34:36
[+] admincount: 1
The [+] markers (yellow) highlight attributes that are immediately relevant for a pentester: group memberships, Shadow Credentials, UAC flags like DONT_EXPIRE_PASSWORD, stale passwords, and admincount: 1 as an indicator for privileged accounts.
Works identically for computers:
Get-DomainComputer -Identity DC01 | Show-Object
Instead of sifting through raw LDAP output, you get the overview in seconds - which attributes are set, which ones are interesting, and where it’s worth digging deeper.
Finding High-Value Targets with a Single Parameter
Every pentest starts with the question: where are the interesting systems? Normally that means building LDAP filters, parsing SPNs, filtering output. Or you just use -KnownSPN:
# All SQL servers in AD
Get-DomainComputer -KnownSPN MSSQL
# All Exchange servers
Get-DomainComputer -KnownSPN Exchange
# All ADCS Certificate Authorities
Get-DomainComputer -KnownSPN CA
The beauty of it: adPEAS knows the SPN patterns for each service type. MSSQL matches MSSQLSvc/*, Exchange matches exchangeMDB/*, exchangeRFR/* and exchangeAB/*, CA matches certsvc/*. You don’t need to know the SPN syntax - just specify the service name.
The supported service types:
| Parameter | SPN Pattern | Typical Target |
|---|---|---|
MSSQL | MSSQLSvc/* | SQL Server |
Exchange | exchangeMDB/*, exchangeRFR/*, exchangeAB/* | Exchange Server |
SCCM | SMS Site Server/*, CmRcService/* | SCCM/MECM |
WSUS | wsusService/*, WSUS/* | Windows Update Services |
ADFS | adfssrv/* | Federation Services |
CA | certsvc/* | Certificate Authorities |
SCOM | MSOMHSvc/*, MSOMSdkSvc/* | System Center Operations Manager |
Backup | wbengine/*, veeam* | Backup Servers |
HyperV | Microsoft Virtual Console Service/*, vmms/* | Hyper-V Hosts |
RDP | TERMSRV/* | Remote Desktop |
WinRM | WSMAN/* | WinRM/PSRemoting |
HTTP | HTTP/* | Web Services |
FTP | FTP/* | FTP Servers |
Uncovering GPO Scope
GPOs are one of the potential attack vectors in AD. But knowing which GPOs exist is only half the battle — you need to know where they apply:
# Which GPOs apply to the Finance OU?
Get-DomainGPO -AppliedToOU "OU=Finance,DC=contoso,DC=com"
This answers the question: if I can edit a GPO, which systems are affected? Or conversely: which GPOs influence a specific part of the AD structure?
ADCS Templates at a Glance
ADCS vulnerabilities (ESC1-ESC15) have been one of the most popular privilege escalation paths since 2021. adPEAS makes searching for vulnerable templates easy:
# ESC1: Templates that allow arbitrary Subject Names + have Client Auth
Get-CertificateTemplate -EnrolleeSuppliesSubject -ClientAuthentication
# Templates without Manager Approval (faster exploitation)
Get-CertificateTemplate -NoManagerApproval
The filters are combinable. -EnrolleeSuppliesSubject -ClientAuthentication finds templates that meet both conditions — exactly what’s needed for ESC1. Without adPEAS you’d have to manually parse the msPKI-Certificate-Name-Flag and pKIExtendedKeyUsage attributes.
Access Control Analysis
Finding Dangerous ACLs
ACL analysis is normally tedious. Get-ObjectACL takes the pain away:
# All dangerous permissions on privileged objects
Get-DomainUser -AdminCount | Get-ObjectACL -DangerousOnly
-DangerousOnly filters for ACEs that are actually relevant for attacks: GenericAll, GenericWrite, WriteDacl, WriteOwner, and critical ExtendedRights. Everything else — the hundreds of “Read Property” and “List Contents” ACEs that clutter every analysis — gets ignored.
This also works with computers, groups, and GPOs:
# Who can edit GPOs?
Get-DomainGPO | Get-ObjectACL -DangerousOnly
# Who has rights on the Domain Root?
Get-ObjectACL -Identity "DC=contoso,DC=com" -DangerousOnly
Classifying Identities
Who is actually privileged? The question sounds simple, but it isn’t. Domain Admins — obvious. But what about Account Operators? Server Operators? DnsAdmins? Exchange Windows Permissions?
$result = Test-IsPrivileged -Identity "S-1-5-21-3623811015-12345-512"
$result.IsPrivileged # $true
$result.Category # "Privileged"
$result.Reason # "Privileged group (RID suffix -512)"
Test-IsPrivileged classifies SIDs into five categories: Privileged, Operator, BroadGroup, ExchangeService, and Standard. The function doesn’t just check the SID itself against known privileged SIDs and RID suffixes — it also recursively resolves all group memberships with a single LDAP query using LDAP_MATCHING_RULE_IN_CHAIN. A user who is in a group, that is in a group, that is in Domain Admins, will be correctly identified as Privileged. Additionally, the function checks the sIDHistory attribute for privileged SIDs — a classic vector for SID History Injection.
Kerberos Toolbox
Five Auth Methods, One Function
Invoke-KerberosAuth is the Swiss army knife for Kerberos authentication. Five different methods, one unified API:
# Password-based (AES256)
Invoke-KerberosAuth -UserName "admin" -Domain "contoso.com" -Password "P@ssw0rd"
# Overpass-the-Hash (NT-Hash -> RC4 TGT)
Invoke-KerberosAuth -UserName "admin" -Domain "contoso.com" -NTHash "A1B2C3D4E5F6..."
# Pass-the-Key (AES256)
Invoke-KerberosAuth -UserName "admin" -Domain "contoso.com" -AES256Key "4a3b2c1d5e6f..."
# Pass-the-Key (AES128)
Invoke-KerberosAuth -UserName "admin" -Domain "contoso.com" -AES128Key "4a3b2c1d..."
# PKINIT (certificate-based)
Invoke-KerberosAuth -UserName "admin" -Domain "contoso.com" -Certificate "user.pfx"
Two particularly useful parameters:
-OutputKirbi "tgt.kirbi"— Saves the TGT directly as a .kirbi file. No manual Base64 decoding, no byte array handling. Just specify the filename.
Network & Lateral Movement
Testing Admin Access — in Parallel
After enumeration, the next question: which systems do I have admin rights on? Testing this manually takes forever, especially in large environments. Test-RemoteAdminAccess solves this with parallel execution:
# Test all servers - 32 at a time (default)
Get-DomainComputer -OperatingSystem "*Server*" | Select-Object -ExpandProperty dNSHostName | Test-RemoteAdminAccess
The function tests via SMB (ADMIN$ share) by default. If SMB is blocked, it automatically falls back to WMI. With -Method you can force a specific method:
# SMB only (no WMI fallback)
Test-RemoteAdminAccess -ComputerName "SERVER01" -Method SMB -NoFallback
# WMI only (when ADMIN$ is blocked)
Test-RemoteAdminAccess -ComputerName "SERVER01" -Method WMI
Important for real-world use: -Timeout sets a per-target timeout. This prevents the entire scan from hanging because a single host isn’t responding. The default is a few seconds — for slow VPN connections you can increase the value.
The function also automatically detects whether Kerberos tickets (PTT) are present and adjusts its behavior accordingly.
NTLM Impersonation — runas /netonly as a Function
Sometimes you need NTLM authentication instead of Kerberos. For example, when you have credentials but want to keep the existing Kerberos tickets in the cache. Invoke-NTLMImpersonation does exactly that — like runas /netonly, but programmatically:
# Create NTLM token
$token = Invoke-NTLMImpersonation -Credential (Get-Credential)
# All network operations now use the new credentials
# SMB, LDAP, etc. — everything over NTLM
# ... do your work ...
# Revert to original identity
Invoke-RevertToSelf -TokenHandle $token
The key point: existing Kerberos tickets remain in the cache. You only switch the NTLM identity for network operations.
Checking EPA Status - Testing NTLM Relay Protection
NTLM Relay remains one of the most effective attack vectors in AD environments. The countermeasure: Extended Protection for Authentication (EPA). But how do you check whether EPA is actually active on Exchange or ADCS? Normally: manually provoke an NTLM Type2 Challenge, analyze the Channel Binding Token, interpret the result. Or you just use -TestEPA:
# Exchange: check EPA status per endpoint
Invoke-HTTPRequest -ScanExchange -Uri "mail.contoso.com" -TestEPA
# ADCS: check EPA status of Web Enrollment (ESC8 relevance!)
Invoke-HTTPRequest -ScanADCS -Uri "ca.contoso.com" -TestEPA
# ADCS with CA name: also test CES endpoints
Invoke-HTTPRequest -ScanADCS -Uri "ca.contoso.com" -CAName "Contoso-CA" -TestEPA
What happens under the hood: adPEAS performs an NTLM handshake without a Channel Binding Token. If the server rejects the authentication, EPA is active - NTLM Relay is blocked. If the server accepts the handshake, EPA is disabled and the endpoint is vulnerable to relay attacks.
Particularly relevant for Exchange: IIS allows different EPA settings per Virtual Directory. OWA might have EPA enabled while EWS does not. That’s why adPEAS tests each endpoint individually:
$scan = Invoke-HTTPRequest -ScanExchange -Uri "mail.contoso.com" -TestEPA
# Query EPA status per endpoint
$scan.Endpoints.OWA.EPAEnabled # $true or $false
$scan.Endpoints.EWS.EPAEnabled # Can be different!
$scan.Endpoints.Autodiscover.EPAEnabled
As a bonus, adPEAS extracts useful information about the target server from the NTLM Type2 Challenge — without needing to authenticate:
$scan.NTLMInfo.NbComputerName # NetBIOS name: "EX01"
$scan.NTLMInfo.NbDomainName # NetBIOS domain: "CONTOSO"
$scan.NTLMInfo.DnsComputerName # FQDN: "ex01.contoso.com"
$scan.NTLMInfo.DnsDomainName # Domain: "contoso.com"
$scan.NTLMInfo.DnsTreeName # Forest: "contoso.com"
$scan.NTLMInfo.ServerTimestamp # Server time (helpful for clock skew analysis)
For ADCS, -TestEPA is particularly interesting in the context of ESC8 (NTLM Relay to ADCS Web Enrollment). If /certsrv/ offers NTLM without EPA, an attacker can use coercion methods (PetitPotam, PrinterBug) to relay a Domain Controller’s authentication to the CA and request a certificate on behalf of the DC.
Helper Utilities
Account Analysis with Test-AccountActivity
Test-AccountActivity is a pipeline-based filter for AD accounts — like Where-Object, but with built-in AD logic:
# Accounts inactive for more than 90 days
Get-DomainUser | Test-AccountActivity -IsInactive
# Password older than 3 years
Get-DomainUser | Test-AccountActivity -PasswordAgeDays 1095
# Never logged in
Get-DomainUser | Test-AccountActivity -NeverLoggedIn
# Password set in 2018 or earlier
Get-DomainUser | Test-AccountActivity -PasswordChangedBeforeYear 2019
What makes it special: all filters are combinable. This allows you to build very targeted queries:
# The "ticking time bombs": active accounts + password never expires + password older than 10 years
Get-DomainUser | Test-AccountActivity -IsActive -PasswordNeverExpires -PasswordAgeDays 3650
# Enabled + never logged in (forgotten accounts - perfect for takeover)
Get-DomainUser | Test-AccountActivity -IsEnabled -NeverLoggedIn
# Inactive computers (no login for 6 months - outdated patches?)
Get-DomainComputer | Test-AccountActivity -IsInactive -InactiveDays 180
One detail that sets this function apart from manual filtering: it differentiates between “inactive” and “never logged in.” An account without a lastLogonTimestamp isn’t “inactive” (= was once active, no longer is), but rather “never used.” These are different findings with different implications.
With -IncludeDetails you can attach the computed values as properties:
Get-DomainUser | Test-AccountActivity -IsInactive -IncludeDetails |
Select-Object sAMAccountName,
@{N='DaysInactive'; E={$_.ActivityDetails.DaysSinceActivity}},
@{N='PwdYear'; E={$_.ActivityDetails.PasswordYear}},
@{N='NeverExpires'; E={$_.ActivityDetails.HasPasswordNeverExpires}}
The complete list of filters:
| Filter | Description |
|---|---|
-IsActive / -IsInactive | Based on lastLogonTimestamp |
-InactiveDays 180 | Custom threshold (default: 90 days) |
-NeverLoggedIn / -HasLoggedIn | Never logged in vs. logged in at least once |
-PasswordAgeDays 1825 | Password older than N days |
-PasswordChangedBeforeYear 2020 | Password changed before year X |
-PasswordChangedInYear 2021 | Password changed exactly in year X |
-PasswordChangedAfterYear 2023 | Password changed after year X |
-PasswordNeverSet | Password was never set |
-IsEnabled / -IsDisabled | Account status |
-PasswordNeverExpires | DONT_EXPIRE_PASSWORD flag |
-PasswordNotRequired | PASSWD_NOTREQD flag |
Analyzing Certificates with Get-CertificateInfo
You have a PFX from an ADCS attack, from Shadow Credentials, or from a file share - and want to know what’s inside? Get-CertificateInfo shows you everything important without importing the certificate into the Windows Certificate Store:
Get-CertificateInfo -Certificate ".\admin.pfx" -CertificatePassword "pass"
[?] Certificate Information
[+] General:
Subject: CN=Administrator
Issuer: CN=Contoso-CA, DC=contoso, DC=com
Serial Number: 1A00000005...
Thumbprint: 3F2B1C...
[+] Validity:
Not Before: 2026-02-19 10:50:21
Not After: 2027-02-19 10:50:21
Status: Valid (expires in 365 days)
[+] Key Information:
Has Private Key: True
Key Algorithm: RSA (2048 bit)
[+] Extended Key Usage:
[1] Client Authentication (1.3.6.1.5.5.7.3.2)
[2] Smartcard Logon (1.3.6.1.4.1.311.20.2.2)
[+] PKINIT Assessment:
PKINIT Capable: True
PKINIT EKUs: Client Authentication, Smartcard Logon
Identities:
[1] UPN: Administrator@contoso.com
Usage:
Connect-adPEAS -Domain contoso.com -Certificate '.\admin.pfx' -CertificatePassword 'pass'
Particularly useful: The PKINIT Assessment instantly tells you whether and how you can use the certificate for Kerberos authentication - including a ready-made Connect-adPEAS command for copy-paste.
Works with all common formats:
# PFX/P12 with password
Get-CertificateInfo -Certificate ".\admin.pfx" -CertificatePassword "pass"
# PFX without password (e.g., from Request-ADCSCertificate -NoPassword)
Get-CertificateInfo -Certificate ".\admin.pfx"
# CER/DER (public key only)
Get-CertificateInfo -Certificate ".\server.cer"
# For scripting: -PassThru returns an object
$info = Get-CertificateInfo -Certificate ".\admin.pfx" -PassThru
$info.PKINITCapable # True/False
$info.Identities # @("UPN:Administrator@contoso.com")
$info.HasPrivateKey # True
Tab Completion — Faster Navigation
In large AD environments, tab completion is a real productivity boost. adPEAS offers two modes:
Lazy Loading (Default): The cache is built automatically on the first TAB press - per object type and only on demand. Pressing Get-DomainUser -Identity adm<TAB> triggers the user cache. Computers, Groups, and GPOs are only loaded when actually needed. No overhead at connect time, no unnecessary load.
Eager Loading: If you want to preload all object types, use -BuildCompletionCache at connect time:
Connect-adPEAS -Domain "contoso.com" -UseWindowsAuth -BuildCompletionCache
In both cases, tab completion works identically for all Get-Domain* and Set-Domain* functions:
Get-DomainUser -Identity adm<TAB> # -> "administrator", "admin.smith", ...
Get-DomainComputer -Identity DC<TAB> # -> "DC01", "DC02", ...
Get-DomainGroup -Identity Domain<TAB> # -> "Domain Admins", "Domain Users", ...
Get-DomainGPO -Identity Def<TAB> # -> "Default Domain Policy", ...
In an environment with 5,000 users, 500 computers, and 200 groups, this saves several seconds per query. Over the course of a full pentest day, that adds up.
The cache persists as long as the PowerShell session is running and can be manually refreshed at any time with Build-CompletionCache.
Checking Session Status
Mid-pentest question: am I still connected? Which auth method? Which DC?
Get-adPEASSession
Shows the current session status: domain, server, authentication method, and whether Kerberos tickets are present. Especially useful after a longer break or when queries suddenly start failing.
Regenerating and Comparing Reports Offline
Two functions that don’t need an active AD connection — just a JSON file from a previous scan:
Report Conversion: When a new adPEAS version is released, you don’t have to re-scan. Convert-adPEASReport takes the JSON file and generates fresh reports with updated templates and scoring:
# Regenerate all formats (HTML + Text + JSON)
Convert-adPEASReport -InputJson ".\audit.json" -OutputPath ".\new_report"
# HTML only for the client
Convert-adPEASReport -InputJson ".\audit.json" -OutputPath ".\new_report" -Format HTML
Report Comparison: After a remediation phase, you want to know what changed. Compare-adPEASReport compares two JSON exports:
# Baseline vs. Current
Compare-adPEASReport -Baseline ".\scan_q1.json" -Current ".\scan_q2.json"
The output shows new, remediated, and changed findings — grouped by check category. Especially useful for quarterly compliance audits or validating remediation efforts.
Details on both functions in Episode 5: Output & Reports.
Searching Attributes with Search-Value
You have 500 users from Get-DomainUser and you’re looking for a specific string — but you don’t know which attribute it’s in? Search-Value searches all properties of every pipeline object:
# Which attribute contains "admin"?
Get-DomainUser | Search-Value "admin"
administrator
sAMAccountName: administrator
description: Built-in account for administering the computer/domain
f.sinatra
description: User Help Desk Admin
memberOf: ClientAdmins
memberOf: ServerAdmins
For each matching object, Search-Value shows the account name and the properties where the search term was found. This saves you the manual pipeline with ForEach-Object and Where-Object.
The search works with all Get-Domain* functions:
# Computers with "SQL" in any attribute
Get-DomainComputer | Search-Value "SQL"
# Groups with "Exchange" in name or description
Get-DomainGroup | Search-Value "Exchange" -Property name,description
# Regex search: users whose password was set in 2019
Get-DomainUser | Search-Value "^01/..../../2019" -Regex -Property pwdLastSet
# Exact match
Get-DomainUser | Search-Value "DONT_EXPIRE_PASSWORD" -Exact -Property userAccountControl
The three search modes:
| Mode | Syntax | Behavior |
|---|---|---|
| Wildcard (Default) | Search-Value "admin" | *admin* — matches anywhere in the value |
| Exact | Search-Value "admin" -Exact | Exact string comparison |
| Regex | Search-Value "^adm\d+" -Regex | Regular expression |
Use -Property to restrict the search to specific attributes. Without it, all attributes are searched.
Previous episode: Episode 8 - PAC Deep-Dive & Ticket Forging
About the Author
Related Articles
adPEAS v2 Blog Series: Active Directory Security Analysis with adPEAS
Introducing adPEAS v2 — a complete rewrite of the PowerShell-based Active Directory analysis tool with native Kerberos support, zero dependencies, and over 40 security checks.
adPEAS v2 Episode 2: Under the Hood - Anatomy of a Scan
What happens when adPEAS scans an Active Directory? From authentication and LDAP queries to context-dependent severity ratings and caching -- a look under the hood.
adPEAS v2 Episode 5: Output & Reports - From Scan to Deliverable
How adPEAS v2 turns scan results into actionable output: color-coded console, interactive HTML reports, incremental scanning, offline conversion, and report diffing.