MS14-068

PAC原理

PAC的全称是Privilege Attribute Certificate(特权属性证书), 其中所包含的是各种授权信息, 例如用户所属的用户组, 用户所具有的权限等。

在Windows的Kerberos实现中, 默认情况下,KRB_AS_REP信息中将包含一组PAC信息, 也就是说, 用户所得到的TGT(TicketGranting Ticket)会包含用户的授权信息。用户再用包含有授权信息的TGT去申请相应的Service Ticket,KDC在收到这个KBR_AP_REQ请求的时候, 将TGT里的PAC信息解析出来, 加入到Service Ticket里返回。接下来, 当用户向服务器程序提交KRB_AP_REQ消息时, 服务器程序则将其中所包含的PAC信息传送给操作系统得到一个访问令牌, 并且同时将这个PAC的数字签名以KRB_VERIFY_PAC的消息传输给KDC, KDC再将验证这个PAC的数字签名的结果以RPC返回码的形式告诉服务器, 服务器就可以根据这个结果判断PAC数据的真实性和完整性,并做出最后对KRB_AP_REQ的判断。

​ —— 微软

image-20210610204040544

PAC 结构如下:

image-20210608171243827

PAC 结构是 AuthorizationData:

1
2
3
4
AuthorizationData       ::= SEQUENCE OF SEQUENCE {
    ad-type         [0] Int32,
    ad-data         [1] OCTET STRING
}

由图可知:

  • ad-type 为 AD-IF-RELEVANT
  • ad-data 仍然是一个 AuthorizationData,ad-type 为 AD-WIN2K-PAC,ad-data包含一个 PACTYPE 结构以及若干个 PAC_INFO_BUFFER 结构数组。每个 PAC_INFO_BUFFER 定义了 PAC 缓冲区的类型和字节偏移量。PAC_INFO_BUFFER 数组没有定义的顺序。因此,PAC_INFO_BUFFER 缓冲区的顺序没有意义。但是,一旦生成了 KDC 和服务器签名,缓冲区的顺序不得更改,否则 PAC 内容的签名验证将失败。PAC_INFO_BUFFER结构如下:

image-20210608174608208

ulType=0x00000001时:对应结构体为 KERB_VALIDATION_INFO,PAC 结构必须包含一个这种类型的缓冲区,即登录信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
typedef struct _KERB_VALIDATION_INFO {
   FILETIME LogonTime;
   FILETIME LogoffTime;
   FILETIME KickOffTime;
   FILETIME PasswordLastSet;
   FILETIME PasswordCanChange;
   FILETIME PasswordMustChange;
   RPC_UNICODE_STRING EffectiveName;
   RPC_UNICODE_STRING FullName;
   RPC_UNICODE_STRING LogonScript;
   RPC_UNICODE_STRING ProfilePath;
   RPC_UNICODE_STRING HomeDirectory;
   RPC_UNICODE_STRING HomeDirectoryDrive;
   USHORT LogonCount;
   USHORT BadPasswordCount;
   ULONG UserId;	//一个 32 位无符号整数,包含帐户的RID,如果UserId成员等于0x00000000,则此成员中的第一个组SID就是此帐户的SID
   ULONG PrimaryGroupId;
   ULONG GroupCount;
   [size_is(GroupCount)] PGROUP_MEMBERSHIP GroupIds; //指向组成员结构列表的指针,其中包含帐户域中帐户所属的组。此列表中的组数必须等于GroupCount
   ULONG UserFlags;
   USER_SESSION_KEY UserSessionKey;
   RPC_UNICODE_STRING LogonServer;
   RPC_UNICODE_STRING LogonDomainName;
   PISID LogonDomainId;
   ULONG Reserved1[2];
   ULONG UserAccountControl;
   ULONG SubAuthStatus;
   FILETIME LastSuccessfulILogon;
   FILETIME LastFailedILogon;
   ULONG FailedILogonCount;
   ULONG Reserved3;
   ULONG SidCount;
   [size_is(SidCount)] PKERB_SID_AND_ATTRIBUTES ExtraSids;
   PISID ResourceGroupDomainSid;
   ULONG ResourceGroupCount;
   [size_is(ResourceGroupCount)] PGROUP_MEMBERSHIP ResourceGroupIds;
 } KERB_VALIDATION_INFO;

ulType=0x0000000A 时:对应结构体为 PAC_CLIENT_INFO,其中包含客户端的名称和身份验证时间:

1
2
3
4
5
ClientId (8 bytes)::小端格式的FILETIME结构,包含Kerberos初始 TGT 验证时间,

NameLength (2 bytes):小端格式的无符号 16 位整数,指定Name 字段的长度(以字节为单位)。

Name (variable): 包含用户帐户名称的小端格式的 16 位 Unicode 字符数组。

uIType=0x00000006uIType=0x00000007

0x00000006 对应的是服务检验和,0x00000007 对应的是KDC校验和。分别由server密码和KDC密码加密,是为了防止PAC内容被篡改。

两个都是 PAC_SIGNATURE_DATA 结构:

image-20210609215204896

1
2
3
4
5
6
7
8
9
SignatureType (4 bytes):一个 32 位无符号整数值,采用小端格式,用于计算校验和

KERB_CHECKSUM_HMAC_MD5 0xFFFFFF76	16字节
HMAC_SHA1_96_AES128	   0x0000000F	12字节
HMAC_SHA1_96_AES256	   0x00000010	12字节

Signature (长度可变):包含校验和的8位无符号字符数组,签名的大小由SignatureType字段的值决定

RODCIdentifier (2 bytes): 当KDC是RODC时,是一个小端格式的16位无符号整数值,包含密钥版本号的前16位;当KDC不是RODC时,此字段不存在

微软官方的 PAC 验证过程如下:

image-20210610092807869

  1. 客户端尝试访问需要Kerberos 身份验证的资源。客户端发送 AP-REQ 向服务器请求认证。
  2. 服务将PAC传递给操作系统以接收访问令牌。服务操作系统将 AP-REQ 中的 PAC 签名转发给域控制器以在 KERB_VERIFY_PAC 消息中进行验证。
  3. 域控制器验证响应上的签名并将结果返回给服务器。该错误作为适当的 RPC 状态代码返回。
  4. 服务器验证AP-REQ,如果验证成功则发送AP-REP。

MS14-068

KERB_VALIDATION_INFO 结构中,GroupIds 包含帐户域中帐户所属的组,如果将域管组的 sid 加入 GroupIds,发送用户的 TGS 去询问域管是否有访问服务的权限的时候,域控会解密PAC,提取用户的 sid 和 GroupId,此时域控会把这个用户当做域管组里面的成员,从而提升为域管。

利用过程

Client 向 KDC-AS 发送伪造的 KRB_AS_REQ

与正常的 KRB_AS_REQ 的区别在于 include_pac 为 false,这个选项是指定身份验证过程中是否需要 PAC 的参与,设置为 false 后,返回的 TGT 就不会包含 PAC

KDC 返回给 Client KRB_AS_RSP

KRB_AS_RSP:Clien hash 加密的 session key,用 KDC hash 加密的 TGT

Client 向 KDC-TGS 发送伪造的 KRB_TGS_REQ

如图,为 KRB_TGS_REQ 结构 5.4.1

image-20210610172610309

KRB_TGS_REQ padata 结构:

image-20210610113048779

padata-value AP-REQ 结构(存放着用 krbtgt hash 加密的 TGT):

image-20210610113438849

REQ-BODY结构:

image-20210610172602061

1
2
sname 表示要请求的服务,TGS_REP 获得的 ticket 就是用该服务的 hash 进行加密
enc-authorization-data 表示用密钥加密的信息,一般使用 sub-sessionkey 加密,sub-sessionkey 存储于 PA-DATA.AP-REQ.Authenticator 字段,也可以直接用 sessionkey 加密,这个信息只会存在于 KRB_TGS_REQ 请求,sub-sessionkey 可由 Client 指定

此时构造 PAC ,PAC 中有两个校验和,分别由server密码和KDC密码加密,理论上规定是必须带有 Hash 的签名算法才行,但是微软在实现上,允许任意算法,而且由客户端指定,这就是 ms14-068 漏洞根本原因之一。因此在伪造时,随机生成一个 subkey,用 sessionkey 加密后,放在 authenticator 中,用 sub-sessionkey 加密 PAC,放在 enc-authorization-data 字段。

KDC-TGS 返回 KRB_TGS_RSP

KDC 从 Authenticator 中提取了 sub-sessionkey,从而解密出 subkey,用 subkey 解密了 PAC,利用客户端设定的签名算法验证了签名。验证成功后,KDC 解密 PAC 的尾部,重新用自己的 server hash 和 kdc hash 生成一个签名,将 server session key 用 subkey 加密,组成一个新的 TGT 返回给 Client。

最终使用这个 TGT 和 server session key 制作的票据就可以达到提权为域管的目的。

复现

  • 获取当前域用户 sid:whoami /all S-1-5-21-835054697-1105834094-592000501-1106

    image-20210611180149126

  • 获取域控名:nslookup -type=SRV _ldap._tcp

    image-20210611180228573

  • 执行EXP:MS14-068.exe -u test@hack.com -s S-1-5-21-835054697-1105834094-592000501-1106 -d hack.com

image-20210611180417355

  • mimikatz 导入票据:kerberos::ptc TGT_test@hack.test.ccache

image-20210611180526732

  • 查看权限:PsExec.exe \\win2008-1.hack.com cmd.exe

    image-20210611180800215