又这个图可以知道,PAC只不过是ticket里Authorization DATA的一个分支。
而Authorization data的结构是这样的
AuthorizationData ::= SEQUENCE OF SEQUENCE {
ad-type [0] Int32,
ad-data [1] OCTET STRING }
ad-type中就有这么一个类型 AD-IF-RELEVANT 对应数字1,由上上图可知这是PAC的外壳。
若类型为 AD-IF-RELEVAN ,那么ad-data也是一个 AuthorizationData类型的结构体,也有ad-type 和ad-data.那么这个外壳ad-data的ad-type就是次外壳AD-WIN2K-PAC 了,与 AD-WIN2K-PAC 这个ad-type对应的ad-data就是一段连续空间。这段空间包含一个头部PACTYPE以及若干个PAC_INFO_BUFFER 。
PACTYPE包含的是 cBuffers,版本以及缓冲区 。
PAC_INFO_BUFFER是key-value型的。PAC_INFO_BUFFER的key有很多
0x00000002 | 凭证信息。PAC结构不应包含多个此类缓冲区。第二或后续凭证信息缓冲区在接收时必须被忽略。 |
0x00000006 | 服务器校验和。PAC结构必须包含一个这种类型的缓冲区。其他登录服务器校验和缓冲区必须被忽略。 |
0x00000007 | KDC(特权服务器)校验和(第2.8节)。PAC结构必须包含一个这种类型的缓冲区。附加的KDC校验和缓冲区必须被忽略。 |
0x0000000A | 客户名称和票证信息。PAC结构必须包含一个这种类型的缓冲区。附加的客户和票据信息缓冲区必须被忽略。 |
0x0000000B | 受约束的委派信息。PAC结构必须包含一个S4U2proxy请求的此类缓冲区,否则不包含。附加的受约束的委托信息缓冲区必须被忽略。 |
0x0000000C | 用户主体名称(UPN)和域名系统(DNS)信息。PAC结构不应包含多个这种类型的缓冲区。接收时必须忽略第二个或后续的UPN和DNS信息缓冲区。 |
0x0000000D | 客户索取信息。PAC结构不应包含多个这种类型的缓冲区。附加的客户要求信息缓冲区必须被忽略。 |
0x0000000E | 设备信息。PAC结构不应包含多个这种类型的缓冲区。附加的设备信息缓冲区必须被忽略。 |
0x0000000F | 设备声明信息。PAC结构不应包含多个这种类型的缓冲区。附加的设备声明信息缓冲区必须被忽略。 |
其中比较重要的是1,6和7
0x00000001 KERBVALIDATIONINFO 这个结构用于存储用户的身份信息.它是一个结构体,这个结构体是这样的(待会我们伪造PAC的时候主要就是伪造此处额 UserId 以及 PGROUP_MEMBERSHIP GroupIds )服务器解包PAC后提取用户的sid以及groupid,然后就把当前发包过来的用户权限当成sid,groupid的权限处理。
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; //用户的sid
ULONG PrimaryGroupId;
ULONG GroupCount;
[size_is(GroupCount)] PGROUP_MEMBERSHIP GroupIds;//用户所在的组,如果我们可以篡改的这个的话,添加一个500(域管组),那用户就是域管了。在ms14068 PAC签名被绕过,用户可以自己制作PAC的情况底下,pykek就是靠向这个地方写进域管组,成为使得改用户变成域管
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;
0x00000006和0x00000007 6是服务器校验和,由server密码加密。7是KDC校验和,又KDC密码加密。存在的目的就是防止PAC被篡改。
MS14068这个漏洞的产生主要是微软犯下了三个错误:
1.对校验和的算法实现不够细致,导致在校验和生成时可以不用管server和KDC的hash直接生成,而且生成出来的校验和还是合法的
2.PAC可以不用放在TGT中,即使是这样,KDC也能照常解析出TGT外的PAC
3.下面再说
那么这个漏洞大致的攻击原理是什么呢。
首先,我们在as_request的时候,把include-PAC标志设置为false,那么as_rep就不会在TGT中返回PAC了。然后这个时候我们自己伪造一个pac,在TGS_REQ时发过去就行了。伪造的pac修改USER SID&GROUP SID(在PAC的 0x00000001 KERBVALIDATIONINFO 结构)可以把我们的用户权限改到很高(域管),从而达到提权到域管的能力。
那么伪造PAC,我们就遇到了两个问题:
1.pac里有校验和,防止自己被篡改,我们得有server和KDC密码才能使校验和合法。
2.pac按理说应该在TGT里,但是TGT是被kbrtgt hash加密的,我们无法获取kbrtgt hash继而无法修改TGT内容,继而不能修改PAC。
来说说如何绕过PAC校验和问题。
我们刚刚说过,0x00000006和0x00000007 这两个结构的存在是为了防止PAC被篡改,这个校验和算法采用的是个叫checksum算法然后把kdc hash和服务hash当作key对PAC加密产生的值,从而防止PAC被篡改。但是checksum算法是有很多种的,md5也是checksum的分支之一,修改PAC后,我们只需设置加密算法为MD5并用MD5算法对0x00000001 KERBVALIDATIONINFO进行加密,将生成的值放入两个检验和即可,KDC拿到0x00000001 KERBVALIDATIONINFO,并且通过解析数据包获取当前加密算法为MD5,然后对其进行MD5加密,若加密结果与校验和一致,则认为PAC未被修改
再来说说如何绕过krbtgt hash加密TGT的问题。
因为我们在AS_REQ时设置include-PAC为false,TGT里就不会包含PAC了。那么我们在TGS_REQ时如何把PAC传递给KDC?只需要把PAC放入req-body即可。
这样KDC依旧会正常解析这个TGS_REQ包
注意TGS_REQ里的include-pac依旧是false。
然后KDC接收到PAC后会先解密TGT拿到authenticator里的key对PAC进行解密(TGS_REQ时加密PAC用的key是随机生成的,这个key会放在authenticator里),然后验证一下PAC的签名,若成功然后把解密得到的PAC采用server key和KDC key重新生成校验和,拼接成一个新的TGT返回给客户端。
上面这一段就是微软犯下的第三个错误,很不可思议,居然莫名其妙的返回了一个包含了PAC的TGT回来。总结来说就是构造了一个畸形的TGS_REQ,从TGS_RES得到了一个包含伪造PAC的TGT。
原理就是上面这些,然后我们用包含伪造PAC的TGT到处访问服务即可。
M14068利用1.工具kekeo https://github.com/gentilkiwi/kekeo/releases/tag/2.2.0-20200718
具体方法为,在kekeo里先执行 kerberos::purge清空票据
然后再执行 exploit::ms14068 /user:xxx /password:xxx /domain:xxx /ptt
即可。
然后就dir \域控\c$ 试试,如果可以就说明提权成功了(不是每次都能成功的)
2.golenpac https://github.com/maaaaz/impacket-examples-windows/blob/master/goldenPac.exe
这个工具好用,
执行类似上述命令,就能返回一个域控的 system权限的cmd shell回来,感觉蛮好用
另外在最后指定域控机器时,可以指定域控以外的机器并获取他们的本地system权限用户.
但返回的似乎不是域控?
Kerberoast攻击原理: 攻击者从 TGS-REP 中提取加密的服务票证。由于服务票证是用链接到请求 spn 的帐户的哈希加密的,所以攻击者可以离线破解这个加密块,恢复帐户的明文密码
How to get SPN如何得到域中的所有SPN?
1.setspn
很简单,只需执行
setspn -q */*
即可
2.kerberoast工具集的GetUserSpns powershell脚本