Kerberos 是希腊神话中的地狱三头犬。和那条犬有三个头一样,Kerberos 协议也有三个”头”
- 客户端
- 服务端
- Key Distribution Center 密钥分发中心
Kerberos 协议是为了解决,“我如何证明我是我” 的一个问题。
首先是关于“我如何证明我是我”。现在又两个角色:客户端 C 和服务器 S,C 和 S 互相没建立过实际联系,也就是说 C 和 S 互相不认识。现在 C 向 S 发起一个请求,C 把携带自己身份信息的数据包发送给 S。
现在就出现了一个问题,一个你素未谋面的人跑到你面前跟你说,我是秦始皇,v 我 50,鬼信啊。所以对于 S 来说,这个 C 的身份认证是无法进行的。同理 C 也无法认证 S 的身份。
Kerberos 引入了一个第三方 KDC
,客户端想要访问服务端的某个服务,首先需要购买服务端认可的 ST
(Service Ticket,服务票据)。也就是说,客户端在访问服务之前需要先买好票,等待服务验票之后才能访问。但是这张票并不能直接购买,需要一张 TGT
(Ticket Granting Ticket,认购权证)。也就是说,客户端在买票之前必须先获得一张 TGT
。
KDC
有两个模块:AS
和 TGT
。AS
是来发放 TGT
的,TGS
是来发放 ST
的。所以说客户端的大体请求过程如下
- 客户端请求
KDC
中的 AS
,AS
对客户端进行认证,如果认证成功,AS
将给客户端发送一份 TGT
票据,票据里有请求 TGT 用户的信息 - 客户端收到
TGT
票据后,拿着 TGT 再次向 KDC 中的TGS 发起请求。TGS 会检查 TGT
内用户身份和请求用户是否相同,如果认证成功,发给客户端发一份 ST
票据 - 客户端收到 ST 后,拿着
ST
去请求服务端的服务。服务端看是否有用户所请求的 Server IP
;检查 ST
内容与客户端身份
根据这个图我们可以看到,整体可以分为三个模块
AS-Req
和 AS-Rep
TGS-Req
和 TGS-Rep
AP-Rep
和 AP-Rep
TGT
和 ST
均是由 KDC
发放的,因为 KDC
运行在域控上,所以说 TGT
和 ST
均是由域控发放的。
Kerberos 使用 TCP/UDP 88
端口进行认证,使用 TCP/UDP 464
端口进行密码重设。
在 Ker 最初的设计流程里,只说明了如何认证客户端的真实身份,并没有说明客户有没有权限去访问该服务,因为域中不同权限的用户能够访问的资源是不同的。微软为了解决这个问题引入了 PAC
PAC
是特权属性证书。PAC
包含各种授权信息、附加凭据信息、配置文件和策略信息等。例如用户所属的用户组、用户所具有的权限等。
在最初的 RFC1510
规定的标准 Kerberos 认证过程中并没有 PAC
,微软在自己的产品所实现的 Kerberos 流程中加人了 PAC
的概念。由于在域中不同权限的用户能够访问的资源是不同的,因此微软设计 PAC
用来辨别用户身份和权限。
在一个正常的 Kerberos 认证流程中,KDC
返回的 TGT
和 ST
中都是带有 PAC
的。这样做的好处是在以后对资源的访问中,服务端接收到客户请求的时候不再需要借助 KDC
提供完整的授权信息来完成对用户权限的判断,而只需要根据请求中所包含的 PAC
信息直接与本地资源的 ACL
相比较来做出裁决。
PAC 的顶部结构如下
我们可以抓包获取。不过需要 PAC
是在 TGT
和 ST
里的,所以我们需要对流量进行解密。下面是解密后得到的流量数据
在结构体里还有一个 PAC_INFO_BUFFER
结构,结构包含了关于PAC的每个部分的信息,这部分是最重要的,结构如下:
ulType
表示此缓冲区中包含的数据类型。以下是它的可能值及其含义:
Logon Info (1)
ulType
的值为 1
表示该缓冲区包含登录信息。- 登录信息通常包括用户的登录时间、用户名等。
Client Info Type (10)
ulType
的值为 10
表示该缓冲区包含客户端信息类型。- 客户端信息可能包括客户端的操作系统版本、硬件信息等。
UPN DNS Info (12)
ulType
的值为 12
表示该缓冲区包含 UPN(User Principal Name)和 DNS(Domain Name System)信息。- 该信息可以帮助区分用户的标识符和他们在域中的 DNS 名称。
Server Checksum (6)
ulType
的值为 6
表示该缓冲区包含服务器校验和。- 该缓冲区用于确保从服务器到客户端的数据完整性和一致性。
Privsvr Checksum (7)
ulType
的值为 7
表示该缓冲区包含私有服务器校验和。- 与服务器校验和类似,但该缓冲区可能涉及私有服务器或独特的认证流程。
抓包得到内容如下
LOGON INFO
类型的 PAC_LOGON_INFO
包含Kerberos票据客户端的凭据信息。
数据本身包含在一个KERB_VALIDATION_INFO
结构中,该结构是由NDR编码的。NDR编码的输出被放置在LOGON INFO
类型的PAC_INFO_BUFFER
结构中。如下:
流量如图
我们提取出来看看
提供的 PAC_LOGON_INFO
包含了有关用户登录数据的详细信息。以下是相关字段的解释和它们的含义:
关键字段解释:
- Acct Name:这个字段对应的是用户的 sAMAccountName 属性值,在本例中为
trtyr
。这是用户在域中的主要登录名。 - Full Name:这个字段为空,但通常它对应的是
displayName
属性,代表用户的全名。 - User RID:
User RID
(相对标识符)是用户 SID 的一部分。User RID
的值是 1001
,它是 SID 的最后一部分,唯一标识域中的用户。 - Group RID:
Group RID
表示用户所属的特定组的 RID。在本例中,Group RID
的值是 513
,对应的是“Domain Users”组。 - Num RIDs:表示用户所属的组的数量。在此例中,用户所属 1 个组。
- GroupIDs:此字段列出了用户所属的所有组的 RID。用户所属的组为
Group RID = 513
,即“Domain Users”组。
其他信息:
- Logon Time:用户登录时间为
2025年1月9日 12:16:05.598521200 中国标准时间
。 - Logoff Time:设置为
Infinity
,表示未设置注销时间或不适用。 - Kickoff Time:同样设置为
Infinity
,表示会话不能被管理员强制终止。 - PWD Last Set:密码最后设置时间为
2024年12月27日 08:14:34.914131600 中国标准时间
。 - PWD Can Change:用户可以更改密码的时间为
2024年12月28日
。 - PWD Must Change:密码必须更改的时间为
2025年2月7日
。 - User Flags:
User Flags
字段的值为 0x00000020
,该值的各个比特字段表示具体含义:- Extra SIDs bit is SET:表示用户有额外的 SID。
- User Session Key:会话密钥设置为
00000000000000000000000000000000
,表示没有分配特定的会话密钥。 - Server:登录发生的服务器名称为
WIN-04BJ6QOG2N1
。 - Domain:域名为
TRTYR
。 - SID pointer:指向域 SID
S-1-5-21-697332047-570282633-840858215
。 - User Account Control:该字段表示账户的各种属性,包括:
- Normal Account:这是一个普通用户账户。
- Don’t Require PreAuth:账户要求预身份验证。
- Account Disabled:账户未被禁用。
- Num Extra SID:有 1 个额外的 SID,关联到 “Authentication Authority Asserted Identity”(SID
S-1-18-1
),表示账户属于一个特定的认证组。
这里涉及到 Server Checksum
和 Privsvr Checksum
。Server Checksum
是使用服务密钥进行签名,而 Privsvr Checksum
是使用KDC密钥进行签名。
签名有两个原因。
- 存在带有服务密钥的签名,以验证此PAC由服务进行了签名
- 带有KDC密钥的签名是为了防止不受信任的服务用无效的PAC为自己伪造票据。
签名有两个原因。首先,存在带有服务密钥的签名,以验证此PAC由服务进行了签名。其次,带有KDC密钥的签名是为了防止不受信任的服务用无效的PAC为自己伪造票据。
这两个签名分别以 PAC_SERVER_CHECKSUM
和 PAC_PRIVSVR_CHECKSUM
类型的 PAC_INFO_BUFFER
发送。
在PAC数据用于访问控制之前,必须检查 PAC_SERVER_CHECKSUM
签名。这将验证客户端是否知道服务的密钥。而 PAC_PRIVSVR_CHECKSUM
签名的验证是可选的,默认不开启。它用于验证PAC是否由KDC签发,而不是由KDC以外的具有访问服务密钥的人放入票据中。
结构体如下
SignatureType
:表示签名的类型,例如,PAC_SERVER_CHECKSUM
或 PAC_PRIVSVR_CHECKSUM
。Signature
:存储签名的字节数组,长度为1个字节(但实际可能包含多个字节)。
流量如下
提取信息
KDC 验证 PAC,主要是靠那两个 PAC 签名
- 服务密钥签名
PAC_SERVER_CHECKSUM
- KDC 密钥签名
PAC_PRIVSVR_CHECKSUM
在客户端拿着 ST
去请求服务端时,向服务端发送 AP-REQ
服务端收到客户端发来的 AP-REQ
消息时,只能校验 PAC_SERVER_CHECKSUM
签名,而并不能校验 PAC_PRIVSVR_CHECKSUM
签名。因为 PAC_PRIVSVR_CHECKSUM
签名是 KDC
去签名认证的,所以如果要验证 PAC_PRIVSVR_CHECKSUM
签名,服务端还需要把 ST
里的 PAC
发给 KDC
验证。大部分服务默认并没有 KDC
验证 PAC
这一步。
那么如何配置服务主机开启 KDC 签名校验呢,根据微软官方文档的描述,若要开启 KDC 校验 PAC,需要有以下条件:
- 应用程序具有
SeTcbPrivilege
权限。SeTcbPrivilege
权限允许为用户帐户分配“作为操作系统的一部分”。本地系统、网络服务和本地服务帐户都是由 Windows 定义的服务用户帐户。每个帐户都有一组特定的特权。 - 应用程序是一个服务,验证
KDC PAC
签名的注册表项被设置为 1,默认为 0。修改方法如下- 启动注册表编辑器
regedit.exe
- 找到以下子键:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters
- 添加一个
ValidateKdcPacSignature
的键值 (DWORD类型)。该值为0时,不会进行 KDC PAC
校验。该值为 1 时,会进行 KDC PAC
校验。因此可以将该值设置为 1 启用 KDC PAC
校验。
对于验证 KDC PAC 签名这个注册表键值,有以下几点注意事项:
- 如果服务端并非一个服务程序,而是一个普通应用程序,它将不受以上注册表的影响,而总是进行
KDC PAC
校验。 - 如果服务端并非一个程序,而是一个驱动,其认证过程在系统内核内完成,它将不受以上注册表的影响,而永不进行
PAC
校验。 - 使用以上注册表项,需要在
Windows Server 2003 SP2
或更新的操作系统。 - 在运行
Windows Server 2008
或更新操作系统的服务器上,该注册表项的值缺省为 0 (默认没有该 ValidateKdcPacSignature
键值),也就是不进行 KDC PAC
校验。
需要说明的是,注册在本地系统帐户下的服务无论如何配置,都不会触发 KDC
验证 PAC
签名。也就是说譬如 SMB
、CIFS
、HOST
等服务无论如何都不会触发 KDC
验证 PAC
签名。
大体的流程如下
没有 PAC 是这样的
![](https://img.trtyr.top/images/blog/Kerberos%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3/Pastedimage 20250109143630.webp)
有 PAC 后变成了这样
客户端在访问网络资源的时候,服务端不再需要向 KDC 查询授权信息,而是直接在本地进行 PAC 信息与 ACL 的比较。从而节约了网络资源。
但是 PAC 在用户的认证阶段引入会导致认证耗时过长。
Windows Kerberos
客户端会通过 RPC
调用 KDC
上的函数来验证 PAC
信息,这时候用户会观察到在服务器端与 KDC
之间的 RPC
包流量的增加。而另一方面,由于 PAC
是微软特有的一个特性,所以启用了 PAC
的域中将不支持装有其他操作系统的服务器,制约了域配置的灵活性。
并且在2014年,由于PAC
的安全性导致产生了一个域内极其严重的提权漏洞 MS14-068
环境如下
- DC:
192.168.10.132
- 域主机 A(WIN7):
192.168.10.145
- 域主机 B:
192.168.10.134
现在在域主机 B 上运行命令
得到如下流量
我们开始分析
首先第一阶段,是客户端从 AS
那里请求 TGT
。域主机 B 向 DC 发起请求。
当域内某个用户想要访问域内某个服务时,于是输入用户名和密码,本机就会向 KDC
的 AS
认证服务发送一个 AS-REQ
认证请求
流量如下
流量信息如下
这里主要注意第一项 PA-DATA pA-ENC-TIMESTAMP
字段。它是预认证,使用用户 Hash 加密时间戳,作为 Value
发送给 KDC
的 AS
服务。KDC
从活动目录查询出用户的 hash
,使用用户 Hash
解密时间戳,若能解密且时间戳在范围内,则认证通过。这也是导致哈希传递攻击(PTH)的原因。
我们看看 impacket
里的代码
完整代码如下
可以看到 encriptedTimeStamp
的生成过程里有 key
参数,而 key
参数的生成需要使用 用户的密码 Hash
或 用户的密码 AES Key
来加密时间戳。
对其使用用户 Hash 进行解密得到
当KDC
的AS
认证服务接收到客户端发来的AS-REQ
请求后,从活动目录数据库中取出该用户的密钥,然后用该密钥对请求包中的Authenticator
预认证部分进行解密,如果解密成功,并且时间戳在有效的范围内,则证明请求者提供的用户密钥正确。
KDC
的 AS
认证服务在成功认证客户端的身份之后,发送 AS-REP
响应包给客户端。
流量如下
流量信息如下
AS-REP
返回包中最重要的就是 TGT
认购权证和加密的 Logon Session Key
了。TGT认购权证中加密部分是使用 krbtgt
密钥加密的,而 Logon Session Key
是使用请求的用户密钥加密的。
解密流量得到
其中的 authorization-data
就是 PAC
了,主要是靠 PAC_LOGON_INFO
里的 User RID
和 Group RID
来区分权限
KDC
生成 PAC
的过程如下:KDC在收到客户端发来的 AS-REQ
请求后,从请求中取出 cname
字段,然后查询活动目录数据库,找到 sAMAccountName
属性为 cname
字段的值的用户,用该用户的身份生成一个对应的 PAC
。
AS-REP
响应包最外层的那部分便是加密的 Login session Key
了,其作用是用于确保客户端和 KDC
下阶段之间通信安全,它使用请求的用户密钥加密。得到如下流量内容
代码如下
可以看到这个生成过程里的 plainText
是需要 key
的参与,而这个 key
就是我们上面生成加密时间戳时用到的那个 key
客户端再收到 KDC
的 AS-REP
回复后,使用用户密钥解密加密过的 Logon Session Key
,得到 Logon Session Key
,并且也拿到了TGT认购权证。之后它会在本地缓存此 TGT
认购权证和 Logon Session Key
。现在客户端需要凭借这张 TGT
认购凭证向 KDC
购买相应的 ST
服务票据。
客户端拿着上一步获得的TGT
认购权证发起TGS-REQ
请求,向KDC
购买针对指定服务的ST
服务票据
得到流量信息如下
为了确保后阶段的会话安全,TGS-REQ
中 ap-req
中的 authenticator
字段的值是用上一步 AS-REP
中返回的 Logon Session Key
加密的时间戳
看一下代码
得到代码
这个 authenticator
就是 encryptedEncodedAuthenticator
,而 encryptedEncodedAuthenticator
的生成需要 sessionKey
。
KDC
的 TGS
服务接收到 TGS-REQ
请求之后,首先使用 krbtgt
密钥解密 TGT
认购权证中加密部分得到 Logon Session key
和 PAC
等信息,如果能解密成功则说明该 TGT
认购权证是 KDC
颁发的。然后验证 PAC
的签名,如果签名正确,则证明 PAC
未经过篡改。然后使用 Logon Session Key
解密 Authenticator
得到时间戳等信息,如果能够解密成功,并且票据时间在范围内,则验证了会话的安全性。
在完成上述的检测后,KDC
的 TGS
服务完成了对客户端的认证,TGS
服务发送响应包给客户端。
流量内容如下
TGS-REP
返回包中最重要的就是 ST
服务票据和 Service Session key
了。ST
服务票据中加密部分是使用服务密钥加密的,而 Service Session key
是使用 Logon Session Key
加密的。
TGS-REP
响应包中的 ticket
便是 ST
服务票据了。流量如下
看一下 PAC
的 User RID
和 Group RID
可以发现,和 TGT
过程中的一样。在正常的 TGS
过程,KDC
在 ST
中的 PAC
是直接拷贝 TGT
的 PAC
在外层的 enc-part
就是 Service Session Key
了,使用 Logon Session Key
加密
这里的 keyvalue
在 ST
票据里也有
客户端在收到 KDC
返回的 TGS-REP
消息,从中取出 ST
服务票据后,就准备要开始申请访问服务了。
由于我们是用 SMB
进行连接的,所以之后的内容都是在 SMB
里
客户端接收到 KDC
的 TGS
回复后,通过缓存的 Logon Session Key
解密 enc_Service Session key
得到 Service Session Key
,同时它也拿到了 ST
服务票据。Serivce Session Key
和 ST
服务票据会被客户端缓存。
我们查看流量
内容如下
可以看到,这里的 keyvalue
就是之前的 ST
,客户端拿着 ST
去请求服务。authenticator
下面是这样的
这一步是可选的,当客户端希望验证提供服务的服务端时(也就是 AP-REQ
请求中 mutual-required
协商选项为 True
),服务端返回 AP-REP
消息。
服务端收到客户端发来的 AP-REQ
消息后,通过服务密钥解密 ST
服务票据得到 Service Session Key
和 PAC
等信息,然后用 Service Session Key
解密 Authenticator
得到时间戳。如果能解密成功且时间戳在有效范围内,则验证了客户端的身份。
验证了客户端身份后,服务端从 ST
服务票据中取出 PAC
中代表用户身份权限信息的数据,然后与请求的服务 ACL
做对比,生成相应的访问令牌。
同时,服务端会检查AP-REQ
请求中mutual-required
协商选项是否为True
,如果为True
的话,说明客户端想验证服务端的身份。此时,服务端会用Service Session Key
加密时间戳作为Authenticator
,在AP-REP
响应包中发送给客户端进行验证。
如果 mutual-required
选项为 False
的话,服务端会根据访问令牌的权限决定是否返回相应的服务给客户端。