6 minute read

This is part one of a two-part series on Kerberos FAST armoring. Part one covers the protocol mechanics and how enforcement behaves at the AS and TGS layers. Part two covers the tool I built to test all of this – SteadFAST – along with the empirical findings against a fully patched Windows Server 2025 environment.

What is FAST?

FAST stands for Flexible Authentication Secure Tunneling. It is defined in RFC 6113 and describes a way to wrap a Kerberos AS-REQ or TGS-REQ inside an encrypted tunnel keyed off a pre-existing ticket – typically a machine account TGT. The goal is to protect the user’s pre-authentication data from offline attack. In a standard (unarmored) AS-REQ, the PA-ENC-TIMESTAMP padata is encrypted with the user’s long-term key and goes out on the wire in the clear. If you capture it, you can crack it offline. FAST puts a second layer of encryption around the whole exchange so an observer sees only ciphertext derived from the armor ticket’s session key, not anything tied to the user’s password.

Microsoft implemented FAST under the name “Kerberos armoring” and shipped it in Windows Server 2012 / Windows 8. You will see both names used interchangeably. In practice the terms are synonymous: armoring is Microsoft’s name for the FAST mechanism as implemented in MS-KILE.

How armoring works mechanically

Before a user can send an armored AS-REQ, a machine account obtains its own TGT unarmored (machine account AS-REQs are the one class that remains unarmored even in enforced environments, because there is no prior credential to bootstrap from). That TGT and its session key become the armor source.

The client then derives an armor key using the RFC 6113 KRB-FX-CF2 function:

1
armor_key = KRB-FX-CF2(subkey, tgt_session_key, "subkeyarmor", "ticketarmor")

where subkey is a fresh random key generated per-request. The actual user pre-authentication data – the PA-ENC-TIMESTAMP or, in an armored TGS-REQ, the authenticator – is encrypted inside a PA-FX-FAST padata element keyed with this armor key. The outer AS-REQ/TGS-REQ body remains structurally normal; only the padata payload is replaced with the FAST wrapper.

On the wire you can tell an armored request by the PA-FX-FAST padata type (136). Inside it you find an armor field carrying an AP-REQ for the armor TGT and an enc-fast-req containing the actual request content. The KDC decrypts the armor, verifies the AP-REQ, derives the same armor key, decrypts the inner request, and processes it normally.

Enforcement levels

Windows exposes four enforcement states via the KDC group policy (“KDC support for claims, compound authentication and Kerberos armoring”):

Level GPO name Behavior
0 / not configured Off FAST not offered; unarmored accepted
1 Supported FAST offered; unarmored still accepted
2 Always provide claims Same as level 1 for most scenarios; claims always included
3 Fail unarmored authentication requests Unarmored AS-REQ or TGS-REQ rejected

Levels 1 and 2 are indistinguishable from a client’s perspective: both accept unarmored requests. Only level 3 – S3 in the testing I describe in part two – actually enforces armoring. When S3 is active and a client sends an unarmored AS-REQ, the KDC returns KDC_ERR_POLICY immediately. The client can then retry with armor if it has a machine TGT available.

The enforcement is configured DC-side at:

1
2
3
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\KDC\Parameters
EnableCbacAndArmor = 1 (DWORD)
CbacAndArmorLevel  = 3 (DWORD)

One gotcha I found in the lab: the \Parameters subkey is required. Writing directly under ...\System\KDC without the \Parameters child key has no effect. The reliable way to flip the level is via Group Policy Editor and gpupdate /force.

Domain Functional Level matters too: below DFL 2012, the fail-unarmored behavior is silently ignored even if the registry key says 3. In a Windows Server 2025 domain this is not a concern, but it is worth knowing if you are testing in a mixed or older environment.

What the AS exchange looks like under S3

Under S3 enforcement an unarmored AS-REQ gets a clean KDC_ERR_POLICY error with a PA-FX-FAST offer in the error data – the KDC is telling the client what it expects. An armored AS-REQ gets a PA-FX-FAST PREAUTH-REQUIRED response on the first round (with the enc-fast-rep containing the cookie), followed by the full armored AS-REQ with the PA-ENCRYPTED-CHALLENGE (preauth type 138), followed by the AS-REP.

The preauth type is the easiest detection signal. A normal unarmored AS-REQ shows preauth type 2 (PA-ENC-TIMESTAMP). An armored one shows type 138 (PA-ENCRYPTED-CHALLENGE). Event ID 4768 logs both; the difference is in the PreAuthType field.

What the TGS exchange looks like under S3

This is where enforcement behavior diverges from what most documentation describes and what I found to be undocumented: the TGS refusal under S3 is not a clean KDC_ERR_POLICY.

When a client sends an unarmored TGS-REQ under S3 fail-unarmored, the KDC returns a zero-length recordRecord Length: 0, BER Empty choice was found in Wireshark – not a Kerberos error at all. The client gets no usable response and hangs. I confirmed this against both user accounts and machine accounts (the machine account exemption applies only to the AS exchange, not the TGS), and I confirmed it cross-client: impacket’s own unarmored TGS-REQ under S3 draws the identical zero-length record.

The likely reason is that the TGS code path cannot FAST-wrap an error without an armor context to wrap it in, so it falls back to returning nothing. The AS path does not have this problem because the PA-FX-FAST offer in the error data is unencrypted. This asymmetry – clean KDC_ERR_POLICY at the AS, empty record at the TGS – means that unarmored tools hang rather than failing gracefully when they try to request a service ticket in a fail-unarmored domain. Part two covers what that means operationally.

Samba’s model

For completeness: Samba 4.24 supports FAST armoring (via its Heimdal snapshot) and advertises it with kdc enable fast = Yes in smb.conf. However, Samba has no equivalent of Windows’ graduated CbacAndArmorLevel switch. There is no “fail unarmored” mode at the domain level. The only way to require armoring on Samba is through per-account Authentication Policies, which require Domain Functional Level 2016. At FL 2008R2 – the level my test Samba domain runs at – armoring is offered but never enforced, and there is no knob to change that.

This is the core cross-implementation difference: Windows enforces armoring with a global domain switch; Samba expresses it through per-principal access-check policies. The consequence for the empty-reply finding is clean: since Samba has no fail-unarmored TGS path, it cannot produce that zero-length record. The behavior is Windows-specific by construction.

What FAST does and does not protect

A few things worth stating plainly:

What it protects: The user’s pre-authentication data (the PA-ENC-TIMESTAMP or PA-ENCRYPTED-CHALLENGE exchange) is encrypted inside the armor tunnel. An offline observer cannot extract the user’s long-term key material from the AS-REQ. It also provides signed KDC errors, which mitigate error-spoofing and certain downgrade attacks.

What it does not protect by itself: A service ticket obtained via AS-REQ (not a TGS-REQ) – which I cover in part two – bypasses the TGS exchange entirely. The armoring protects the credential exchange, not the roasting attack surface that exists in the issued ticket’s encrypted portion.

RC4 armor is refused on WS2025: When I tested with an NT hash as the armor credential, the KDC returned KDC_ERR_ETYPE_NOSUPP. The DC will not issue an RC4-encrypted armor TGT. This means an attacker cannot force the FAST exchange down to RC4 even if they have only an NT hash for the armor account. AES256 is the required armor etype in practice.


Part two covers SteadFAST, the tool I built to test all of this, along with the empirical findings matrix, the armored delegation capability, the Scapy bug I found and fixed, and the telemetry side-by-sides.

Updated: