AD Kerberos 加密类型排查
AD Kerberos 加密类型排查
用户能正常登录,不代表访问某个 HTTP 服务时一定能拿到服务票据;同一个 klist 缓存里的 TGT 与服务票据,也可能分别使用 AES 和 RC4。排查要落到准确的 SPN、服务账号和这一次 TGS 请求。
示例环境为 Windows Server 2022,域名 corp.test.checo.cc,主机为 LAB-DC1 与 LAB-MEMBER。HTTP/KB1RC4 用来观察禁止 RC4 后的取票失败;另一个服务账号 kb4769-svc 用来比较账号声明变化前后的票据算法。
1. 位掩码与算法编号
账号属性是位掩码,TicketEncryptionType 是算法编号。把两个十六进制值混用,会从第一步就筛错对象:
| 所在位置 | 数值 | 含义 |
|---|---|---|
| msDS-SupportedEncryptionTypes | 0x1 | DES-CBC-CRC 位,不是 RC4 |
| 同上 | 0x4 / 十进制 4 | RC4 位 |
| 同上 | 0x8、0x10 | AES128、AES256 位 |
| 同上 | 0x18 / 十进制 24 | AES128 与 AES256 |
| 同上 | 0x1C / 十进制 28 | RC4、AES128、AES256 |
| TicketEncryptionType | 0x17 | RC4-HMAC 票据 |
| TicketEncryptionType | 0x11、0x12 | AES128、AES256 票据 |
更高位还承载其他能力,不能拿本表当所有版本的完整属性定义。属性未设置、显式为 0、显式为 4 也不能一律当作同一种状态;实际默认行为还受 DC 更新与 KDC 配置影响。属性用途
2. AES TGT 与 RC4 服务票
测试服务账号 kb4769-svc 注册了 HTTP/kb4769svc.corp.test.checo.cc。账号已有 AES 与 RC4 密钥,实验先把允许声明设为 4。成员机在目标登录会话清缓存、请求这个 SPN,得到:
TGT: krbtgt/CORP.TEST.CHECO.CC
KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
Service: HTTP/kb4769svc.corp.test.checo.cc
KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)DC 同时记录:
TargetUserName = [email protected]
ServiceName = kb4769-svc
ServiceSid = S-1-5-21-3628476133-752301219-3504167884-6426
IpAddress = ::ffff:10.77.77.20
Status = 0x0
TicketEncryptionType = 0x17
ServiceSupportedEncryptionTypes = 0x4 (RC4)
ServiceAvailableKeys = AES-SHA1, RC4
ClientAdvertizedEncryptionTypes = AES256-CTS-HMAC-SHA1-96, AES128-CTS-HMAC-SHA1-96, RC4-HMAC-NT
SessionKeyEncryptionType = 0x17这条记录里“客户端愿意用 AES”“服务账号有 AES 密钥”都成立,实际服务票却是 RC4。把服务账号声明从 4 改为 24,清缓存重取,客户端服务票变成 AES256,4769 的 TicketEncryptionType 与 SessionKeyEncryptionType 都变成 0x12,ServiceSupportedEncryptionTypes 变成 0x18。
这说明账号声明是本次对照中的关键变量。不能再扩大成“只有服务账号决定加密类型”:客户端请求、服务能力及现有密钥、KDC/主机策略、补丁后的选择规则共同构成约束。其他条件改变时,同一个属性值不保证仍得到相同结果。

3. 禁止 RC4 后取票失败
另一账号 kb1-rc4user 的 SPN 是 HTTP/KB1RC4,属性为 4。禁止 RC4 前,12:38:30 的服务请求成功,TicketEncryptionType=0x17。随后实验把主机策略中的允许集合限制为 AES128、AES256,读回 SupportedEncryptionTypes=24 并刷新策略。
客户端同一 SPN 的新请求变成:
klist get HTTP/KB1RC4
Current LogonId is 0:0x3e7
GetTicket substatus: 0x80090342
klist failed with 0xc00002fd/-1073741059:
The encryption type requested is not supported by the KDC.12:39:02 的失败 4769:
ServiceName = HTTP/KB1RC4
Status = 0xe
TicketEncryptionType = 0xffffffff
ServiceSupportedEncryptionTypes = -
ServiceAvailableKeys = -0xe 是 KDC_ERR_ETYPE_NOSUPP。本例中失败字段没有完整展开账号能力,应保留原值;不能从连字符进一步断言 KDC 的内部执行一定停在某个精确函数或“密钥完全没有参与”。0xffffffff 也不是成功签发了某种新算法。
12:39:18,该账号的另一条 TGT 记录仍成功,算法为 0x12。这解释了“登录正常、特定服务取票失败”可以同时出现。AS 与 TGS 面向的目标密钥和约束不同,不应拿一次 TGT 成功来排除服务票问题。
恢复实验前策略后,重新取票在 12:40:25 成功,服务票回到 0x17。它证明当次故障与允许类型交集有关;它没有完成服务迁移到 AES,也没有验证真正 HTTP 业务的完整调用。该记录来自当时配置,不能作为所有当前补丁环境仍默认允许 RC4 的证据。
4. 收集票据证据
先记录服务账号 SID、SPN 归属、属性及密码时间,再在真正发起请求的会话取票。以下是整理后的命令示例:
# Management host: capture identity and the stored AD attribute first.
$dc = 'LAB-DC1.corp.test.checo.cc'
Get-ADUser kb4769-svc -Server $dc -Properties `
servicePrincipalName,msDS-SupportedEncryptionTypes,pwdLastSet,msDS-KeyVersionNumber |
Select-Object SamAccountName,SID,servicePrincipalName,
msDS-SupportedEncryptionTypes,pwdLastSet,msDS-KeyVersionNumber
setspn.exe -Q HTTP/kb4769svc.corp.test.checo.cc
# Request in the intended client logon session and record its LogonId.
klist
klist get HTTP/kb4769svc.corp.test.checo.cc
klistSYSTEM、管理员和普通用户有不同 LogonId,别在一个会话 purge、另一个会话访问后比较。示例中的历史 klist 请求来自机器身份,不是一个普通用户控制台访问。已有缓存票据也不会因为目录属性改变而立刻改写。
在实际处理请求的 DC 上解析字段名,不用整段 XML 模糊匹配账号名:
# Run on the DC handling the request; adjust the historical window.
$start = (Get-Date).AddMinutes(-15)
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4769;StartTime=$start} |
ForEach-Object {
$xml = [xml]$_.ToXml()
$d = @{}
foreach ($item in $xml.Event.EventData.Data) {
$d[[string]$item.Name] = [string]$item.'#text'
}
if ($d.ServiceName -eq 'kb4769-svc' -or
$d.ServiceName -eq 'HTTP/kb4769svc.corp.test.checo.cc') {
[pscustomobject]@{
Time = $_.TimeCreated; DC = $_.MachineName
RecordId = $_.RecordId; Version = $xml.Event.System.Version
Requester = $d.TargetUserName; Service = $d.ServiceName
ServiceSid = $d.ServiceSid; Client = $d.IpAddress
Status = $d.Status; TicketType = $d.TicketEncryptionType
SessionKeyType = $d.SessionKeyEncryptionType
ServiceTypes = $d.ServiceSupportedEncryptionTypes
ServiceKeys = $d.ServiceAvailableKeys
ClientTypes = $d.ClientAdvertizedEncryptionTypes
}
}
} | Sort-Object Time | Format-ListServiceName 在成功记录中通常是持有 SPN 的账号名,不保证是请求的完整 SPN;失败记录的形式还可能不同。确认具体 SPN 要结合客户端的 Server 行、SPN 目录查询或网络证据,4768 的 TGT ServiceName 不能替代此处 SPN 取证。
ServiceSid 适合关联账号改名前后;来源 IP 要正常解析 IPv4-mapped IPv6,不用字符串后缀匹配来区分客户端。TicketEncryptionType、SessionKeyEncryptionType、能力集合与 AvailableKeys 各自保留,不把它们合成一个“加密正常”布尔值。
4769 扩展字段与事件版本、系统更新有关。微软说明 2025 年 1 月及以后相关累计更新增加了新格式;旧记录缺少字段不代表账号缺少对应密钥。事件中的能力值还可能经过系统处理,需与 AD 原始属性交叉检查。4769 字段与版本
5. 迁移 AES
下面复现的是“已有 AES 密钥时修改账号声明”,只适用于专用实验账号,并保存原属性是否存在:
# Dedicated lab account only; preserve whether the attribute existed.
$dc = 'LAB-DC1.corp.test.checo.cc'
$u = Get-ADUser kb4769-svc -Server $dc -Properties msDS-SupportedEncryptionTypes
$before = $u.'msDS-SupportedEncryptionTypes'
$backup = Join-Path $env:TEMP ('kb4769-etype-' + (Get-Date -Format yyyyMMdd-HHmmss) + '.clixml')
[pscustomobject]@{Guid=$u.ObjectGUID;Value=$before} | Export-Clixml -LiteralPath $backup
Set-ADUser -Identity $u.ObjectGUID -Server $dc -Replace @{'msDS-SupportedEncryptionTypes'=24}
# In the disposable client logon session; affects its entire ticket cache.
klist purge
klist get HTTP/kb4769svc.corp.test.checo.cc
klist这次实验不需要再重置密码,是因为记录已经显示 ServiceAvailableKeys 中有 AES。对历史账号,允许 AES 不等于创建了 AES 密钥;如果需要通过密码轮换补齐,必须同步更新服务配置、计划任务、应用凭据或 keytab,并核对密钥版本。不能统一承诺“改完不需要重启服务”。
在域内停止使用 RC4 前,先盘点实际服务请求和旧设备依赖,再逐项完成 AES 取票及真实业务验证。不要只搜索 ServiceSupportedEncryptionTypes 恰好等于 4:位掩码可以有多位,默认值也可能未显式配置。也不要把出现 AES 票据的某一天直接当作“当天有人改了账号属性”的证明,策略、客户端、密钥轮换等都可能影响结果。微软 RC4 检测与迁移指引
恢复测试属性使用实际快照:
# Restore the captured value, not a guessed default.
$saved = Import-Clixml -LiteralPath $backup
if ($null -eq $saved.Value) {
Set-ADUser -Identity $saved.Guid -Server $dc -Clear 'msDS-SupportedEncryptionTypes'
} else {
Set-ADUser -Identity $saved.Guid -Server $dc -Replace @{
'msDS-SupportedEncryptionTypes'=[int]$saved.Value
}
}域策略也应恢复所属 GPO 的原配置;“删除一个注册表值”只有原来确实不存在且不会被 GPO 重写时,才等于恢复基线。不要把 31 当作通用回滚值,它还包含 DES 位。该实验的策略路径是 HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters 下的 SupportedEncryptionTypes,不是 KDC 的 DefaultDomainSupportedEncTypes;两者不要互换。
6. 业务验收
同一 SPN 的新请求应不再报加密类型不支持,DC 的 Status=0,客户端与 DC 的服务票算法符合目标策略。AES128 在允许策略内也可能是合格结果,并非所有环境必须恰好 0x12。
随后还要验证服务能解密票据并完成业务。4769 成功而应用失败,继续检查 SPN 是否由正确账号持有、服务实际用哪个身份、密码/keytab 是否同步、时钟与服务授权,而不是宣称整个认证链已经成功。
TicketOptions 是请求选项,klist Ticket Flags 是返回票据标志,不应按数值相等来验收。历史记录分别为 0x40810000 与 0x40a10000,差异本身不说明篡改。哈希字段可以辅助关联,但一次业务可能重用票据,事件也受保留与采集范围影响;不把 4769 条数当 HTTP 请求数。
